← 返回首页
JavaSE系列教程(七十一)
发表时间:2020-02-20 00:01:28
讲解IO流之字节流。

字节流是用来传送图片、视频、音频、大文件、文本等各类文件,所有的电脑文件都可以使用通过字节流进行传输。

字节流常见的操作接口类如下: - InputStream 字节输入流 - OutputStream 字节输出流 - FileInputStream 实例化字节输入流 - FileOutputStream 实例化字节输出流 - BufferedInputStream 加强版输入流,用于大文件传输时输入缓存 - BufferedOutputStream 加强版输出流,用于大文件传输时输出缓存

其中,InputStream和OutputStream是两个抽象类不能直接实例,必须实例化它们的子类。而FileInputStream/FileOutputStream和BufferedInputStream/BufferedOutputStream分别是它们的子类。

1.FileInputSteram和FileOutputStream实现文件读写

++InputStream最常用的方法如下:++

public abstract int read() throws IOException;:抽象方法。从输入流读取单个字节,返回所读取的字节数据(字节数据可直接转换为int类型)。返回-1表示到了输入流的结束点。

public int read(byte b[], int off, int len) throws IOException:从输入流中最多读取len个字节的数据,并将其存储在数组b中,放入数组b时,并不是从数组起点开始,而是从off位置开始,返回实际(实际长度可能小于len)读取的字节数。返回-1表示到了输入流的结束点。

public int read(byte b[]) throws IOException:从输入流中最多读取b.length个字节的数据,并将其存储在数组b中,返回实际读取的字节数。相当于调用read(b, 0, b.length)。返回-1表示到了输入流的结束点。

++OutputStream最常用方法如下:++ public abstract void write(int b) throws IOException;:将指定的一个字节输出到输出流中,此处b表示字节。

public void write(byte b[], int off, int len) throws IOException:将字节数组中从off位置开始,长度为len的字节输出到输出流中。

public void write(byte b[]) throws IOException:将字节数组中的数据输出到指定输出流中。相当于调了write(b, 0, b.length)。

实例:使用字节流实现一个图片文件的复制每次读写一个字节并统计总用时多少毫秒。

public class FileOutputStreamDemo {

    public static void main(String[] args) {

        File srcFile = new File("d:" + File.separator + "java_logo.jpg");
        File dest = new File("d:" + File.separator +  "dest.jpg");
        InputStream fin=null;
        OutputStream fout=null;
        try {
            fin = new FileInputStream(srcFile);
            fout = new FileOutputStream(dest);
            long startTime = System.currentTimeMillis();

            //一边读,一边写
            int data=-1;
            while((data= fin.read())!=-1){
                fout.write(data);
            }
            long endTime = System.currentTimeMillis();
            System.out.println("总用时:"+(endTime - startTime)+"毫秒");

        } catch (Exception ex) {
            ex.printStackTrace();
        }finally{
            if(fin!=null){
                try {
                    fin.close();
                    fin=null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fout!=null){
                try {
                    fout.close();
                    fout=null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果:
总用时:351毫秒

字节流也可以读写字符文件,例如:使用字节流读取当前java源码代码文件,并且输出到控制台。

public static void main(String[] args) {

        //读出当前源程序的内容,输出到控制台。
        InputStream fin =null;
        try {
            File file = new File("D:\\java_lesson\\iodemo\\src\\streamdemo\\FileInputStreamDemo.java");//源文件的绝对地址
            fin = new FileInputStream(file);
            byte[] buffer = new byte[fin.available()];//获取源文件的字节长度。
            fin.read(buffer);
            String temp = new String(buffer);//构造新字符串
            System.out.println(temp);

        }catch(Exception ex){
            ex.printStackTrace();
        }finally {
            if(fin!=null){
                try {
                    fin.close();
                    fin=null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

2.BufferedInputStream和BufferedOutputStream

BufferedInputStream和BufferedOutputStream 是带缓冲的输入和输出流可以大大提升读写效率。 把上面的图片复制的例子改写为使用带缓冲的流实现。

public class FileOutputStreamDemo {

    public static void main(String[] args) {

        File srcFile = new File("d:" + File.separator + "java_logo.jpg");
        File dest = new File("d:" + File.separator +  "dest.jpg");
        InputStream fin=null;
        OutputStream fout=null;
        try {
            fin = new BufferedInputStream(new FileInputStream(srcFile));
            fout = new BufferedOutputStream(new FileOutputStream(dest));
            long startTime = System.currentTimeMillis();

            //一边读,一边写
            byte[] buffer = new byte[1024]; //定义缓冲区大小
            int len = -1;
            while((len=fin.read(buffer))!=-1){ //通过缓冲区读写
                fout.write(buffer,0,len);
            }

            long endTime = System.currentTimeMillis();
            System.out.println("总用时:"+(endTime - startTime)+"毫秒");

        } catch (Exception ex) {
            ex.printStackTrace();
        }finally{
            if(fin!=null){
                try {
                    fin.close();
                    fin=null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fout!=null){
                try {
                    fout.close();
                    fout=null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果:
总用时:1毫秒

3.FileInputStream和BufferedInputStream区别

BufferedInputStream 有个内部缓冲区当read时会先把缓冲区填满(默认缓冲区是8192),然后下次读取是直接从缓冲区读取。当读取的位置大于缓冲区时会再一次加载缓冲区。read()和read(byte[] buf, int off, int len)处理方式一样,区别在于后者一次返回多个数据,但是同样都是先放入缓冲区,然后再读取。

至于性能问题,我们都知道文件的物理读取性能肯定要大于内存读取,FileInputStream.read()相当于一次物理读取,而BufferedInputStream .read()大部分相当于一次内存读取。同理FileOutputStream和BufferedOutputStream原理也是一样,也是有个缓冲区,每次write相当于写入缓冲区,当缓冲区慢了后在物理写入文件。

4.综合案例

使用字节流实现文件夹的拷贝。

import java.io.*;

/*
 * 使用字节流实现目录复制的程序。
 *
 * */
public class DirectoryCopyDemo {

    //复制文件
    //src:源文件 source 源
    //dest:目标文件 destination 目标
    public static void copy(File src, File dest) {
        BufferedInputStream bin = null;  //buffered input  输入流
        BufferedOutputStream bout = null; //buffered output 输出流
        int len = -1; //每次读取的字节的个数
        byte[] buff = new byte[1024]; //一个kb的缓冲区。
        try {
            bin = new BufferedInputStream(new FileInputStream(src));
            bout = new BufferedOutputStream(new FileOutputStream(dest));
            //一头读,一头写
            while ((len = bin.read(buff, 0, buff.length)) != -1) {
                bout.write(buff, 0, len);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (bin != null) {
                    bin.close();
                    bin = null;
                }
                if (bout != null) {
                    bout.close();
                    bout = null;
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void directoryCopy(File srcDir,File destDir){
        //源目录肯定存在,目标目录不一定存在。
        try{
            if(!destDir.exists()){
                destDir.mkdirs();
            }
            //遍历
            File[] files = srcDir.listFiles();
            for(File f: files){
                //是不是目录
                if(f.isDirectory()){
                    //递归,
                   File tempDir = new File(destDir,f.getName());
                   directoryCopy(f,tempDir);
                }else{
                    File tempFile = new File(destDir,f.getName());
                    copy(f,tempFile);
                }
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        File srcDir = new File("E:"+File.separator+"web_lesson");
        File destDir = new File("H:"+File.separator+"web_lesson");
        directoryCopy(srcDir,destDir);
    }
}