杰瑞科技汇

Java的FileInputStream如何高效读取文件?

FileInputStream 是 Java I/O (输入/输出) 库中一个非常核心的类,它属于 java.io 包,它的主要作用是从文件系统中的某个文件中读取原始字节流 (raw byte stream)

Java的FileInputStream如何高效读取文件?-图1
(图片来源网络,侵删)

你可以把它想象成一个连接你的 Java 程序和硬盘上文件的“水管”,数据(字节)就像水一样,通过这个水管从文件流向你的程序。


核心概念

  • 字节流FileInputStream 是一个字节输入流 (InputStream),这意味着它一次读取一个字节(8位),而不是像 FileReader 那样直接读取字符,这使得它非常适合处理所有类型的文件,包括:
    • 文本文件(如 .txt, .csv, .json
    • 图片文件(如 .jpg, .png, .gif
    • 音频/视频文件(如 .mp3, .mp4
    • 可执行文件(.exe
    • 以及任何其他二进制文件。
  • 低级操作FileInputStream 提供的是最底层的文件读取功能,它只负责“读”,不负责将字节解码成字符,在处理文本文件时,通常建议使用 FileReader(它内部会使用字符集将字节解码为字符)或者更高级的 ScannerBufferedReader

主要构造方法

创建 FileInputStream 对象通常有几种方式,最常用的是通过 File 对象或文件路径字符串。

1 构造方法

  1. FileInputStream(String name)

    • 参数name 是文件的路径字符串。
    • 示例
      FileInputStream fis = new FileInputStream("C:\\data\\input.txt");
    • 注意:如果文件不存在,会立即抛出 FileNotFoundException
  2. FileInputStream(File file)

    Java的FileInputStream如何高效读取文件?-图2
    (图片来源网络,侵删)
    • 参数file 是一个 java.io.File 对象。
    • 示例
      File file = new File("data/input.txt");
      FileInputStream fis = new FileInputStream(file);
    • 注意:同样,如果文件不存在,会立即抛出 FileNotFoundException

主要方法

FileInputStream 继承自 InputStream 类,并重写了其中的核心读取方法。

方法 描述
int read() 读取一个字节的数据,并返回一个 0255 之间的 int 值,如果到达文件末尾,则返回 -1
int read(byte[] b) 最多读取 b.length 个字节的数据到字节数组 b 中,返回实际读取的字节数,如果到达文件末尾,则返回 -1这是最常用的方法。
int read(byte[] b, int off, int len) 最多读取 len 个字节的数据到字节数组 b 中,从 off 位置开始存放,返回实际读取的字节数。
void close() 关闭此文件输入流并释放与该流关联的所有系统资源。非常重要!
long skip(long n) 跳过输入流中的 n 个字节。

使用示例

下面通过几个例子来展示 FileInputStream 的具体用法。

示例 1:基本用法 - 逐字节读取

这个例子演示了如何使用 read() 方法一个字节一个字节地读取文件。注意:这种方法效率很低,不推荐用于大文件。

import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamExample {
    public static void main(String[] args) {
        // 使用 try-with-resources 语句,可以自动关闭流,避免资源泄漏
        try (FileInputStream fis = new FileInputStream("example.txt")) {
            int byteData;
            System.out.println("开始读取文件 (逐字节):");
            // read() 方法返回一个字节,如果到达文件末尾,返回 -1
            while ((byteData = fis.read()) != -1) {
                // 将 int 类型的字节转换为 char 并打印
                // 注意:这仅适用于单字节编码的文本文件,如 ASCII
                System.out.print((char) byteData);
            }
            System.out.println("\n文件读取完毕。");
        } catch (IOException e) {
            System.err.println("发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

示例 2:高效用法 - 使用缓冲区(推荐)

这个例子使用 read(byte[] b) 方法,将数据读取到一个字节数组(缓冲区)中,这是处理文件的标准且高效的方式。

Java的FileInputStream如何高效读取文件?-图3
(图片来源网络,侵删)
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamBufferedExample {
    public static void main(String[] args) {
        // 定义一个缓冲区,大小为 1024 字节
        byte[] buffer = new byte[1024];
        try (FileInputStream fis = new FileInputStream("example.txt")) {
            int bytesRead;
            System.out.println("开始读取文件 (使用缓冲区):");
            // read(buffer) 会尝试将数据填满 buffer,并返回实际读取的字节数
            while ((bytesRead = fis.read(buffer)) != -1) {
                // 将读取到的字节转换为字符串并打印
                // new String(buffer, 0, bytesRead) 只转换实际读取的部分
                System.out.write(buffer, 0, bytesRead);
            }
            System.out.println("\n文件读取完毕。");
        } catch (IOException e) {
            System.err.println("发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

示例 3:处理二进制文件(如图片)

FileInputStream 的真正威力在于处理非文本文件,下面的例子演示了如何读取一个图片文件。

import java.io.FileInputStream;
import java.io.IOException;
public class ReadBinaryFileExample {
    public static void main(String[] args) {
        // 假设有一个名为 "image.jpg" 的文件
        String filePath = "image.jpg";
        try (FileInputStream fis = new FileInputStream(filePath)) {
            byte[] imageBuffer = new byte[1024];
            int totalBytesRead = 0;
            int bytesRead;
            System.out.println("开始读取二进制文件...");
            while ((bytesRead = fis.read(imageBuffer)) != -1) {
                totalBytesRead += bytesRead;
                // 在实际应用中,这里会将读取到的字节数据写入另一个输出流
                // 用于网络传输或复制文件
            }
            System.out.println("文件读取完成,总共读取了 " + totalBytesRead + " 个字节。");
        } catch (IOException e) {
            System.err.println("读取二进制文件时出错: " + e.getMessage());
        }
    }
}

最佳实践与重要注意事项

1 使用 try-with-resources (Java 7+)

这是最重要的一点。FileInputStream 是一个需要系统资源(文件句柄)的对象,如果在使用后没有正确关闭,可能会导致资源泄漏,使文件无法被其他程序访问,甚至耗尽系统资源。

try-with-resources 语句可以确保无论 try 块是否正常执行,流都会在语句结束时自动关闭。

// 推荐:自动关闭
try (FileInputStream fis = new FileInputStream("file.txt")) {
    // ... 读取操作 ...
} catch (IOException e) {
    // ... 处理异常 ...
}
// 不推荐:需要手动关闭,容易出错
FileInputStream fis = null;
try {
    fis = new FileInputStream("file.txt");
    // ... 读取操作 ...
} catch (IOException e) {
    // ... 处理异常 ...
} finally {
    if (fis != null) {
        try {
            fis.close(); // 必须在 finally 块中关闭
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2 异常处理

FileInputStream 的构造方法和 read() 方法都可能抛出 IOException(及其子类 FileNotFoundException),你必须使用 try-catch 块来捕获这些异常,或者使用 throws 关键字将异常声明抛给上层调用者处理。

3 FileInputStream vs. FileReader

特性 FileInputStream FileReader
数据类型 字节流 字符流
读取单位 字节 字符
适用场景 所有文件(二进制、文本) 文本文件
编码处理 无,直接读取原始字节 有,可以指定字符集(如 UTF-8)将字节解码为字符
继承关系 InputStream Reader
  • 如果要处理图片、音频、视频或任何非文本文件
分享:
扫描分享到社交APP
上一篇
下一篇