FileInputStream:直连磁盘的「原始接口」
磁盘上存的是什么?是字节。图片、视频、PDF、压缩包——不管什么文件,底层都是一串字节。
FileInputStream 就是 Java 读写这些字节的「原始接口」,直接连通文件系统,不做任何加工。
三种构造方式
java
// 方式 1:字符串路径(最直接)
FileInputStream fis = new FileInputStream("data.bin");
// 方式 2:File 对象(可以先检查)
File file = new File("data.bin");
if (file.exists() && file.isFile()) {
FileInputStream fis = new FileInputStream(file);
}
// 方式 3:FileDescriptor(标准输入)
FileInputStream fis = new FileInputStream(FileDescriptor.in);方式 2 适合需要先判断文件存在性的场景,但注意:FileInputStream 构造函数本身也会在文件不存在时抛 FileNotFoundException,所以检查和构造要放一起。
读取方式:按需选择
单字节读取(慢,测试用)
java
try (FileInputStream fis = new FileInputStream("data.txt")) {
int b;
while ((b = fis.read()) != -1) {
System.out.print((char) b);
}
}每次 read() 返回一个字节(0~255)或 -1(流结束)。读 1MB 文件触发约 100 万次系统调用,仅适合小文件或测试场景。
批量读取(推荐)
java
try (FileInputStream fis = new FileInputStream("data.txt")) {
byte[] buffer = new byte[8192];
int len;
while ((len = fis.read(buffer)) != -1) {
// 处理 buffer[0] ~ buffer[len-1]
System.out.write(buffer, 0, len); // 直接输出到控制台
}
}一次读 8KB,1MB 文件只需 128 次系统调用,这是标准用法。
预分配数组(适合小文件)
java
try (FileInputStream fis = new FileInputStream("small.dat")) {
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
// 一次性读完
}available() 返回「无需阻塞可读取的字节数估计值」,对小文件基本准确。但注意:大文件用它可能预分配过大内存。
完整示例:读取图片到字节数组
java
public byte[] readImageBytes(String path) throws IOException {
try (FileInputStream fis = new FileInputStream(path)) {
// 先读文件大小
int size = fis.available();
byte[] data = new byte[size];
fis.read(data);
return data;
}
}
public void processBinaryFile(String path) throws IOException {
try (FileInputStream fis = new FileInputStream(path)) {
byte[] buffer = new byte[8192];
int len;
while ((len = fis.read(buffer)) != -1) {
processBytes(buffer, len);
}
}
}配套使用:BufferedInputStream
FileInputStream 是节点流,每次 read() 都可能触发系统调用。加上缓冲层:
java
try (
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("big.dat"), 8192)
) {
byte[] buffer = new byte[8192];
while (bis.read(buffer) != -1) {
process(buffer);
}
}性能差异:1GB 文件从数分钟降到几秒。
mark() / reset():偷看与回退
FileInputStream 支持 mark/reset,可以在读取过程中标记位置,之后再回到那里:
java
try (FileInputStream fis = new FileInputStream("data.bin")) {
fis.mark(100); // 标记当前位置,后续最多回退 100 字节
fis.read(); fis.read(); fis.read(); // 读 3 字节
fis.reset(); // 回到标记位置
fis.read(); // 重新读第一个字节
}避坑指南
编码问题
FileInputStream 只管字节,不管字符。读中文文本:
java
// ❌ 错:返回的是 UTF-8 字节,不是字符
FileInputStream fis = new FileInputStream("utf8.txt");
int b = fis.read(); // 返回字节值,不是 Unicode 字符
// ✅ 对:用 InputStreamReader 指定编码
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("utf8.txt"), StandardCharsets.UTF_8));文件不存在
java
// ❌ 文件不存在直接抛异常
new FileInputStream("not-exist.txt");
// ✅ 先检查
File f = new File("not-exist.txt");
if (f.exists() && f.isFile()) {
fis = new FileInputStream(f);
}一句话总结:FileInputStream 是字节输入流的根基,但永远用它包装 BufferedInputStream,永远用批量读取而非单字节。
