JAVA 序列化和反序列化
1. 实现方式及侧重点
JAVA提供了两种接口实现方式,先看下相关接口结构图
其中,
1)实现Serializable接口的java类,默认会为内部所有字段(除去transient修饰属性和静态属性)实现序列化和反序列化处理逻辑,适用于序列化属性较多的情形,对于少量的需排除属性,可以使用transient修饰。
2)实现Externalizable接口的java类,需要显式实现writeExternal和readExternal方法,且序列化和反序列化的属性顺序需要一致,适用于序列化属性较少的情形。
2. Serializable方式实证
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student student = new Student("JEENO", "123456", "Software Engineering");
student.setBirth(LocalDateTime.now());
student.setMale(true);
String filePath = "D:/serializable-demo.text";
// 序列化 - Serializable方式
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath));
out.writeObject(student);
// 这里为了验证serializable序列化不包括静态变量。可以看到反序列后language变成了"Go"
Student.language = "Go";
// 反序列化 - Serializable
ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath));
Student stu1 = (Student) in.readObject();
System.out.println(stu1);
}
static class Student implements Serializable {
private String name;
/**
* transient 修饰变量,可以避开Serializable序列化
*/
private transient String password;
private String major;
private LocalDateTime birth;
private Boolean male;
/**
* 静态变量,不参与序列化
*/
public static String language = "JAVA";
public Student(){}
public Student(String name, String password, String major) {
this.name = name;
this.password = password;
this.major = major;
}
// 此处省略字段的getter、setter方法
...
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
", major='" + major + '\'' +
", birth=" + birth +
", male=" + male +
", language=" + language +
'}';
}
}
}
运行结果:
Student{name=‘JEENO’, password=‘null’, major=‘Software Engineering’, birth=2021-06-18T00:19:26.759, male=true, language=Go}
容易看到:
- transient 修饰字段不参与。student对象中password属性没有参与序列化,在反序列后对象stu1可看到其属性值为null。
- 静态属性不参与。 初始状态language为“JAVA”,将student序列化后、反序列化前,将language更新成了“Go”,反序列后对象stu1其属性值也为“Go”。说明在更新值后,即便经过反序列,其值仍未改变,仍旧是读取JVM中Student类的静态属性。
3. Externalizable方式实证
public class ExternalizableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student student = new Student("JEENO", "123456", "Software Engineering");
student.setBirth(LocalDateTime.now());
student.setMale(true);
String filePath = "D:/externalizable-demo.text";
// 序列化 - Externalizable方式
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath));
out.writeObject(student);
// 更新静态属性值
Student.language = "Go";
// 反序列化 - Externalizable
ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath));
Student stu1 = (Student) in.readObject();
System.out.println(stu1);
System.out.println("current Student's language : " + Student.language);
}
static class Student implements Externalizable {
private String name;
private transient String password;
private String major;
private LocalDateTime birth;
private Boolean male;
public static String language = "JAVA";
public Student() {
}
public Student(String name, String password, String major) {
this.name = name;
this.password = password;
this.major = major;
}
// 此处省略字段的getter、setter方法
...
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
", major='" + major + '\'' +
", birth=" + birth +
", male=" + male +
", language=" + language +
'}';
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.name);
out.writeObject(this.password);
out.writeObject(this.major);
out.writeObject(this.birth);
out.writeObject(this.male);
out.writeObject(language);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.name = (String) in.readObject();
this.password = (String) in.readObject();
this.major = (String) in.readObject();
this.birth = (LocalDateTime) in.readObject();
this.male = (Boolean) in.readObject();
language = (String) in.readObject();
}
}
}
运行结果:
Student{name=‘JEENO’, password=‘123456’, major=‘Software Engineering’, birth=2021-06-18T00:24:20.895, male=true, language=JAVA}
current Student’s language : JAVA
可以得到结论:
- 区别于Serializable接口实现,Externalizable实现需要实现两个方法,分别为writeExternal和readExternal。并且要显式地指定(序列化/反序列化)顺序;
- 不受transient关键字限制。Student中的password属性由于被显式指定,因此也参与了 序列化、反序列化 过程;
- 静态属性也可参与。序列化过程中包含了language="JAVA"的数据,因此即便在反序列前已经设置为“Go”,但反序列过程中会将“JAVA”重新写会到类Student中。