Java I/O 流(Input/Output Stream)

Java I/O 流(Input/Output Stream)全面详解

一、I/O 流概述

I/O 流(Input/Output Stream)是 Java 中用于处理输入/输出的核心机制,提供了读写数据(字节或字符)的统一抽象。

1.1 核心概念

  • 流(Stream):数据的有序序列,按先进先出方式传输
  • 方向性
    • 输入流(Input):从数据源读取数据到程序
    • 输出流(Output):从程序写入数据到目的地
  • 数据单位
    • 字节流(Byte Stream):以字节(8bit)为单位
    • 字符流(Character Stream):以字符(16bit Unicode)为单位

1.2 主要分类体系

Java I/O
├── 字节流(Byte Stream)
│   ├── InputStream(抽象类)
│   └── OutputStream(抽象类)
└── 字符流(Character Stream)
    ├── Reader(抽象类)
    └── Writer(抽象类)

二、字节流(Byte Stream)

2.1 InputStream 体系

java.io.InputStream
├── FileInputStream(文件输入)
├── ByteArrayInputStream(字节数组输入)
├── PipedInputStream(管道输入)
├── FilterInputStream(装饰器父类)
│   ├── BufferedInputStream(缓冲输入)
│   ├── DataInputStream(基本数据类型输入)
│   └── PushbackInputStream(回退输入)
├── ObjectInputStream(对象反序列化)
└── SequenceInputStream(合并多个流)
常用方法
int read() // 读取单个字节(0-255),到达末尾返回-1
int read(byte[] b) // 读取到字节数组,返回实际读取字节数
int read(byte[] b, int off, int len) // 读取到数组指定位置
long skip(long n) // 跳过n个字节
int available() // 返回可读取的估计字节数
void close() // 关闭流
void mark(int readlimit) // 标记当前位置
void reset() // 返回到标记位置
boolean markSupported() // 是否支持标记
示例:文件读取
try (InputStream is = new FileInputStream("test.txt")) {
    
    
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
    
    
        System.out.write(buffer, 0, bytesRead);
    }
} catch (IOException e) {
    
    
    e.printStackTrace();
}

2.2 OutputStream 体系

java.io.OutputStream
├── FileOutputStream(文件输出)
├── ByteArrayOutputStream(字节数组输出)
├── PipedOutputStream(管道输出)
├── FilterOutputStream(装饰器父类)
│   ├── BufferedOutputStream(缓冲输出)
│   ├── DataOutputStream(基本数据类型输出)
│   └── PrintStream(格式化输出)
└── ObjectOutputStream(对象序列化)
常用方法
void write(int b) // 写入单个字节(低8位)
void write(byte[] b) // 写入整个字节数组
void write(byte[] b, int off, int len) // 写入数组指定部分
void flush() // 强制刷新缓冲区
void close() // 关闭流
示例:文件写入
try (OutputStream os = new FileOutputStream("output.txt")) {
    
    
    String content = "Hello, Java IO!";
    os.write(content.getBytes(StandardCharsets.UTF_8));
    os.flush(); // 确保数据写入磁盘
} catch (IOException e) {
    
    
    e.printStackTrace();
}

三、字符流(Character Stream)

3.1 Reader 体系

java.io.Reader
├── InputStreamReader(字节到字符转换)
│   └── FileReader(文件字符输入)
├── BufferedReader(缓冲字符输入)
├── CharArrayReader(字符数组输入)
├── FilterReader(装饰器父类)
│   └── PushbackReader(回退字符)
├── PipedReader(管道字符输入)
└── StringReader(字符串输入)
常用方法
int read() // 读取单个字符(0-65535),末尾返回-1
int read(char[] cbuf) // 读取到字符数组
int read(char[] cbuf, int off, int len) // 读取到数组指定位置
long skip(long n) // 跳过n个字符
boolean ready() // 是否准备好读取
void close() // 关闭流
示例:缓冲读取文件
try (Reader reader = new BufferedReader(new FileReader("text.txt"))) {
    
    
    char[] buffer = new char[1024];
    int charsRead;
    while ((charsRead = reader.read(buffer)) != -1) {
    
    
        System.out.println(new String(buffer, 0, charsRead));
    }
} catch (IOException e) {
    
    
    e.printStackTrace();
}

3.2 Writer 体系

