Java I/O 流深度解析:数据流动的艺术

Java I/O(输入/输出)流是 Java 处理数据输入输出的核心机制,提供了统一的方式来处理各种数据源(文件、网络、内存等)的读写操作。I/O 流体系庞大而完善,是 Java 开发者必须掌握的重要技能。

一、I/O 流核心体系

1. 流分类体系

2. 核心区别

​特性​ ​字节流​ ​字符流​
​数据单位​ 字节(8 bit) 字符(16 bit Unicode)
​处理类型​ 二进制数据(图片、视频等) 文本数据
​基类​ InputStream/OutputStream Reader/Writer
​编码处理​ 无编码转换 自动处理字符编码
​主要实现​ FileInputStream/FileOutputStream FileReader/FileWriter

二、字节流详解

1. 文件字节流

// 文件复制(字节流)
try (InputStream in = new FileInputStream("source.jpg");
     OutputStream out = new FileOutputStream("copy.jpg")) {
    
    byte[] buffer = new byte[8192]; // 8KB缓冲区
    int bytesRead;
    while ((bytesRead = in.read(buffer)) != -1) {
        out.write(buffer, 0, bytesRead);
    }
} catch (IOException e) {
    e.printStackTrace();
}

2. 缓冲字节流

// 使用缓冲流提高性能
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("largefile.bin"));
     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.bin"))) {
    
    int data;
    while ((data = bis.read()) != -1) {
        bos.write(data);
    }
}

3. 数据字节流

// 读写基本数据类型
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.bin"));
     DataInputStream dis = new DataInputStream(new FileInputStream("data.bin"))) {
    
    // 写入
    dos.writeInt(42);
    dos.writeDouble(3.14);
    dos.writeUTF("你好");
    
    // 读取(顺序必须一致)
    int i = dis.readInt();
    double d = dis.readDouble();
    String s = dis.readUTF();
}

4. 对象序列化流

class Person implements Serializable {
    private String name;
    private int age;
    // 构造方法、getter/setter
}

// 序列化对象
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
    oos.writeObject(new Person("Alice", 25));
}

// 反序列化对象
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
    Person p = (Person) ois.readObject();
    System.out.println(p.getName()); // Alice
}

三、字符流详解

1. 文件字符流

// 文本文件复制
try (Reader reader = new FileReader("source.txt");
     Writer writer = new FileWriter("copy.txt")) {
    
    char[] buffer = new char[4096];
    int charsRead;
    while ((charsRead = reader.read(buffer)) != -1) {
        writer.write(buffer, 0, charsRead);
    }
}

2. 缓冲字符流

// 使用缓冲流按行读取
try (BufferedReader br = new BufferedReader(new FileReader("text.txt"));
     BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
    
    String line;
    while ((line = br.readLine()) != null) {
        // 处理行数据
        bw.write(line.toUpperCase());
        bw.newLine(); // 写入换行符
    }
}

3. 转换流(字节流←→字符流)

// 字节流转换为字符流(指定编码)
try (InputStream is = new FileInputStream("utf8.txt");
     Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8);
     BufferedReader br = new BufferedReader(reader)) {
    
    // 读取UTF-8编码文件
    String content = br.readLine();
}

// 字符流转换为字节流
try (OutputStream os = new FileOutputStream("gbk.txt");
     Writer writer = new OutputStreamWriter(os, "GBK");
     BufferedWriter bw = new BufferedWriter(writer)) {
    
    // 写入GBK编码文件
    bw.write("中文内容");
}

四、NIO(New I/O)框架

1. 核心组件

  • ​Buffer​​:数据容器
  • ​Channel​​:数据传输通道
  • ​Selector​​:多路复用选择器

2. 文件NIO操作

// 文件复制(NIO)
try (FileChannel source = FileChannel.open(Paths.get("source.txt"), StandardOpenOption.READ);
     FileChannel dest = FileChannel.open(Paths.get("copy.txt"), 
          StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
    
    // 高效传输(零拷贝)
    source.transferTo(0, source.size(), dest);
    
    // 或使用缓冲区
    ByteBuffer buffer = ByteBuffer.allocateDirect(8192); // 直接缓冲区
    while (source.read(buffer) != -1) {
        buffer.flip(); // 切换为读模式
        dest.write(buffer);
        buffer.clear(); // 清空缓冲区
    }
}

3. 内存映射文件

// 大文件随机访问
try (RandomAccessFile raf = new RandomAccessFile("largefile.dat", "rw");
     FileChannel channel = raf.getChannel()) {
    
    // 映射文件区域到内存
    MappedByteBuffer buffer = channel.map(
        FileChannel.MapMode.READ_WRITE, 0, channel.size());
    
    // 直接操作内存
    buffer.position(1000); // 定位到1000字节位置
    buffer.putInt(42); // 写入整数
    buffer.flip();
    int value = buffer.getInt(); // 读取整数
}

五、Java 7+ 文件操作新特性

1. Paths 和 Files 工具类

Path path = Paths.get("data", "files", "test.txt"); // 跨平台路径

// 读取所有行
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);

