缓冲流
Java 的缓冲流是对字节流和字符流的一种封装,通过在内存中开辟缓冲区来提高 I/O 操作的效率。Java 通过 BufferedInputStream 和 BufferedOutputStream 来实现字节流的缓冲,通过 BufferedReader 和 BufferedWriter 来实现字符流的缓冲。
缓冲流的工作原理是将数据先写入缓冲区中,当缓冲区满时再一次性写入文件或输出流,或者当缓冲区为空时一次性从文件或输入流中读取一定量的数据。这样可以减少系统的 I/O 操作次数,提高系统的 I/O 效率,从而提高程序的运行效率。
字节缓冲流
构造方法
BufferedInputStream(InputStream in)
:创建一个新的缓冲输入流,注意参数类型为InputStream。BufferedOutputStream(OutputStream out)
: 创建一个新的缓冲输出流,注意参数类型为OutputStream。
// 创建字节缓冲输入流,先声明字节流 FileInputStream fps = new FileInputStream(b.txt); BufferedInputStream bis = new BufferedInputStream(fps) // 创建字节缓冲输入流(一步到位) BufferedInputStream bis = new BufferedInputStream(new FileInputStream("b.txt")); // 创建字节缓冲输出流(一步到位) BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));
字节缓冲流为什么快
传统的 Java IO 是阻塞模式的,它的工作状态就是“读/写,等待,读/写,等待。。。。。。”
字节缓冲流解决的就是这个问题:一次多读点多写点,减少读写的频率,用空间换时间。
- 减少系统调用次数:在使用字节缓冲流时,数据不是立即写入磁盘或输出流,而是先写入缓冲区,当缓冲区满时再一次性写入磁盘或输出流。这样可以减少系统调用的次数,从而提高 I/O 操作的效率。
- 减少磁盘读写次数:在使用字节缓冲流时,当需要读取数据时,缓冲流会先从缓冲区中读取数据,如果缓冲区中没有足够的数据,则会一次性从磁盘或输入流中读取一定量的数据。同样地,当需要写入数据时,缓冲流会先将数据写入缓冲区,如果缓冲区满了,则会一次性将缓冲区中的数据写入磁盘或输出流。这样可以减少磁盘读写的次数,从而提高 I/O 操作的效率。
- 提高数据传输效率:在使用字节缓冲流时,由于数据是以块的形式进行传输,因此可以减少数据传输的次数,从而提高数据传输的效率。
BufferedInputStream 的 read 方法:
public synchronized int read() throws IOException { if (pos >= count) { // 如果当前位置已经到达缓冲区末尾 fill(); // 填充缓冲区 if (pos >= count) // 如果填充后仍然到达缓冲区末尾,说明已经读取完毕 return -1; // 返回 -1 表示已经读取完毕 } return getBufIfOpen()[pos++] & 0xff; // 返回当前位置的字节,并将位置加 1 }
这段代码主要有两部分:
fill()
:该方法会将缓冲 buf 填满。getBufIfOpen()[pos++] & 0xff
:返回当前读取位置 pos 处的字节(getBufIfOpen()
返回的是 buffer 数组,是 byte 类型),并将其与 0xff 进行位与运算。这里的目的是将读取到的字节 b 当做无符号的字节处理,因为 Java 的 byte 类型是有符号的,而将 b 与 0xff 进行位与运算,就可以将其转换为无符号的字节,其范围为 0 到 255。
FileInputStream 的 read 方法:
public int read() throws IOException { return read0(); } private native int read0() throws IOException;
BufferedOutputStream 的 write(byte b[], int off, int len)
方法:
public synchronized void write(byte b[], int off, int len) throws IOException { if (len >= buf.length) { // 如果写入的字节数大于等于缓冲区长度 /* 如果请求的长度超过了输出缓冲区的大小, 先刷新缓冲区,然后直接将数据写入。 这样可以避免缓冲流级联时的问题。*/ flushBuffer(); // 先刷新缓冲区 out.write(b, off, len); // 直接将数据写入输出流 return; } if (len > buf.length - count) { // 如果写入的字节数大于空余空间 flushBuffer(); // 先刷新缓冲区 } System.arraycopy(b, off, buf, count, len); // 将数据拷贝到缓冲区中 count += len; // 更新计数器 }
首先,该方法会检查写入的字节数是否大于等于缓冲区长度,如果是,则先将缓冲区中的数据刷新到磁盘中,然后直接将数据写入输出流。这样做是为了避免缓冲流级联时的问题,即缓冲区的大小不足以容纳写入的数据时,可能会引发级联刷新,导致效率降低。
级联问题(Cascade Problem)是指在一组缓冲流(Buffered Stream)中,由于缓冲区的大小不足以容纳要写入的数据,导致数据被分割成多个部分,并分别写入到不同的缓冲区中,最终需要逐个刷新缓冲区,从而导致性能下降的问题。
其次,如果写入的字节数小于缓冲区长度,则检查缓冲区中剩余的空间是否足够容纳要写入的字节数,如果不够,则先将缓冲区中的数据刷新到磁盘中。然后,使用 System.arraycopy()
方法将要写入的数据拷贝到缓冲区中,并更新计数器 count。
最后,如果写入的字节数小于缓冲区长度且缓冲区中还有剩余空间,则直接将要写入的数据拷贝到缓冲区中,并更新计数器 count。
也就是说,只有当 buf 写满了,才会 flush,将数据刷到磁盘,默认一次刷 8192 个字节。
public BufferedOutputStream(OutputStream out) { this(out, 8192); }
FileOutputStream 的 write 方法:
public void write(int b) throws IOException { write(b, append); } private native void write(int b, boolean append) throws IOException;
字符缓冲流
BufferedReader 类继承自 Reader 类,提供了一些便捷的方法,例如 readLine()
方法可以一次读取一行数据,而不是一个字符一个字符地读取。
BufferedWriter 类继承自 Writer 类,提供了一些便捷的方法,例如 newLine()
方法可以写入一个系统特定的行分隔符。
构造方法
BufferedReader(Reader in)
:创建一个新的缓冲输入流,注意参数类型为Reader。BufferedWriter(Writer out)
: 创建一个新的缓冲输出流,注意参数类型为Writer。
// 创建字符缓冲输入流 BufferedReader br = new BufferedReader(new FileReader("b.txt")); // 创建字符缓冲输出流 BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
字符缓冲流特有方法
- BufferedReader:
String readLine()
: 读一行数据,读取到最后返回 null - BufferedWriter:
newLine()
: 换行,由系统定义换行符。
// 创建流对象 BufferedReader br = new BufferedReader(new FileReader("a.txt")); // 定义字符串,保存读取的一行文字 String line = null; // 循环读取,读取到最后返回null while ((line = br.readLine())!=null) { System.out.print(line); System.out.println("------"); } // 释放资源 br.close();
// 创建流对象 BfferedWriter bw = new BufferedWriter(new FileWriter("b.txt")); // 写出数据 bw.write("沉"); // 写出换行 bw.newLine(); bw.write("默"); bw.newLine(); bw.write("王"); bw.newLine(); bw.write("二"); bw.newLine(); // 释放资源 bw.close();
本文作者:n1ce2cv
本文链接:https://www.cnblogs.com/sprinining/p/18336041
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步