今天请了一天的假,去医院看了看腰,拍了一个片,还好没事,拿了点膏药,注意休息。医生让我在床上躺一个星期,但是感觉一整天躺在床上,太浪费光阴了,所以小写点东西,剩下的时间就休息。
字节流不仅可以读取字符文件,也可以读写字节文件。今天先用字节流实现读写字符文件,然后再使用字节流来复制一张图片(这可就是字节数据了。),再使用字节缓冲流实现复制一个MP3文件
- 使用字节流读写字符文件。
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的读写可能了解的更少。