这篇博文主要介绍下Java序列化相关的内容以及如何去理解transient关键字。
首先在Java中有一个序列化接口Serializable,当需要表示该类是可序列化的,我们可以显示的声明实现序列化(Serializable)接口。然而transient关键字正是对于实现了序列化接口的。它的作用是对于标识了transient修饰符不做序列化操作。我们先构建一个类,代码如下:
class LoggingInfo implements Serializable {
//This field is used for check the versions between the original one and deserialized ones.
private static final long serialVersionUID = 1L;
private Date loggingDate = new Date();
private String username;
private transient String password;
LoggingInfo(String username, String password) {
this.username = username;
this.password = password;
}
public String toString() {
return "username=" + username + " password=" + password;
}
}
然后我们用方法对上面的实例进行序列化和反序列化。代码如下:
public class TestSerial {
public static void main(String[] args) throws IOException, ClassNotFoundException {
LoggingInfo logInfo = new LoggingInfo("MIKE", "MECHANICS");
System.out.println(logInfo.toString());
FileOutputStream fos = new FileOutputStream("c:\\logging.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(logInfo);
oos.close();
}
}
代码执行完后我们会发现在c盘根目录多出一个名为logging.obj的文件。说明序列化成功。同时最重要的是在序列化之前控制台上会打出password为'MECHANICS'的字样。这说明在序列化前这个对象的password是有值的。
于是我们再对其进行反序列化。代码如下:
public class TestSerial {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("c:\\logging.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
LoggingInfo info = (LoggingInfo) ois.readObject();
ois.close();
System.out.println(info);
}
}
再运行了此段代码后你会发现password值变成了null,也就说明了transient值起作用了。
最后还有一点需要强调,就是serialVersionUID。该域是控制版本使用的。对于同一个对象,如果该域的原始值和
反序列化之后的值不一致。则认为这2个同一类型的对象,是序列化不兼容的。如果我们默认不显式设置该值,那么该值会在编译时期进行添加。也就是说一旦当你第一次序列化成功后,如果原对象进行再编译,那么序列化对象是无法成功反序列化的。 因为两次对该对象的编译产生的serialVersionUID是不同的。所以我们一般比较通用的做法是将该域的值显式进行
赋值,这样就能对序列化和反序列化做兼容了。
当然并不serialVersionUID相同就一定能够成功的进行序列化操作。前提是同一个对象的序列化和反序列化对象在
内容上保持一致。如果对某对象添加了某个新的方法等等改变原有对象行为的操作,哪怕serialVersionUID相等也只有在
少数情况下,能够对序列化进行兼容。