在Java编程中,IO(输入/输出)流是处理数据输入输出的核心机制。无论是读取文件内容、网络通信还是对象持久化,IO流都扮演着至关重要的角色。本文将从四个维度深入解析Java IO流:方向、单位、功能划分和对象序列化。
一、按数据流向划分:输入流与输出流
输入流(Input Stream)
输入流负责从数据源读取数据到程序中,是数据的"消费者"。
java
// FileInputStream示例:从文件读取数据
try (FileInputStream fis = new FileInputStream("input.txt")) {
int data;
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
输出流(Output Stream)
输出流将程序中的数据写入到目标位置,是数据的"生产者"。
java
// FileOutputStream示例:向文件写入数据
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
String content = "Hello, Java IO!";
fos.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
二、按数据处理单位划分:字节流与字符流
字节流(Byte Stream)
以字节(8位)为单位进行数据传输,适合处理所有类型的数据。
核心类:
· InputStream / OutputStream(抽象基类)
· FileInputStream / FileOutputStream(文件字节流)
· BufferedInputStream / BufferedOutputStream(缓冲字节流)
java
// 使用缓冲字节流提高文件复制效率
try (FileInputStream fis = new FileInputStream("source.jpg");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("copy.jpg");
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
字符流(Character Stream)
以字符(16位Unicode)为单位进行数据传输,专门处理文本数据。
核心类:
· Reader / Writer(抽象基类)
· FileReader / FileWriter(文件字符流)
· BufferedReader / BufferedWriter(缓冲字符流)
· InputStreamReader / OutputStreamWriter(字节流到字符流的桥梁)
java
// 使用缓冲字符流读取文本文件
try (BufferedReader reader = new BufferedReader(new FileReader("text.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// 指定编码读取文件
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream("text.txt"), "UTF-8"))) {
// 读取操作
} catch (IOException e) {
e.printStackTrace();
}
三、按功能角色划分:节点流与处理流
节点流(Node Stream)
直接连接数据源,进行基础的数据读写操作。
常见节点流:
· FileInputStream / FileOutputStream
· FileReader / FileWriter
· ByteArrayInputStream / ByteArrayOutputStream
· StringReader / StringWriter
处理流(Processing Stream)
对现有流进行包装,提供增强功能(如缓冲、转换、合并等)。
常见处理流及其功能:
处理流 功能描述 示例
BufferedInputStream/Reader 添加缓冲功能,提高IO效率 new BufferedReader(new FileReader(...))
DataInputStream/DataOutputStream 读写基本数据类型 dataInputStream.readInt()
ObjectInputStream/ObjectOutputStream 对象序列化与反序列化 objectOutputStream.writeObject(obj)
PrintStream/PrintWriter 格式化输出 printWriter.printf()
SequenceInputStream 合并多个输入流 new SequenceInputStream(stream1, stream2)
java
// 使用DataStream处理基本数据类型
try (DataOutputStream dos = new DataOutputStream(
new FileOutputStream("data.bin"))) {
dos.writeInt(100);
dos.writeDouble(3.14);
dos.writeUTF("Java IO");
} catch (IOException e) {
e.printStackTrace();
}
四、对象序列化:将对象转换为字节流
序列化基础
对象序列化是将对象状态转换为字节流的过程,以便存储或传输。反序列化则是将字节流恢复为对象。
实现序列化的条件:
-
实现 Serializable 接口(标记接口)
-
所有成员变量必须是可序列化的
-
使用 transient 关键字标记不需要序列化的字段
java
// 可序列化的Person类
import java.io.Serializable;
public class Person implements Serializable {
// 序列化版本UID,用于版本兼容性
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient String password; // 不参与序列化
// 构造方法、getter/setter省略...
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
序列化与反序列化实践
java
import java.io.*;
public class SerializationDemo {
// 序列化对象到文件
public static void serializeObject(Object obj, String filename) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filename))) {
oos.writeObject(obj);
System.out.println("对象序列化成功: " + filename);
} catch (IOException e) {
e.printStackTrace();
}
}
// 从文件反序列化对象
public static Object deserializeObject(String filename) {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(filename))) {
Object obj = ois.readObject();
System.out.println("对象反序列化成功: " + filename);
return obj;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
// 创建Person对象
Person person = new Person();
person.setName("张三");
person.setAge(25);
// 序列化对象
serializeObject(person, "person.ser");
// 反序列化对象
Person restoredPerson = (Person) deserializeObject("person.ser");
if (restoredPerson != null) {
System.out.println("恢复的对象: " + restoredPerson);
// 注意:password字段为null,因为它被标记为transient
}
}
}
序列化高级特性
自定义序列化
通过实现 writeObject() 和 readObject() 方法控制序列化过程:
java
public class CustomSerialization implements Serializable {
private String data;
private transient String sensitiveData;
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // 默认序列化
// 对敏感数据加密后序列化
String encrypted = encrypt(sensitiveData);
oos.writeObject(encrypted);
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // 默认反序列化
// 解密敏感数据
String encrypted = (String) ois.readObject();
sensitiveData = decrypt(encrypted);
}
private String encrypt(String data) { /* 加密逻辑 */ }
private String decrypt(String data) { /* 解密逻辑 */ }
}
序列化版本控制
serialVersionUID 确保序列化兼容性:
java
public class VersionedClass implements Serializable {
// 显式声明serialVersionUID
private static final long serialVersionUID = 123456789L;
// 添加新字段时,保持serialVersionUID不变可实现向后兼容
private String newField; // 新增字段
// 删除字段时,反序列化时该字段值将为null或默认值
}
五、IO流的最佳实践
- 使用try-with-resources自动关闭资源
java
// Java 7+ 自动资源管理
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
// 使用资源
} catch (IOException e) {
// 异常处理
} // 资源自动关闭
- 合理选择缓冲流提高性能
java
// 大文件处理使用缓冲
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("largefile.bin"), 8192)) {
// 8192字节缓冲区
}
- 注意字符编码问题
java
// 明确指定字符编码
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream("file.txt"), StandardCharsets.UTF_8))) {
// 读取操作
}
- 对象序列化的安全考虑
· 对敏感字段使用 transient 关键字
· 考虑实现自定义序列化逻辑
· 验证反序列化数据的合法性
六、NIO与传统IO的对比
特性 传统IO NIO (New IO)
数据处理方式 流式处理 块/缓冲区处理
阻塞模式 阻塞IO 支持非阻塞IO
多路复用 不支持 支持Selector多路复用
适用场景 连接数较少,数据传输量小 高并发连接,大数据量传输
总结与对比
输入输出流核心类对比
类别 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
文件操作 FileInputStream FileOutputStream FileReader FileWriter
缓冲操作 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
内存操作 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
数据类型 DataInputStream DataOutputStream - -
对象操作 ObjectInputStream ObjectOutputStream - -
转换流 - - InputStreamReader OutputStreamWriter
打印流 - PrintStream - PrintWriter
选择指南
- 文件类型决定流类型:
· 文本文件:优先使用字符流(FileReader/FileWriter)
· 二进制文件:使用字节流(FileInputStream/FileOutputStream)
- 性能考虑:
· 频繁读写:使用缓冲流包装
· 大文件:使用缓冲流 + 合适大小的缓冲区
- 功能需求:
· 读写基本类型:使用DataInputStream/DataOutputStream
· 对象持久化:使用ObjectInputStream/ObjectOutputStream
· 格式化输出:使用PrintStream/PrintWriter
结论
Java IO流是一个完整而强大的体系,理解其层次结构和设计原则是编写高效IO代码的关键。从基础的FileInputStream/FileOutputStream到高级的处理流组合,Java提供了灵活的工具集来应对各种数据输入输出需求。掌握这些知识不仅有助于日常开发,也是深入理解Java IO模型的基础。
记住最佳实践:始终使用try-with-resources确保资源正确关闭,根据场景选择合适的流类型,处理文本时注意字符编码,处理大文件时使用缓冲机制。随着对NIO和NIO.2的学习,你将能构建更加高效和可扩展的IO应用程序。