java IO 三(字节流的使用)

来源:互联网 发布:做网页的软件 知乎 编辑:程序博客网 时间:2024/06/10 16:47

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

今天开始学习字节流InputStream,OutPutStream,小记录一下,方便以后查阅。字节流不仅可以读取字符文件,也可以读写字节文件。所以说,字节流黑白通吃。今天先用字节流实现以下读写字符文件,然后再使用字节流来复制一张图片(这可就是字节数据了。),再使用字节缓冲流实现复制一个MP3文件

  1. 第一个demo,使用字节流读写字符文件。为了使代码简洁,demo中的异常我就简单的抛出,让java虚拟机来处理。
package javase;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class FileStream {    public static void main(String[] args) throws IOException {        writeFile();        readFile();    }    /**     * 使用字节流向filestream.txt文件中写入helloworld这个字符串     * @throws IOException     */    public static void writeFile() throws IOException {        FileOutputStream fos = null;        //在项目的根目录下新建一个filestream.txt文件        fos = new FileOutputStream("filestream.txt");        byte[] data = "helloworld".getBytes();//把字符串转化为字节数据        fos.write(data);//写出数据        //关闭输出流        if (fos != null) {            fos.close();        }    }    /**     * 通过字节流把filestream.txt文件中的内容读出来     * @throws IOException     */    public static void readFile() throws IOException {        FileInputStream fis =null;        fis= new FileInputStream("filestream.txt");        byte[]buf=new byte[1024];//自定义一个缓冲区        int len=0;        //循环读取数据        while((len=fis.read(buf))!=-1){            //把读到的字节数据转化成字符串输出            System.out.println(new String(buf,0,len));        }        //关闭输入流        if (fis!=null) {            fis.close();        }    }}

下面看一看几个字节输出流写出数据的方法

void write(byte[] b) //向外写出一个byte[]数组void write(byte[] b, int off, int len) //写出一个byte[]数组的一部分,off 起始位置,len,起始位置到b末尾的字节个数void write(int b) //向外一次写一个字节,想必效率不咋得啊。

字节输入流的读数据方法和字节输出流是对应的,就不列出来了。

2.使用字节流复制一张图片。

/**     * 复制一张图片     */    public static void copyImg(){        FileOutputStream fos=null;        FileInputStream fis=null;        try {        //原始图片路径            fis=new FileInputStream("D:/original.png");        //文件复制的目的地路径            fos=new FileOutputStream("D:/destination.png");            byte[]buf=new byte[1024];            int len=0;            //循环读写            while((len=fis.read(buf))!=-1){                fos.write(buf);            }            fos.flush();        } catch (IOException e) {            e.printStackTrace();        }finally {        //关闭流文件            if (fis!=null) {                try {                    fis.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (fos!=null) {                try {                    fos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }

使用字节缓冲流,复制一个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()方法

//返回一个字节,当到了文件末尾返回-1;我们发现 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;       取值范围在08192之间包括08192.当buf数组中没有数据的时候取值是0     * The index one greater than the index of the last valid byte in     * the buffer.     * This value is always     * in the range <code>0</code> through <code>buf.length</code>;     * elements <code>buf[0]</code>  through <code>buf[count-1]     * </code>contain buffered input data obtained     * from the underlying  input stream.     */    protected int count;    /**        buf数组中的当前位置,来标记下一个将要从buf数组中读取的character。        取值为0count,如果如果值比count小,那么buf[pos]就是流中下一个字节,如果值等于count,那么下一次read或者skip操作要求从流中读取更多的数据。     * The current position in the buffer. This is the index of the next     * character to be read from the <code>buf</code> array.     * <p>     * This value is always in the range <code>0</code>     * through <code>count</code>. If it is less     * than <code>count</code>, then  <code>buf[pos]</code>     * is the next byte to be supplied as input;     * if it is equal to <code>count</code>, then     * the  next <code>read</code> or <code>skip</code>     * operation will require more bytes to be     * read from the contained  input stream.     *     * @see     java.io.BufferedInputStream#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()这个操作代码有点长,而且我也没仔细看看,就大体说说这个方法的作用。从外存中一次性读取buf长度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
原创粉丝点击