关于缓冲流的一些问题
之前I/O复习的时候,有个关于字节缓冲的代码
public class Demo4 {
public static void main(String[] args) throws IOException {
//1.创建字节输入流
FileInputStream fileInputStream = new FileInputStream("D:\\i.txt");
BufferedInputStream bufferedInputStream
= new BufferedInputStream(fileInputStream);
//2.创建字节输出流
FileOutputStream fileOutputStream = new FileOutputStream("D:\\j.txt");
BufferedOutputStream bufferedOutputStream
= new BufferedOutputStream(fileOutputStream);
//3.进行读写
int num = 0;
try {
while ((num = bufferedInputStream.read()) != -1) {
bufferedOutputStream.write(num);//还是一个字节一个字节的写的
bufferedOutputStream.flush();
}
}catch (IOException e){
e.printStackTrace();
}finally {
//关闭流对象
fileInputStream.close();
fileOutputStream.close();
}
}
}
我们都知道缓冲流为我们预先开辟了一个8192的缓冲数组。
//BufferedInputStream源码片段
class BufferedInputStream extends FilterInputStream {
//默认的缓冲大小是8k
private static int DEFAULT_BUFFER_SIZE = 8192;
那么这8k缓冲数组是有什么用呢?如最上面那个demo看,似乎我们最终还是一个字符一个字符的读写。
//缓冲流
while ((num = bufferedInputStream.read()) != -1) {//num是一个字符的ascii码
bufferedOutputStream.write(num);//还是一个字符一个字符的写的
bufferedOutputStream.flush();
}
//非缓冲流
while ((num = fileInputStream.read()) != -1) {
fileOutputStream.write(num);
fileOutputStream.flush();
}
在debug模式下我们也能很清楚的看到,确实一个一个字符一个字符的写入文档的。那么这个这个8k用到哪里去了?真的快了吗?
于是做了下面这样的实验
//缓冲流copy文件
public static void copyFileByBuffer(File srcFile,File destFile) throws IOException {
//1.创建字节输入流
FileInputStream fileInputStream = new FileInputStream(srcFile);
BufferedInputStream bufferedInputStream
= new BufferedInputStream(fileInputStream);
//2.创建字节输出流
FileOutputStream fileOutputStream = new FileOutputStream(destFile);
BufferedOutputStream bufferedOutputStream
= new BufferedOutputStream(fileOutputStream);
//3.进行读写
int num = 0;
byte[] buf=new byte[1024*4];
try {
while ((num = bufferedInputStream.read(buf)) != -1) {
bufferedOutputStream.write(buf,0,num);
bufferedOutputStream.flush();
}
}catch (IOException e){
e.printStackTrace();
}finally {
//关闭流对象
fileInputStream.close();
fileOutputStream.close();
}
}
public static void copyFileBybyte(File srcFile,File destFile) throws IOException {
//1.创建字节输入流
FileInputStream fileInputStream = new FileInputStream(srcFile);
//2.创建字节输出流
FileOutputStream fileOutputStream = new FileOutputStream(destFile);
//3.进行读写
int num = 0;
byte[] buf=new byte[1024*4];
try {
while ((num = fileInputStream.read(buf)) != -1) {
fileOutputStream.write(buf,0,num);
fileOutputStream.flush();
}
}catch (IOException e){
e.printStackTrace();
}finally {
//关闭流对象
fileInputStream.close();
fileOutputStream.close();
}
}
public static void main(String[] args) throws IOException {
Long startTime=System.currentTimeMillis();
//4.27GB的一个文件
copyFileByBuffer(new File("F:\\test.iso"),new File("F:\\test2.iso"));
Long endTime=System.currentTimeMillis();
System.out.println(endTime-startTime);//24441
startTime=System.currentTimeMillis();
copyFileBybyte(new File("F:\\test.iso"),new File("F:\\test3.iso"));
endTime=System.currentTimeMillis();
System.out.println(endTime-startTime);//38365
}
上面的代码我因为文件比较大,都开了一个4k的缓冲区。发现单纯的字节流用的时间差不多是缓冲流的1.5倍。
这就很明显的看出那8k的效率了。
缓冲流8K的工作原理
普通的字节流I/O操作
如果不涉及自定义的缓冲区,操作是这样的:来一个字节,请求一次IO,然后再来一个字节再请求一次IO。
缓冲流的I/O操作
如果如果不涉及自定义的缓冲区,操作起来是这样的:来一个字节,先放入自带的缓冲区,判断缓冲区满没满或者是不是全写完了,是就请求一个IO把8k的数据一次性写出去。
上面的代码,都开了一个4k的缓冲区。但是缓冲流的缓冲区有8k,也就是差不多两个while请求一次IO。
效率区别就是在这里体现的,减少了请求IO的次数。
注意点:
这里我们发现,快的只有1.5倍?不应该是2倍吗?
当buf(我们自己定义的缓冲区大小).length<buffer(缓冲流的缓冲区大小).length。是这样工作的,数据来了先读满整个buffer,然后通过System.arrayCopy把数据copy到buf中。这一步花销了时间。
当buf>buffer时,缓冲流的buffer就不会工作了。
总结
缓冲流之所以能够提高性能,主要是利用了在内存中开辟的buf空间来实现的,减少了直接消耗系统IO资源的次数,从而提高了性能。