关于Java对象序列化您不知道的5件事(1)

来源:互联网 发布:淘宝有什么促销活动 编辑:程序博客网 时间:2024/06/05 20:24

Java对象序列化是JDK1.1中引入的一组开创性特性之一,之前51CTO也曾介绍过Java序列化的机制和原理,这里我们将使用Person来发现您可能不知道的关于Java对象序列化的5件事。

51CTO推荐专题:Java基础教程

实际上,序列化的思想是“冻结”对象状态,传输对象状态(写到磁盘、通过网络传输等等),然后“解冻”状态,重新获得可用的Java对象。所有这些事情的发生有点像是魔术,这要归功于ObjectInputStream/ObjectOutputStream类、完全保真的元数据以及程序员愿意用Serializable标识接口标记他们的类,从而“参与”这个过程。清单1显示一个实现Serializable的Person类。

  1. 清单1.SerializablePerson  
  2. packagecom.tedneward;  
  3.  
  4. publicclassPerson  
  5. implementsjava.io.Serializable  
  6. {  
  7. publicPerson(Stringfn,Stringln,inta)  
  8. {  
  9. this.firstName=fn;this.lastName=ln;this.age=a;  
  10. }  
  11.  
  12. publicStringgetFirstName(){returnfirstName;}  
  13. publicStringgetLastName(){returnlastName;}  
  14. publicintgetAge(){returnage;}  
  15. publicPersongetSpouse(){returnspouse;}  
  16.  
  17. publicvoidsetFirstName(Stringvalue){firstName=value;}  
  18. publicvoidsetLastName(Stringvalue){lastName=value;}  
  19. publicvoidsetAge(intvalue){age=value;}  
  20. publicvoidsetSpouse(Personvalue){spouse=value;}  
  21.  
  22. publicStringtoString()  
  23. {  
  24. return"[Person:firstName="+firstName+  
  25. "lastName="+lastName+  
  26. "age="+age+  
  27. "spouse="+spouse.getFirstName()+  
  28. "]";  
  29. }  
  30.  
  31. privateStringfirstName;  
  32. privateStringlastName;  
  33. privateintage;  
  34. privatePersonspouse;  
  35.  

将Person序列化后,很容易将对象状态写到磁盘,然后重新读出它,下面的JUnit4单元测试对此做了演示。

  1. 清单2.对Person进行反序列化  
  2. publicclassSerTest  
  3. {  
  4. @TestpublicvoidserializeToDisk()  
  5. {  
  6. try  
  7. {  
  8. com.tedneward.Personted=newcom.tedneward.Person("Ted","Neward",39);  
  9. com.tedneward.Personcharl=newcom.tedneward.Person("Charlotte",  
  10. "Neward",38);  
  11.  
  12. ted.setSpouse(charl);charl.setSpouse(ted);  
  13.  
  14. FileOutputStreamfos=newFileOutputStream("tempdata.ser");  
  15. ObjectOutputStreamoos=newObjectOutputStream(fos);  
  16. oos.writeObject(ted);  
  17. oos.close();  
  18. }  
  19. catch(Exceptionex)  
  20. {  
  21. fail("Exceptionthrownduringtest:"+ex.toString());  
  22. }  
  23.  
  24. try  
  25. {  
  26. FileInputStreamfis=newFileInputStream("tempdata.ser");  
  27. ObjectInputStreamois=newObjectInputStream(fis);  
  28. com.tedneward.Personted=(com.tedneward.Person)ois.readObject();  
  29. ois.close();  
  30.  
  31. assertEquals(ted.getFirstName(),"Ted");  
  32. assertEquals(ted.getSpouse().getFirstName(),"Charlotte");  
  33.  
  34. //Cleanupthefile  
  35. newFile("tempdata.ser").delete();  
  36. }  
  37. catch(Exceptionex)  
  38. {  
  39. fail("Exceptionthrownduringtest:"+ex.toString());  
  40. }  
  41. }  

到现在为止,还没有看到什么新鲜的或令人兴奋的事情,但是这是一个很好的出发点。

1.序列化允许重构

序列化允许一定数量的类变种,甚至重构之后也是如此,ObjectInputStream仍可以很好地将其读出来。JavaObjectSerialization规范可以自动管理的关键任务是:

◆将新字段添加到类中。

◆将字段从static改为非static。

◆将字段从transient改为非transient。

◆取决于所需的向后兼容程度,转换字段形式(从非static转换为static或从非transient转换为transient)或者删除字段需要额外的消息传递。

重构序列化类

既然已经知道序列化允许重构,我们来看看当把新字段添加到Person类中时,会发生什么事情。如清单3所示,PersonV2在原先Person类的基础上引入一个表示性别的新字段。

  1. 清单3.将新字段添加到序列化的Person中  
  2. enumGender  
  3. {  
  4. MALE,FEMALE  
  5. }  
  6.  
  7. publicclassPerson  
  8. implementsjava.io.Serializable  
  9. {  
  10. publicPerson(Stringfn,Stringln,inta,Genderg)  
  11. {  
  12. this.firstName=fn;this.lastName=ln;this.age=a;this.gender=g;  
  13. }  
  14.  
  15. publicStringgetFirstName(){returnfirstName;}  
  16. publicStringgetLastName(){returnlastName;}  
  17. publicGendergetGender(){returngender;}  
  18. publicintgetAge(){returnage;}  
  19. publicPersongetSpouse(){returnspouse;}  
  20.  
  21. publicvoidsetFirstName(Stringvalue){firstName=value;}  
  22. publicvoidsetLastName(Stringvalue){lastName=value;}  
  23. publicvoidsetGender(Gendervalue){gender=value;}  
  24. publicvoidsetAge(intvalue){age=value;}  
  25. publicvoidsetSpouse(Personvalue){spouse=value;}  
  26.  
  27. publicStringtoString()  
  28. {  
  29. return"[Person:firstName="+firstName+  
  30. "lastName="+lastName+  
  31. "gender="+gender+  
  32. "age="+age+  
  33. "spouse="+spouse.getFirstName()+  
  34. "]";  
  35. }  
  36.  
  37. privateStringfirstName;  
  38. privateStringlastName;  
  39. privateintage;  
  40. privatePersonspouse;  
  41. privateGendergender;  

序列化使用一个hash,该hash是根据给定源文件中几乎所有东西—方法名称、字段名称、字段类型、访问修改方法等—计算出来的,序列化将该hash值与序列化流中的hash值相比较。

为了使Java运行时相信两种类型实际上是一样的,第二版和随后版本的Person必须与第一版有相同的序列化版本hash(存储为privatestaticfinalserialVersionUID字段)。因此,我们需要serialVersionUID字段,它是通过对原始(或V1)版本的Person类运行JDKserialver命令计算出的。

一旦有了Person的serialVersionUID,不仅可以从原始对象Person的序列化数据创建PersonV2对象(当出现新字段时,新字段被设为缺省值,最常见的是“null”),还可以反过来做:即从PersonV2的数据通过反序列化得到Person,这毫不奇怪。

原创粉丝点击