Java IO 从入门到深入(第五篇):转换流 + 对象流 + 序列化详解(面试高频)
在前几篇中,我们已经掌握了:
- File 类
- 字节流
- 字符流
- 缓冲流
但还有两个非常关键的问题:
问题1:字符编码如何真正控制?
我们之前提到:
id="v1y2b3"
FileReader 无法指定编码
那如何解决?
👉 转换流
问题2:如何把对象写入文件?
例如:
id="x9z8y7"
User 对象 → 保存到磁盘
👉 对象流 + 序列化
本篇重点:
- 转换流原理与使用
- InputStreamReader / OutputStreamWriter
- 对象流 ObjectInputStream / ObjectOutputStream
- Serializable 接口
- transient 关键字
- serialVersionUID(重点)
- 常见易错点
- 面试高频问题
一、什么是转换流?
转换流的核心作用:
在字节流和字符流之间进行转换
结构:
id="k2m3n4"
字节流 ←→ 转换流 ←→ 字符流
二、为什么需要转换流?
问题:
id="a1s2d3"
字节流不处理编码
字符流无法指定编码
解决:
id="q4w5e6"
转换流 = 手动指定编码
三、InputStreamReader(重点)
作用:
将字节流 → 转换为字符流
使用示例
java
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
InputStreamReader reader =
new InputStreamReader(
new FileInputStream("test.txt"),
"UTF-8"
);
int data;
while((data = reader.read()) != -1){
System.out.print((char)data);
}
reader.close();
}
}
本质
id="m7n8b9"
字节 → 解码 → 字符
四、OutputStreamWriter
作用:
字符 → 转换为字节流写出
示例
java
OutputStreamWriter writer =
new OutputStreamWriter(
new FileOutputStream("test.txt"),
"UTF-8"
);
writer.write("你好,Java IO");
writer.close();
五、最佳实践(推荐写法)
java
try(
BufferedReader br =
new BufferedReader(
new InputStreamReader(
new FileInputStream("test.txt"), "UTF-8"
)
)
){
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
}
👉 这是:
id="z9x8c7"
企业级标准写法
六、什么是对象流?
对象流的作用:
将 Java 对象写入文件 / 从文件读取对象
核心类
id="q1w2e3"
ObjectOutputStream
ObjectInputStream
七、对象序列化
概念:
id="l1k2j3"
对象 → 字节流
八、Serializable 接口
要实现序列化,必须:
java
class User implements Serializable
示例
java
import java.io.*;
class User implements Serializable {
String name;
int age;
public User(String name,int age){
this.name = name;
this.age = age;
}
}
public class Main {
public static void main(String[] args) throws Exception {
ObjectOutputStream oos =
new ObjectOutputStream(
new FileOutputStream("user.dat")
);
User user = new User("Tom",18);
oos.writeObject(user);
oos.close();
}
}
九、反序列化
java
ObjectInputStream ois =
new ObjectInputStream(
new FileInputStream("user.dat")
);
User user = (User) ois.readObject();
System.out.println(user.name);
ois.close();
十、transient 关键字
作用:
不参与序列化
示例
java
class User implements Serializable {
String name;
transient String password;
}
结果:
id="x5c6v7"
password 不会被保存
十一、serialVersionUID(重点)
作用:
控制序列化版本
示例
java
class User implements Serializable {
private static final long serialVersionUID = 1L;
}
为什么需要?
问题:
id="b2n3m4"
类结构发生变化
例如:
id="k9l8j7"
新增字段
删除字段
如果没有 UID:
id="p0o9i8"
反序列化失败
异常:
id="y7t6r5"
InvalidClassException
十二、序列化本质(面试重点)
id="f1g2h3"
对象 → 字节数组 → 文件
反序列化:
id="u7y6t5"
文件 → 字节 → 对象
十三、使用场景
1 数据持久化
id="j1k2l3"
对象保存到磁盘
2 网络传输
id="r4t5y6"
对象通过 socket 传输
3 缓存系统
id="u8i9o0"
对象序列化存储
十四、常见易错点
1 没有实现 Serializable
id="e1r2t3"
NotSerializableException
2 忘记 serialVersionUID
id="y1u2i3"
版本不兼容
3 静态变量不会被序列化
id="o1p2a3"
static 不属于对象
4 transient 字段丢失
id="s4d5f6"
反序列化后为默认值
十五、面试高频问题(重点)
1 什么是序列化?
id="g7h8j9"
对象 → 字节流
2 为什么需要序列化?
id="k1l2z3"
存储
传输
3 transient 作用?
id="x1c2v3"
不序列化字段
4 serialVersionUID 有什么用?
id="b1n2m3"
版本控制
防止反序列化失败
5 为什么不推荐 Java 原生序列化?
原因:
id="q2w3e4"
性能低
不安全
不可读
跨语言差
十六、总结
本篇是 Java IO 的 进阶核心内容。
转换流
id="z1x1c1"
InputStreamReader
OutputStreamWriter
作用:
id="v2b2n2"
控制编码
解决乱码
对象流
id="m3n3b3"
ObjectInputStream
ObjectOutputStream
作用:
id="k4l4j4"
对象读写
序列化核心
id="h5g5f5"
Serializable
transient
serialVersionUID