java IO 三(字节流的使用)
来源:互联网 发布:做网页的软件 知乎 编辑:程序博客网 时间:2024/06/10 16:47
今天请了一天的假,去医院看了看腰,拍了一个片,还好没事,拿了点膏药,注意休息。医生让我在床上躺一个星期,但是感觉一整天躺在床上,太浪费光阴了,所以小写点东西,剩下的时间就休息。
今天开始学习字节流InputStream,OutPutStream,小记录一下,方便以后查阅。字节流不仅可以读取字符文件,也可以读写字节文件。所以说,字节流黑白通吃。今天先用字节流实现以下读写字符文件,然后再使用字节流来复制一张图片(这可就是字节数据了。),再使用字节缓冲流实现复制一个MP3文件
- 第一个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; 取值范围在0和8192之间包括0和8192.当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。 取值为0到count,如果如果值比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的读写可能了解的更少。
今天就到这里,虽然医生建议我躺一个星期,但是明天还要继续上班,为了活下去,为了自己的责任,为了自己的理想,坚持学习,坚持努力!
- java IO 三(字节流的使用)
- java IO 字节流的使用
- Java基础---IO流三(字节流& 字符流)
- java IO (三) 字节流与缓冲
- Java-IO(三)——字节流
- IO 字节流的使用
- IO-字节流的使用
- Java IO流之字节流的使用
- JAVA IO :使用带缓冲的字节流读写数据
- Java IO 字符流 和 字节 的使用
- 使用java的IO字节流拷贝图片
- JAVA IO使用高速缓冲区的字节流复制文件
- Java IO 字节流
- java IO字节流
- 【Java】IO字节流
- Java IO字节流
- IO流中字节流的使用
- java中 IO流 总结之三:字节流总结
- xpath中normalize-space的实际应用
- Java基础随笔之静态导入
- sublime text 3103 怎么设置中文
- C#获取汉字的拼音首字母帮助类库
- 加班那点事(三)
- java IO 三(字节流的使用)
- 对“视觉机器学习20讲配套仿真代码”的研究心得---决策树学习
- Sublime Text 2 使用过程记录
- Maven pom文件提示Missing artifact org.springframework:spring-context-support:jar:3.2.2.RELEASE:compile
- 从前端开始
- Java中两种多线程实现方法:Thread类和Runnable接口的联系与区别
- MD5_百度百科
- 别问我为什么开通了这个新的博客,其实我也不知道
- 解读前端模板引擎Mustache.js源码