这篇博文主要介绍下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相等也只有在
少数情况下,能够对序列化进行兼容。