一.案例
以下案例包含4个测试函数:
- 单字节的字节流
- 数组的字节流
- 单字节的缓冲流
- 数组的缓冲流
文件数据为15MB左右。
import java.io.*;
public class IO_test {
// 单字节的字节流
public static void copy(File in, File out) throws IOException {
InputStream is = new FileInputStream(in);
OutputStream os = new FileOutputStream(out);
int by = 0;
while ((by = is.read()) != -1) {
os.write(by);
}
is.close();
os.close();
}
// 数组的字节流
public static void copy_batch(File in, File out) throws IOException {
InputStream is = new FileInputStream(in);
OutputStream os = new FileOutputStream(out);
byte[] array = new byte[1024];
int len = 0;
while ((len = is.read(array)) != -1) {
os.write(array,0,len);
}
is.close();
os.close();
}
// 单字节的缓冲流
public static void bufferedCopy(File in, File out) throws IOException {
BufferedInputStream bi = new BufferedInputStream(new FileInputStream(in));
BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(out));
int by = 0;
while ((by = bi.read()) != -1) {
bo.write(by);
bo.flush();
}
bo.close();
bi.close();
}
// 数组的缓冲流
public static void bufferedCopy_batch(File in, File out) throws IOException {
BufferedInputStream bi = new BufferedInputStream(new FileInputStream(in));
BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(out));
byte[] array = new byte[1024];
int len = 0;
while ((len = bi.read(array)) != -1) {
bo.write(array,0,len);
bo.flush();
}
bo.close();
bi.close();
}
public static void main(String[] args) throws IOException {
File data = new File("E:\\test.mp4");
File a = new File("E:\\test2.mp4");
File b = new File("E:\\test3.mp4");
File c = new File("E:\\test4.mp4");
File d = new File("E:\\test5.mp4");
long start = System.currentTimeMillis();
copy(data, a);
long end = System.currentTimeMillis();
long start2 = System.currentTimeMillis();
copy_batch(data, b);
long end2 = System.currentTimeMillis();
long start3 = System.currentTimeMillis();
bufferedCopy(data, c);
long end3 = System.currentTimeMillis();
long start4 = System.currentTimeMillis();
bufferedCopy_batch(data, d);
long end4 = System.currentTimeMillis();
System.out.println("单字节的字节流耗时:" + (end - start) + " ms");
System.out.println("数组的字节流耗时:" + (end2 - start2) + " ms");
System.out.println("单字节的缓冲流耗时:" + (end3 - start3) + " ms");
System.out.println("数组的缓冲流耗时:" + (end4 - start4) + " ms");
}
}
运行结果:
单字节的字节流耗时:67970 ms
数组的字节流耗时:92 ms
单字节的缓冲流耗时:38420 ms
数组的缓冲流耗时:60 ms
二.问题解决
单字节的缓冲流为什么这么慢
上方的单字节的缓冲流耗时:38420 ms,太长了。
因为执行了同步的flush(),去掉后变为 90ms。
其实感觉没必要写flush(),看源码中write(),自带了flushBuffer()操作:
@Override
public synchronized void write(int b) throws IOException {
if (count >= buf.length) {
flushBuffer();
}
buf[count++] = (byte)b;
}
private void flushBuffer() throws IOException {
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
}
数组的缓冲流到底是什么鬼
单字节的缓冲流其实是用到了BufferedInputStream类中的数组。
public synchronized int read() throws IOException {
if (pos >= count) { //当buf中已经缓存的字节都读取完了,那么将输入流的数据继续读入缓存buf中.
fill();
if (pos >= count) //当发现输入流已经读取完毕了,那么退出.
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
private void fill() throws IOException {
//省略...
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
//省略...
点击read(buffer, pos, buffer.length - pos)这个函数,跳到了InputStream类下实现的read()。
有个问题,案例中第四个方法bufferedCopy_batch中read()函数传递进去的是一个数组,就有个疑问,BufferedInputStream中不是有数组当作缓存么,怎么又传进去一个数组,什么鬼?
原来,调用的不是BufferedInputStream下的read(),而是FilterInputStream下的。然后再点击,跳到了InputStream类下实现的read()。
class FilterInputStream extends InputStream {
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
}
其实说了这么多,原来不管BufferedInputStream中的read()函数,传进去的是字节还是数组,原理一样。不同的是前面用的BufferedInputStream自带的数组,后者用的自己的数组当作缓存。
装饰者模式是什么东西
虽然java的IO很复杂,但是学懂了其实很简单。
很多InputStream的实现类FileInputStream等等,这些类如果都要加同样的属性或者方法,则每个类下都要继承一个类来增添这些东西。会造成类太多。
FilterInputStream这个类就用来装饰,把要加的同样的属性或者方法写在BufferedInputStream这个类里,InputStream的实现类的实例(比如FileInputStream)传入到BufferedInputStream中,通过使用BufferedInputStream,则每个实例都能用到加的同样的属性或者方法。减少了冗余。