字符流与字节流
java中所有IO操作都可以认为是流的操作
常用的类
- befferedReader
- 带有缓存char的fileReader
- 入参为Rander子类, 然后在Reader基础上生成一个缓冲区域(默认8k char),提高IO效率
- FileReader entexd InputStreamReader
- 文件字符输入流
- 入参为 File
- 通过File直接创建FileInputStream流,作为输入字符流的来源 作为InputStreamReader的入参,然后根据默认字符集进行转码
- 缺陷: 不能指定读入的字符流的编码
- InputStreamReader
- 输入字符流读取,在输入流的基础上增加了转码功能
- FileInputStream
- 文件输入流,inputStream子类
- 从文件中读取输入流
BufferInputStream
- 带有缓存的输入流
- 先将IO流读取到缓存区中加速操作
FilterInputStream
- 是BufferInputStream的父类
- 没有什么实际的作用,据说更多地像是一个接口(不了解)
字符流与字节流使用了装饰者模式,如果需要一个带有缓存的文件字符输入流可以通过
bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream("filePath"))));
bufferedReader = new BufferedReader(new FileReader("filePath"));
//这两种方式其实差不多 第二种字节流读取的时候没有缓存区,可能会慢一些,而且FileReader不可以设置编码。
- 最底层是一个 文件字节流,包裹一个带缓存的字节流,通过InputStreamReader转码,再通过BufferedReader保存缓存的字符。
个人十分好奇然后又了解了一下buffer是怎么实现加速的 - 找到了一个10年的博客
https://blog.csdn.net/zealot_2002/article/details/8231194
据我了解,运用FileInputStream读写一段数据是一个字节一个字节的读取,
如果有10个字节大小的文件,就要调用10次系统调用,每次将读取的数据赋值给变量,
然后程序使用变量。缓冲区可以看作是一个放在内存中的数组,但是从硬盘中读取数据
仍然要使用系统调用,系统调用的读取仍然是每次一个,只是每调用一次之后,将所得
到的数据放入缓冲区中的,然后程序一次性使用10个数据。
我是这样理解的,但是不管用与不用缓冲区,使用的系统调用是一样多的。
(不知我的理解正确与否,请指出)如果我理解的是正确的,那么为什么使用缓冲区
读写的效率要高呢??谢谢!
满意回答
2010-11-21 17:31
理解是对的。
调用I\O操作的时候,实际上还是一个一个的读或者写,关键就在,CPU只有一个,
不论是几个核心。CPU在系统调用时,会不会还要参与主要操作?参与多次就会花更多的时间。
系统调用时,若不用缓冲,CPU会酌情考虑使用
中断。此时CPU是主动地,每个周期中都要花去一部分去询问I\O设备是否读完数据,
这段时间CPU不能做任何其他的事情(至少负责执行这段模块的核不能)。
所以,调用一次读了一个字,通报一次,CPU腾出时间处理一次。
而设置缓冲,CPU通常会使用 DMA 方式去执行 I\O 操作。CPU
将这个工作交给DMA控制器来做,自己腾出时间做其他的事,当DMA完成工作时,
DMA会主动告诉CPU“操作完成”。这时,CPU接管后续工作。在此,CPU 是被动的。
DMA是专门 做 I\O 与 内存数据交换的,不仅自身效率高,也节约了CPU时间,
CPU在DMA开始和结束时做了一些设置罢了。
所以,调用一次,不必通报CPU,等缓冲区满了,DMA 会对CPU 说
“嘿,伙计!快过来看看,把他们都搬走吧”。
综上,设置缓冲,就建立了数据块,使得DMA执行更方便,CPU也有空闲,而不是呆呆地候着I\O数据读来。从微观角度来说,设置缓冲效率要高很多。尽管,不能从这个程序上看出来。 几万字的读写\就能看到差距
这个回答涉及了底层硬件DMA的知识,我不太了解底层是否真的会这样去做,
根据java BufferedInputStream的源码
BufferedInputStream.java
/**
* Fills the buffer with more data, taking into account
* shuffling and other tricks for dealing with marks.
* Assumes that it is being called by a synchronized method.
* This method also assumes that all data has already been read in,
* hence pos > count.
*/
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
//删除了markpos不为0的处理
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
它调用了传入字节流的read方法,然后将数据保存到buffer对象中去
FileInputStream.javax
private native int readBytes(byte b[], int off, int len) throws IOException;
public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}
- FileInputStream中的read调用native方法,入参是我们的 (buffer数组,off,len)
- 结合上下我只能认为这个native方法中会实现DMA操作,
但是这个效率和我们直接访问FileInputStream方法的结果是一样的,
他的效率提升可能是通过传入buffer较大,可以一次性地将大量IO集中起来完成(在一块存储单元中)
当我们后续使用 单字符,或较短字符数组 的读取方式时就不需要再去继续IO,提高效率 - emmm 应该是这样