// 写入文件
Files.write(path, "Hello World".getBytes(), StandardOpenOption.CREATE);

// 文件操作
boolean exists = Files.exists(path);
long size = Files.size(path);
Files.copy(source, target);
Files.move(source, target);
Files.deleteIfExists(path);

2. 遍历目录

// 遍历目录(Java 7+)
try (Stream<Path> paths = Files.walk(Paths.get("/projects"))) {
    paths.filter(Files::isRegularFile)
         .filter(p -> p.toString().endsWith(".java"))
         .forEach(System.out::println);
}

// 查找文件
try (Stream<Path> paths = Files.find(
        Paths.get("/projects"), 
        Integer.MAX_VALUE, 
        (path, attr) -> attr.isRegularFile() && path.toString().endsWith(".class"))) {
    paths.forEach(System.out::println);
}

六、I/O 最佳实践

1. 资源管理

// 使用try-with-resources自动关闭资源
try (InputStream in = new FileInputStream("source");
     OutputStream out = new FileOutputStream("target")) {
    // 使用资源
} // 自动调用close()

2. 缓冲优化

  • 总是使用缓冲流(BufferedInputStream/BufferedReader)
  • 根据场景调整缓冲区大小(默认8KB)
  • 大文件使用直接缓冲区(ByteBuffer.allocateDirect)

3. 字符编码处理

  • 明确指定字符编码(避免依赖平台默认编码)
  • 推荐使用UTF-8:StandardCharsets.UTF_8
  • 转换流处理编码:InputStreamReader/OutputStreamWriter

4. 性能选择

​场景​ ​推荐方案​
小文件读取 Files.readAllBytes()
大文件顺序读取 BufferedInputStream + 大缓冲区
大文件随机访问 RandomAccessFile 或内存映射
文本处理 BufferedReader + readLine()
二进制数据处理 DataInputStream/DataOutputStream
对象序列化 ObjectInputStream/ObjectOutputStream
高性能I/O NIO Channel + Buffer

七、常见问题解决方案

1. 文件乱码问题

// 明确指定编码
try (BufferedReader br = new BufferedReader(
        new InputStreamReader(
            new FileInputStream("gbk.txt"), "GBK"))) {
    // 读取GBK编码文件
}

2. 大文件处理

// 流式处理大文件
try (Stream<String> lines = Files.lines(Paths.get("hugefile.txt"))) {
    lines.filter(line -> line.contains("error"))
         .forEach(System.out::println);
}

3. 资源泄露防护

// 使用try-with-resources确保资源关闭
try (Connection conn = DriverManager.getConnection(url);
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery(sql)) {
    // 处理结果集
} // 自动关闭所有资源

八、高级应用场景

1. 网络文件传输

// 从URL读取数据
try (InputStream in = new URL("https://example.com/data.bin").openStream();
     OutputStream out = new FileOutputStream("local.bin")) {
    in.transferTo(out); // Java 9+
}

2. 多文件合并

// 合并多个文件
try (OutputStream out = new FileOutputStream("combined.bin")) {
    for (String file : files) {
        Files.copy(Paths.get(file), out);
    }
}

3. 对象深度克隆

// 通过序列化实现深度克隆
public static <T extends Serializable> T deepClone(T object) {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(baos)) {
        
        oos.writeObject(object);
        
        try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            return (T) ois.readObject();
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

九、I/O 性能优化

1. 基准测试对比

​操作​ ​传统I/O​ ​缓冲I/O​ ​NIO​
小文件读取(1KB) 0.1ms 0.05ms 0.08ms
大文件顺序读(100MB) 1200ms 450ms 350ms
大文件随机访问 极快
内存占用 高(映射)

2. 优化策略

  1. ​缓冲策略​​:使用BufferedXXX流,调整缓冲区大小
  2. ​批量操作​​:减少系统调用次数
  3. ​零拷贝技术​​:使用transferTo()/transferFrom()
  4. ​直接内存​​:使用DirectByteBuffer减少拷贝
  5. ​异步I/O​​:Java 7+的AsynchronousFileChannel

十、I/O 设计模式应用

1. 装饰器模式

// 多层装饰器
InputStream in = new BufferedInputStream(
                 new GZIPInputStream(
                 new FileInputStream("data.gz")));

2. 适配器模式

// 字节流适配为字符流
Reader reader = new InputStreamReader(inputStream, "UTF-8");

3. 工厂模式

// I/O流工厂
public class StreamFactory {
    public static BufferedReader createReader(Path path) {
        return Files.newBufferedReader(path, StandardCharsets.UTF_8);
    }
}

Java I/O 流体系提供了强大而灵活的数据处理能力。掌握各种流的特点、适用场景和最佳实践,能够帮助开发者高效处理各种数据源。在新时代开发中,应优先考虑 NIO 和 Files API,同时合理使用传统 I/O 流以满足特定需求。

更多推荐