1. 引言
在 Java 的 I/O 体系中,BufferedInputStream 是一个非常重要的缓冲输入流。它通过在内存中维护一个缓冲区,减少底层系统调用的次数,从而显著提升文件读取和网络数据读取的性能。本文将从原理、构造方法、常用方法到实战示例,全面讲解 BufferedInputStream 的使用。
2. 什么是 BufferedInputStream
BufferedInputStream 是 java.io 包中的一个类,它继承自 FilterInputStream,本质上是为另一个输入流添加缓冲功能的包装流。当程序频繁读取单个字节或少量字节时,直接操作底层流会产生大量系统调用,性能较差;而 BufferedInputStream 会一次性从底层流读取较多数据到内存缓冲区,后续读取直接从缓冲区返回,从而大幅提升效率。
3. 类继承关系与构造方法
先来看 BufferedInputStream 在 Java 类体系中的位置:
java
java.lang.Object
└── java.io.InputStream
└── java.io.FilterInputStream
└── java.io.BufferedInputStream
BufferedInputStream 提供了两个构造方法:
BufferedInputStream(InputStream in):创建一个默认缓冲区大小(通常为 8192 字节)的缓冲输入流。BufferedInputStream(InputStream in, int size):指定缓冲区大小创建缓冲输入流。
4. 常用方法
BufferedInputStream 继承并重写了 InputStream 的核心方法,常用方法如下:
| 方法 | 说明 |
|---|---|
int read() |
读取一个字节,返回 0 到 255 的整数,读到末尾返回 -1。 |
int read(byte[] b) |
读取若干字节到数组中,返回实际读取的字节数。 |
int read(byte[] b, int off, int len) |
从指定偏移位置开始读取 len 个字节到数组中。 |
long skip(long n) |
跳过并丢弃 n 个字节。 |
int available() |
返回可以无阻塞读取的估计字节数。 |
void close() |
关闭流并释放相关资源。 |
void mark(int readlimit) |
标记当前读取位置。 |
void reset() |
将读取位置重置到最近一次 mark 的位置。 |
boolean markSupported() |
判断该流是否支持 mark 和 reset 操作,BufferedInputStream 返回 true。 |
5. 缓冲原理
BufferedInputStream 的核心是一个内部字节数组 buf,配合 pos(当前位置)、count(缓冲区有效字节数)和 markpos(标记位置)等字段实现缓冲逻辑。其工作流程大致如下:
- 当调用
read()时,先检查缓冲区中是否还有未读数据。 - 如果缓冲区有数据,直接从
buf[pos++]返回。 - 如果缓冲区数据已读完,则调用底层输入流的
read()方法,一次性填充缓冲区。 - 重复以上过程,直到底层流返回 -1 表示读取结束。
通过这种方式,即使程序逐字节读取,底层流也只在缓冲区耗尽时才被真正调用,从而减少了系统调用次数。
6. 实战示例
下面通过一个完整的示例演示如何使用 BufferedInputStream 读取文件内容:
java
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class BufferedInputStreamDemo {
public static void main(String[] args) {
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("example.txt"))) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, bytesRead));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
上述代码使用 try-with-resources 语法自动关闭流,通过 1024 字节的数组批量读取文件内容,既保证了性能,又避免了内存浪费。
7. 性能对比
为了直观感受缓冲带来的性能提升,可以将普通 FileInputStream 逐字节读取与 BufferedInputStream 逐字节读取进行对比。在读取大文件时,BufferedInputStream 通常比直接逐字节读取快数倍甚至数十倍,因为后者每次 read() 都会触发一次系统调用。
8. 注意事项
- 缓冲区大小:默认 8192 字节通常已足够,频繁读取大文件时可适当调大缓冲区,但不宜过大,以免浪费内存。
- 资源关闭 :关闭
BufferedInputStream时,其内部的底层流也会被自动关闭,无需重复关闭。 - mark 与 reset :
mark(int readlimit)中的参数表示在标记失效前最多允许读取的字节数,超过该值后 reset 可能失败。 - 线程安全 :
BufferedInputStream不是线程安全的,多线程环境下需要自行同步。
9. 总结
BufferedInputStream 是 Java I/O 中提升读取性能的利器,它通过内存缓冲减少系统调用,适用于文件读取、网络流处理等场景。掌握其原理和常用方法,能够帮助开发者写出更高效的 I/O 代码。在实际开发中,建议结合 try-with-resources 使用,确保资源正确释放。