Java 文件 I/O 流

Java 文件 I/O 流

 次点击
17 分钟阅读

2025-8-21

IO 即 Input/Output,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。数据传输过程类似于水流,因此称为 IO 流。IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。

Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。

  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

1. I/O流分类

  • 字节流InputStream / OutputStream

  • 字符流Reader / Writer

  • 桥接InputStreamReader / OutputStreamWriter(需指定编码)

2. 常用流

  • 缓冲流BufferedInputStream/BufferedReader(提高性能)

  • 数据流DataInputStream/DataOutputStream(读写基本类型)

  • 对象流ObjectInputStream/ObjectOutputStream(序列化)

  • 打印流PrintWriter(支持 println/printf

  • 随机访问RandomAccessFile

3. Java文件 API

  • 旧版File(路径操作有限)

  • 新版Path/Files(推荐)

    • Files.exists/size/copy/move/delete

    • Files.readString/writeString(Java 11+)

    • Files.lines(流式逐行读取)

    • Files.list/walk/find(遍历)

4. 代码示例

逐行读取文本

try (BufferedReader br = Files.newBufferedReader(
        Path.of("a.txt"), StandardCharsets.UTF_8)) {
    for (String line; (line = br.readLine()) != null; ) {
        System.out.println(line);
    }
}

文本写入

try (BufferedWriter bw = Files.newBufferedWriter(
        Path.of("out.txt"), StandardCharsets.UTF_8)) {
    bw.write("Hello\n");
}

文件复制(字节流)

try (InputStream in = new BufferedInputStream(Files.newInputStream(Path.of("a.bin")));
     OutputStream out = new BufferedOutputStream(Files.newOutputStream(Path.of("b.bin")))) {
    byte[] buf = new byte[8192];
    int n;
    while ((n = in.read(buf)) != -1) out.write(buf, 0, n);
}

大文件复制(高效)

try (FileChannel in = FileChannel.open(Path.of("a.iso"), StandardOpenOption.READ);
     FileChannel out = FileChannel.open(Path.of("b.iso"),
        StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
    in.transferTo(0, in.size(), out);
}

对象序列化

try (ObjectOutputStream oos = new ObjectOutputStream(
        Files.newOutputStream(Path.of("obj.ser")))) {
    oos.writeObject(obj);
}
try (ObjectInputStream ois = new ObjectInputStream(
        Files.newInputStream(Path.of("obj.ser")))) {
    Object o = ois.readObject();
}

5. NIO 特性

  • FileChanneltransferTo/transferFrom(零拷贝)

  • MappedByteBuffer:内存映射文件,支持随机访问

  • Scatter/Gather:分散读/聚集写

  • AsynchronousFileChannel:异步文件 I/O

  • WatchService:目录监听

6. 文件属性

BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class);
Files.isHidden(path);
Files.size(path);

7. 随机访问与文件锁

RandomAccessFile raf = new RandomAccessFile("data.bin", "rw");
raf.seek(1024);
int v = raf.readInt();
raf.writeLong(123L);

try (FileChannel ch = FileChannel.open(Path.of("data.bin"), StandardOpenOption.WRITE);
     FileLock lock = ch.lock()) {
    // 文件锁
}

补充解释

1. 文本流必须显式指定编码

  • 默认编码依赖操作系统(Windows 可能是 GBK,Linux 常见 UTF-8)。

  • 如果不指定,跨平台读写时可能出现乱码。

  • 建议始终显式传入 StandardCharsets.UTF_8

new BufferedReader(new InputStreamReader(
    new FileInputStream("a.txt"), StandardCharsets.UTF_8));

2. 大文件不要使用 readAllLines / readAllBytes

  • Files.readAllLines(path) 会一次性把文件所有内容读入内存。

  • 对几百 MB 或更大文件可能导致 内存溢出

  • 替代:逐行/分块读取。

try (Stream<String> lines = Files.lines(path)) {
    lines.forEach(System.out::println);
}

3. available() 不能当作文件大小

  • InputStream.available() 只保证返回“当前不阻塞情况下可读的字节数”。

  • 并不等于文件剩余长度,也可能比实际可读的少。

  • 获取文件大小应使用:

long size = Files.size(path);

4. flush() vs close()

  • flush():立即把缓冲区数据写入目标(文件/网络)。

  • close():关闭流前会自动调用一次 flush()

  • 场景:写日志、网络通信时,必须及时写出,而不是等缓冲区满。


5. try-with-resources

  • Java 7+ 语法,确保流在 作用域结束时自动关闭,无需手动 finally

  • 自动调用 close(),即使中途抛出异常也能安全关闭。

try (BufferedWriter bw = Files.newBufferedWriter(path)) {
    bw.write("Hello");
} // 这里自动 close()

© 本文著作权归作者所有,未经许可不得转载使用。