工作原理与缓冲区机制
在 Java 的 I/O 体系中,文件的读写涉及到程序与操作系统、磁盘等外部设备的交互。这种交互相对较慢,因为它涉及到硬件操作。`BufferedReader`和`BufferedWriter`采用了缓冲区机制来减少这种交互的次数。
`BufferedReader`
`BufferedReader`内部有一个缓冲区(本质上是一个字符数组)。当调用`read()`或`readLine()`方法读取数据时,它不是每次都直接从数据源(如文件)读取一个字符或一行,而是一次性从数据源读取一大块数据到缓冲区中。后续的读取操作就直接从缓冲区中获取数据,当缓冲区中的数据被读完后,再从数据源读取下一块数据到缓冲区。
例如,若要读取一个大文件中的字符,不使用`BufferedReader`时,每次调用`read()`方法都会触发一次从文件到程序的 I/O 操作;而使用`BufferedReader`时,它会一次性读取很多字符到缓冲区,后续的`read()`操作就直接从缓冲区取数据,避免了频繁的 I/O 操作。
`BufferedWriter`
`BufferedWriter`同样有一个缓冲区。当调用`write()`方法写入数据时,数据会先被写入到缓冲区中,而不是直接写入到目标设备(如文件)。当缓冲区满了或者调用`flush()`方法(强制将缓冲区中的数据写入目标设备)、`close()`方法(关闭流时会自动调用`flush()`)时,才会将缓冲区中的数据一次性写入到目标设备。
减少系统调用
系统调用是指程序向操作系统请求服务的过程,在 I/O 操作中,从程序向磁盘或其他外部设备读写数据就需要通过系统调用。系统调用的开销比较大,因为涉及到用户态和内核态的切换。
`BufferedReader`减少读取时的系统调用
假设要读取一个包含 1000 个字符的文件,如果不使用`BufferedReader`,每次读取一个字符就需要进行一次系统调用,总共要进行 1000 次系统调用。而使用`BufferedReader`,假设缓冲区大小为 100 个字符,那么只需要进行 10 次系统调用(每次读取 100 个字符到缓冲区),大大减少了系统调用的次数,从而提高了读取效率。
`BufferedWriter`减少写入时的系统调用
在写入数据时,如果不使用`BufferedWriter`,每次写入一个字符就会触发一次系统调用。而使用`BufferedWriter`,数据先在缓冲区中积累,当缓冲区满了或者满足其他写入条件时,才一次性将缓冲区中的数据写入,减少了系统调用的次数,提高了写入效率。
示例代码说明
下面是一个使用`BufferedReader`和`BufferedWriter`复制文件的示例,展示它们如何提高效率。
java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class BufferExample {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这个示例中,`BufferedReader`一次性从`input.txt`文件读取多行数据到缓冲区,`BufferedWriter`将数据先写入缓冲区,等缓冲区满或者操作结束时再一次性写入`output.txt`文件,减少了与文件系统的交互次数,提高了文件复制的效率。