20.6 字节缓冲流
BufferedInputStream与BufferedOutputStream分别是FilterInputStream类和FilterOutputStream类的子类,实现了装饰设计模式。提高了读写性能。
笔记:
1.BufferedInputStream内部实现:内部默认是8Kb大小的缓冲区,每一次读取read的字节大小可以自己定:
(1)若read每次读取的大小 >= 缓冲区,则直接从原始输入流中进行读取,从而避免无谓的COPY(从原始输入流至缓冲区,读取缓冲区全部数据,清空缓冲区,重新填入原始输入流数据)
(2)若read每次读取的大小 < 缓冲区时,当文件内容大于缓冲区大小,则首先会从磁盘要读取的文件中一次性填满缓冲区,然后每一次read都从缓冲区读取,每次read时都会比较缓冲区剩余有效的字节数,如果缓冲区剩余可读内容字节大小小于每次read读取的字节数时,缓冲区才会访问磁盘该文件并读满缓冲区,直到最后读取完该文件,若文件剩余内容小于缓冲区大小时,则全部内容读进缓冲区,然后read依然每次从缓冲区读取,最后一次读取若缓冲区内容小于read每次读取大小时,因为缓冲区已读完文件,所以read最后一次会将缓冲区剩余的字节内容全部读取。
2.BufferedOutputStream内部实现:内部默认也是8kb大小的缓冲区,每一次写入write的字节大小可以自己定:
(1)若write每次写入的大小 >= 缓冲区,则直接从原始输入流中进行写入,从而避免无谓的COPY(从原始输入流至缓冲区,写满缓冲区数据,再写入文件后清空缓冲区,重新填入原始输出流数据)
(2)若write每次写入的大小 < 缓冲区时,则每次write到缓冲区,会先比较缓冲区剩余可缓冲大小是否大于每次write的大小,若大于则直接write到缓冲区,若小于则会将缓冲区的内容先写入到磁盘文件后清空缓冲区,再write到缓冲区。当最后一次write到缓冲区后,缓冲区仍然还有剩余容量时,则必须手动刷新才会将缓冲区内容写入磁盘,即调用flush()方法,在close()方法中也会自动调用flush()方法。
(3)jdk1.7后:若直接new在try()的括号中,则会自动调用close(),这样也就自动调用了flush()方法。
3.为什么需要缓冲呢?
原因很简单,效率问题!缓冲中的数据实际上是保存在内存中,而原始数据可能是保存在硬盘或NandFlash等存储介质中;而我们知道,从内存中读取数据的速度比从硬盘读取数据的速度至少快10倍以上。
4.那干嘛不干脆一次性将全部数据都读取到缓冲中呢?
第一,读取全部的数据所需要的时间可能会很长。第二,内存价格很贵,容量不像硬盘那么大。
20.6.1字节输入缓冲流 BufferedInputStream
BufferedInputStream是带缓冲区的输入流,默认缓冲区大小是8Kb,能够减少访问磁盘的次数,提高文件读取性能;
使用方式:
try {
File file = new File("test.txt");
InputStream fos = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fos,2*1024);//2*1024设置需要的缓冲区大小
byte b[] =new byte[1024];
while (bis.read(b)!=-1) {
for (byte c : b) {
System.out.println((char)c);
}
}
bis.close();
}
20.6.2 字节输出缓冲流 BufferedOutputStream
BufferedOutputStream是带缓冲区的输出流,能够提高文件的写入效率。
使用方式:
try {
File file = new File("test.txt");
OutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos =new BufferedOutputStream(fos,2*1024);//2*1024设置需要的缓冲区大小
byte b = "a";
bos.write(b);
bos.flush();//带有缓冲区,所以必须刷新。
bos.close();
}...
20.6.3 字节缓冲输入流特有的方法(了解)
(了解)mark(int readlimit) 方法(只有BufferedInputStream才支持),在流的当前位置做个标记,参数readLimit指定这个标记的“有效期”,如果从标记处开始往后,已经获取或者跳过了readLimit个字节,那么这个标记失效,不允许再重新回到这个位置(通过reset方法)。也就是你想回头不能走得太远呀,浪子回头不一定是岸了,跳过(获取)了太多字节,标记就不再等你啦。多次调用这个方法,前面的标记会被覆盖。
如果我们在 M 处做标记,readLimit为绿色部分,当流的指针在 A 处的时候,这个标记依然有效,可是一旦指针跑到 B 处,标记就失效了。
(了解)reset() 方法(只有BufferedInputStream才支持),用于重定位到最近的标记。如果在这之前mark方法从来没被调用,或者标记已经无效,会抛出IOException。如果没有抛出这个异常,将当前位置重新定位到最近的标记位置。
InputStream is = null; try { is = new BufferedInputStream(new FileInputStream("test.txt")); is.mark(4); is.skip(2); is.reset(); System.out.println((char)is.read()); } finally { if (is != null) { is.close(); } } }
课后扩展笔记:
PS:不同的系统针对不同的换行符号识别是不一样的?
windows:\r\n
linux:\n
Mac:\r
而一些常见的个高级记事本,是可以识别任意换行符号的。