java.io.Writer
├── OutputStreamWriter(字符到字节转换)
│   └── FileWriter(文件字符输出)
├── BufferedWriter(缓冲字符输出)
├── CharArrayWriter(字符数组输出)
├── FilterWriter(装饰器父类)
├── PipedWriter(管道字符输出)
├── PrintWriter(格式化字符输出)
└── StringWriter(字符串输出)
常用方法
void write(int c) // 写入单个字符
void write(char[] cbuf) // 写入字符数组
void write(char[] cbuf, int off, int len) // 写入数组指定部分
void write(String str) // 写入字符串
void write(String str, int off, int len) // 写入字符串部分
void flush() // 强制刷新缓冲区
void close() // 关闭流
示例:缓冲写入文件
try (Writer writer = new BufferedWriter(new FileWriter("output.txt"))) {
    
    
    writer.write("第一行内容\n");
    writer.write("第二行内容");
    writer.flush(); // 确保数据写入
} catch (IOException e) {
    
    
    e.printStackTrace();
}

四、装饰器模式在I/O中的应用

Java I/O 使用装饰器模式动态扩展功能:

4.1 典型装饰器组合

// 带缓冲的UTF-8文件读取
InputStreamReader isr = new InputStreamReader(
    new BufferedInputStream(
        new FileInputStream("data.txt")),
    StandardCharsets.UTF_8);

// 带缓冲的对象序列化输出
ObjectOutputStream oos = new ObjectOutputStream(
    new BufferedOutputStream(
        new FileOutputStream("data.obj")));

4.2 常用装饰器

  • 缓冲处理:BufferedInputStream/BufferedOutputStream
  • 基本数据类型:DataInputStream/DataOutputStream
  • 对象序列化:ObjectInputStream/ObjectOutputStream
  • 字符编码转换:InputStreamReader/OutputStreamWriter

五、高级I/O操作

5.1 NIO(New I/O)快速通道

// 文件复制示例(Java 7+)
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");
try {
    
    
    Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
    
    
    e.printStackTrace();
}

// 使用Files类快捷方法
List<String> lines = Files.readAllLines(Paths.get("data.txt"));
Files.write(Paths.get("output.txt"), lines, StandardOpenOption.CREATE);

5.2 随机访问文件

try (RandomAccessFile raf = new RandomAccessFile("data.dat", "rw")) {
    
    
    raf.seek(100); // 定位到第100字节
    byte[] buffer = new byte[50];
    raf.read(buffer); // 随机读取
    raf.write("New Data".getBytes()); // 随机写入
} catch (IOException e) {
    
    
    e.printStackTrace();
}

六、字符编码处理

6.1 常见编码

  • UTF-8(推荐):变长编码,兼容ASCII
  • GBK:中文编码
  • ISO-8859-1:Latin-1

6.2 编码转换示例

// 读取GBK编码文件
try (Reader reader = new InputStreamReader(
        new FileInputStream("gbk.txt"), "GBK")) {
    
    
    // 读取操作...
}

// 写入UTF-8文件
try (Writer writer = new OutputStreamWriter(
        new FileOutputStream("utf8.txt"), 
        StandardCharsets.UTF_8)) {
    
    
    writer.write("UTF-8内容");
}

七、最佳实践

  1. 始终关闭流:使用try-with-resources
  2. 合理使用缓冲:特别是文件操作
  3. 注意字符编码:明确指定编码(避免平台默认)
  4. 大文件处理:分块读取避免内存溢出
  5. NIO替代方案:Java 7+推荐使用NIO API

八、常见问题解决方案

8.1 文件复制高效方法

// 使用缓冲的字节流
try (InputStream is = new BufferedInputStream(new FileInputStream("src"));
     OutputStream os = new BufferedOutputStream(new FileOutputStream("dest"))) {
    
    
    byte[] buffer = new byte[8192]; // 8KB缓冲区
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
    
    
        os.write(buffer, 0, bytesRead);
    }
}

8.2 读取大文件行处理

try (BufferedReader br = new BufferedReader(new FileReader("large.txt"))) {
    
    
    String line;
    while ((line = br.readLine()) != null) {
    
    
        // 处理每一行
    }
}

8.3 对象序列化注意事项

// 实现Serializable接口
class User implements Serializable {
    
    
    private static final long serialVersionUID = 1L;
    // 瞬态字段不会被序列化
    private transient String password;
}

// 序列化/反序列化
try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("user.obj"))) {
    
    
    oos.writeObject(new User("Alice"));
}

try (ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("user.obj"))) {
    
    
    User user = (User) ois.readObject();
}

九、Java I/O 发展

  1. 传统I/O(Java 1.0):基于流的同步阻塞模型
  2. NIO(Java 1.4):引入通道(Channel)和缓冲区(Buffer)
  3. NIO.2(Java 7):Path/Files等工具类,异步I/O支持

掌握Java I/O体系是处理各种数据输入输出的基础,理解其设计模式和不同场景下的最佳实践对开发高效可靠的应用程序至关重要。