小问题7 | Java序列化的应用场景、序列化方法及代码示例
前置问题:小问题6 | Java中如何实现深拷贝?实现深拷贝的三种方法
Java序列化是指把Java对象转换为字节序列的过程。
Java序列化的应用场景
Java中的序列化主要应用于以下场景:
- 对象状态持久化 - 将对象保存到文件或数据库中
- 网络传输优化 - 通过序列化减少网络传输数据量
- RPC等的应用基础 - 远程过程调用需要以流的形式处理请求和响应
下面介绍Java序列化的方法:
Java序列化的方法
1. Serializable接口
一个类的对象要想序列化,必须先实现Serializable接口:Serializable接口只是一个标识,不需要实现任何方法。
2. transient关键字
transient关键字可以防止个别敏感字段被序列化,字段加了transient后就不会被序列化。
3. Externalizable接口
通过实现Externalizable接口可以自定义序列化逻辑:
在writeExternal方法中可以只序列化需要的字段,自定义序列化逻辑。
在readExternal方法中可以根据写入顺序读取数据,重建对象。
这样通过Externalizable接口实现的自定义序列化可以只序列化必要字段,提高效率。并可以在序列化与反序列化中加入自定义处理逻辑。
注意,static
变量因为不属于任何对象(Object),所以无论有没有 transient
关键字修饰,均不会被序列化。
下面是一个代码示例:
public class Person implements Externalizable {
public Person(){
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private String name;
private int age;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("开始执行自定义的序列化方法:writeExternal");
out.writeUTF(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("开始执行自定义的序列化方法:readExternal");
this.name = in.readUTF();
this.age = in.readInt();
}
}
Java序列化的应用示例
下面的代码示例通过将对象保存到文件中并读取,演示Java序列化将对象持久化到文件系统中的作用,其中的Person类使用上文实现Externalizable的类:
public static void main(String[] args) {
try {
// 对象输出流
ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("E:\\test序列化.txt")));
// 对象
Person person = new Person();
person.setName("张三");
person.setAge(20);
// 序列化对象
out.writeObject(person);
// 关闭流
out.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("E:\\test序列化.txt")));
Person p2 = (Person) ois.readObject();
ois.close();
// 验证反序列化成功
System.out.println(p2.getName());
System.out.println(p2.getAge());
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
执行结果:
然而,序列化存在一些局限性,我们在实际应用中,不推荐使用JDK自带的序列化。
为什么不推荐使用 JDK 自带的序列化?
我们很少或者说几乎不会直接使用 JDK 自带的序列化方式,主要原因有下面这些原因:
- 不支持跨语言调用 : 如果调用的是其他语言开发的服务的时候就不支持了。
- 性能差:相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。并且Java序列化需要将对象转换为字节流的形式,序列化和反序列化时需要进行大量的IO操作,效率较低。
- 存在安全问题:序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。
- 版本兼容性问题:由于Java序列化序列化的是对象状态,而不是对象结构,因此当对象类定义发生变化时,可能会导致反序列化失败或出现意外的结果。
是的,有朋友可能产生新的问题,既然JDK自带的序列化是不被推荐的,那么——
除了JDK序列化以外,有哪些序列化框架?该如何选型?
我们带着问题继续看。
————————————————————
本专栏是【小问题】系列,旨在每篇解决一个小问题,并秉持着刨根问底的态度解决这个问题可能带出的一系列问题。