一、什么是序列化和反序列化
- 序列化(Serialization):将对象的状态信息转换为可以存储或传输的形式的过程。
- 反序列化(Deserialization):从存储区中读取对象的状态并重新构建对象的过程。
二、serialVersionUID的作用
-
在Java中,类的serialVersionUID被用来区分类的版本。
-
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同说明类是一致的,可以进行反序列化,否则会出现反序列化版本不一致的异常,即InvalidCastException。
-
-
下面来验证一下上面的结论
- 创建一个类,用于后续对象的序列化
public class Person implements Serializable {
private static final long serialVersionUID = -5676168367438003317L;
private int no;
private String name;
public Person(int no, String name) {
this.no = no;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
- 创建序列化类
/**
* 序列化person对象,保存到person.txt文件中
*/
public class Serialization {
public static void main(String[] args) throws IOException {
Person tom = new Person(1, "Tom");
System.out.println(tom);
FileOutputStream fos = new FileOutputStream("person.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(tom);
oos.close();
fos.close();
}
}
- 创建反序列化类
/**
* 从person.txt文件中读取person对象信息,并反序列化为对象
*/
public class Deserialization {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("person.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Person person = (Person) ois.readObject();
System.out.println(person);
}
}
假设在A机器的JVM中进行序列化操作,在B机器的JVM中进行反序列化操作
① 模拟A、B两台机器的Person类中的serialVersionUID相同:Person保持不变,先执行Serialization.main进行序列化操作,再执行Deserialization.main执行反序列化操作,查看反序列化结果
② 模拟A、B两台机器的Person类中的serialVersionUID不相同: 先执行Serialization.main进行序列化操作,修改Person类的serialVersionUID,再执行Deserialization.main执行反序列化操作,查看反序列化结果
反序列化失败,抛出异常java.io.InvalidClassException
Exception in thread "main" java.io.InvalidClassException: com.unwrapping.serial.Person; local class incompatible: stream classdesc serialVersionUID = -5676168367438003317, local class serialVersionUID = -5676168367438003318
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
at com.unwrapping.serial.Deserialization.main(Deserialization.java:14)
三、static、transient修饰的属性不参与序列化
- 验证一下static
public class User implements Serializable {
private static int id = 10;
public static void main(String[] args) throws IOException, ClassNotFoundException {
User user = new User();
// 将user序列化保存到user.txt
FileOutputStream fos = new FileOutputStream("user.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(user);
fos.close();
oos.close();
// 将user的id修改为20
user.id = 20;
// user.txt 反序列化
FileInputStream fis = new FileInputStream("user.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
User newUser = (User) ois.readObject();
// 打印id
System.out.println(newUser.id);
}
}
对于static修饰的id,初始值为10,序列化操作后,将其改为20,反序列化操作后得到的对象的id是20而不是10,这说明id根本没有参与序列化;因为序列化保存的是对象的状态,而static修饰的属性是类的状态,所以static修饰的属性不会被序列化
- 验证一下transient
public class User implements Serializable {
private String userName;
private transient String password;
public User(String userName, String password) {
this.userName = userName;
this.password = password;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
User user = new User("Bob", "123456");
// 将user序列化保存到user.txt
FileOutputStream fos = new FileOutputStream("user.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(user);
fos.close();
oos.close();
// user.txt 反序列化
FileInputStream fis = new FileInputStream("user.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
User newUser = (User) ois.readObject();
// 观察输出结果
System.out.println(newUser.password);
}
}
运行结果为null,说明transient修饰的属性不能被序列化