Java中的序列化和反序列化,在将对象进行流传输或本地化保存时,能够很好的进行保存和还原。
一个类实现了 java.io.Serializable 接口后,可以生成这个类的版本号 serialVersionUID ,可以自动生成也可以自己定义一个值,例如
private static final long serialVersionUID = 12345L;
如果一个类没有指定版本号,编译器会自动根据类的属性方法等生成一个序列号作为对象的版本号。如果读取流中传输的这个序列化对象后,如果类发生了变化,那么版本号就会变动,不能还原序列化对象,抛出 java.io.InvalidClassException 。
指定版本号,可以保证如果写入方和读取方类的版本号一致的情况下,能够正常还原对象的信息。
使用 ObjectInputStream 反序列化的时候,ObjectInputStream 会先读取文件中的serialVersionUID,然后与本地的 class 文件的 serialVersionUID 进行对比,如果这两个 id 不一致,反序列则失败。
下面举简单的例子来说明:
User 类,两个属性:
public class User implements Serializable {
private static final long serialVersionUID = 12345L;
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
将其序列化保存到本地文件:
public class TestSerial {
public static void main(String[] args) throws FileNotFoundException, IOException {
User user = new User("张三", "123456");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt"));
oos.writeObject(user);
oos.close();
}
}
之后,进行反序列化获取对象的信息:
public class TestUnSerial {
public static void main(String[] args) throws ClassNotFoundException, IOException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));
User user = (User)ois.readObject();
ois.close();
System.out.println("username:" + user.getUsername() +" password:" + user.getPassword());
}
}
运行结果:
username: 张三 password: 123456
如果我们改变版本号 12345L 为 123456789L ,类的其他信息不变:
private static final long serialVersionUID = 123456789L;
再次运行反序列化测试,会发现报错 java.io.InvalidClassException,错误中会说明版本号不同导致的:
Exception in thread "main" java.io.InvalidClassException: test.run.io.User; local class incompatible: stream classdesc serialVersionUID = 12345, local class serialVersionUID = 123456789
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1630)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
at test.run.io.TestUnSerial.main(TestUnSerial.java:11)
另外,如果版本号不做修改,序列化之后,进行增加或删除属性和方法的操作,那么在反序列化,并不会报错,能够正常的解析:如果增加了属性或方法,即这个属性或方法在序列化对象中没有,那么属性就赋予默认值,方法可以正常调用;如果删除了属性或方法,那么这个属性或方法在反序列化的对象中不会出现。
最后,如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字 transient 修饰。