java IO 三(字节流的使用)

今天请了一天的假,去医院看了看腰,拍了一个片,还好没事,拿了点膏药,注意休息。医生让我在床上躺一个星期,但是感觉一整天躺在床上,太浪费光阴了,所以小写点东西,剩下的时间就休息。

字节流不仅可以读取字符文件,也可以读写字节文件。今天先用字节流实现读写字符文件,然后再使用字节流来复制一张图片(这可就是字节数据了。),再使用字节缓冲流实现复制一个MP3文件

  1. 使用字节流读写字符文件。
private static void test() {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream("D:\\FileInputStreamTest.java"));
            bos = new BufferedOutputStream(new FileOutputStream("D:\\io.java"));
            int hasRead;
            while ((hasRead = bis.read()) != -1) {
                bos.write(hasRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bis != null) {
                    bis.close();
                }
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

2.使用字节流复制一张图片,跟读写字符文件没有区别

private static void test() {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream("D:\\image.png"));
            bos = new BufferedOutputStream(new FileOutputStream("D:\\imageCopy.png"));
            int hasRead;
            while ((hasRead = bis.read()) != -1) {
                bos.write(hasRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bis != null) {
                    bis.close();
                }
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

3.使用字节缓冲流,复制一个MP3文件,和上面两个操作是一样的

/**
     * 使用字节缓冲流复制一个MP3文件
     */
    public static void copyMp3() {
        BufferedOutputStream bufos = null;
        BufferedInputStream bufis = null;

        try {
        //源文件
            bufis = new BufferedInputStream(new FileInputStream("D:/one.mp3"));
            //目标文件
            bufos = new BufferedOutputStream(new FileOutputStream("D:/one_copy.mp3"));
            int len = 0;
            //使用缓冲流循环进行读写操作
            while ((len = bufis.read()) != -1) {
                bufos.write(len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bufos != null) {
                try {
                    bufos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (bufis != null) {
                try {
                    bufis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

关于字节缓冲流的缓冲,我还是有点疑惑的:看代码说话;

//源文件
            bufis = new BufferedInputStream(new FileInputStream("D:/one.mp3"));
            //目标文件
            bufos = new BufferedOutputStream(new FileOutputStream("D:/one_copy.mp3"));

上面是分别构造一个缓冲字节输入流bufis,缓冲字节输出流bufos。我们接下来看看BufferedInputStream和BufferedOutputStream的部分源码。

发现BufferedInputStream类有一个字节数组是:protected byte buf[];当我们调用BufferedInputStream的一个参数的构造函数的时候,实际上在内部调用了BufferedInputStream的两个参数的构造函数,看代码。

 public BufferedInputStream(InputStream in) {
         //DEFAULT_BUFFER_SIZE值是8192
        this(in, DEFAULT_BUFFER_SIZE);//两个参数的构造函数
    }
//两个参数的
 public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        //初始化buf字节数组,缓冲就靠它。
        buf = new byte[size];
    }

类似的BufferedOutputStream类里面也有一个字节数组。

//BufferedOutputStream类一个参数的构造函数
 public BufferedOutputStream(OutputStream out) {
        this(out, 8192);//调用自身两个参数的构造函数
    }
//两个参数的构造函数
 public BufferedOutputStream(OutputStream out, int size) {
        super(out);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        //初始化buf数组
        buf = new byte[size];
    }

然后进行读写操作

int len = 0;
            //使用缓冲流循环进行读写操作
            while ((len = bufis.read()) != -1) {
                bufos.write(len);
            }

不就是每次bufis读入一个字节,bufos就直接写一个字节吗?你也没体现出缓冲的意思啊,骗谁呢。不要着急,继续看BufferedInputStream的read()方法

 public synchronized int read() throws IOException {
        if (pos >= count) {
            fill();
            if (pos >= count)
                return -1;
        }
        return getBufIfOpen()[pos++] & 0xff;
    }

read方法内部有一个判断if (pos >= count)是否成立。对于pos和count这两个变量,类中的定义如下

//这个索引比buf数组中最后一个合法字节的下标大1.当初始化的时候count==0;取值范围在0和buf.length之间.包括0和buf.length.当buf数组中没有数据的时候取值是0。buf[0]到buf[count-1]之间是缓冲的数据

protected int count;

//buf数组中的当前位置,是从buf数组中被读取的下一个character的下标。取值为0到count,如果值比count小,那么buf[pos]就是流中下一个要被读取字节,如果值等于count,那么下一次read或者skip操作要求先从底层的流中读取更多的数据到buf[]中。

protected int pos;

count和pos联合起来使用

//read()方法内部
  if (pos >= count) {
            fill();
            if (pos >= count)
                return -1;
        }
        return getBufIfOpen()[pos++] & 0xff;

第一次读的时候,pos==0,count==0,pos>=count成立,先执行fill()操作。fill()这个操作代码有点长,而且我也没仔细看看,就大体说说这个方法的作用。从外存中一次性读取8192个字节的数据到内存中的buf字节数组中,假如文件最后剩余300个字节,最后就读取300个字节(这是有数据的情况),方法内部会使pos>=count不成立,这时候每次read方法就会返回一个字节的数据,直到buf的末尾。这时候pos>=count又成立,fill()方法又从外存读取8192个字节的数据到buf中,read方法就再从buf中每次读取一个字节的数据。如此循环到buf的末尾,假如这时候已经读到了文件的末尾,fill()方法不可能从外存中读取数据到buf中了,fill()方法内部也不能让pos>=count不成立,所以这时候read方法就会返回-1.

简单的说:BufferedInputStream会一次性从外存中读取8192(默认情况下)个字节的数据到内存的buf字节数组中,然后,我们就可以从buf字节数组中读取数据。而不用每次读取有一个字节的数据都要直接到外存读取。这样就减少了内存和外存交互次数,提高效率。

BufferedOutPutStream也不看源码了。大体原理是:从内存中每次写出一个字节的数据到buf缓存数组中,默认长度是8192,当累计写入buf数组8192个字节的数据以后,就把这些数据一次性flush到外存中,把buf清空准备下一次写入。这样就减少了内存和外存的交互次数,所以提高了写得效率。

这就是缓存字节流的意义所在,以上所说的只是缓冲字节流的基本用法,更多用法,还得自己查看java API文档。读到这里我才豁然感觉到大学中的很多课上的还是值得的《计算机组成原理》,如果没上过这些课,我就不会知道内存和外存,对java的读写可能了解的更少。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值