免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 863 | 回复: 0
打印 上一主题 下一主题

java序列化机制学习 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-02-14 10:26 |只看该作者 |倒序浏览
什么是序列化
java中的序列化(serialization)机制能够将一个实例对象的状态信息写入到一个字节流中,使其可以通过socket进行传输、或
者持久化存储到数据库或文件系统中;然后在需要的时候,可以根据字节流中的信息来重构一个相同的对象。序列化机制在java中有着广泛的应用,EJB、
RMI等技术都是以此为基础的。
正确使用序列化机制
一般而言,要使得一个类可以序列化,只需简单实现java.io.Serializable接口即可。该接口是一个标记式接口,它本身不包含任何内容,实现了该接口则表示这个类准备支持序列化的功能。如下例定义了类Person,并声明其可以序列化。
Java代码

  • public class Person implements java.io.Serializable {}  public class Person implements java.io.Serializable {}
    序列化机制是通过java.io.ObjectOutputStream类和java.io.ObjectInputStream类
    来实现的。在序列化(serialize)一个对象的时候,会先实例化一个ObjectOutputStream对象,然后调用其writeObject
    ()方法;在反序列化(deserialize)的时候,则会实例化一个ObjectInputStream对象,然后调用其readObject()方
    法。下例说明了这一过程。
    Java代码

  • public void serializeObject(){  
  •      String fileName = "ser.out";  
  •      FileOutputStream fos = new FileOutputStream(fileName);  
  •      ObjectOutputStream oos = new ObjectOutputStream(fos);  
  •      oos.writeObject(new Person());  
  •      oos.flush();  
  • }  
  •   
  • public void deserializeObject(){  
  •      String fileName = "ser.out";  
  •      FileInputStream fos = new FileInputStream(fileName);  
  •      ObjectInputStream oos = new ObjectInputStream(fos);  
  •      Person p = oos.readObject();  
  • }  public void serializeObject(){
         String fileName = "ser.out";
         FileOutputStream fos = new FileOutputStream(fileName);
         ObjectOutputStream oos = new ObjectOutputStream(fos);
         oos.writeObject(new Person());
         oos.flush();
    }
    public void deserializeObject(){
         String fileName = "ser.out";
         FileInputStream fos = new FileInputStream(fileName);
         ObjectInputStream oos = new ObjectInputStream(fos);
         Person p = oos.readObject();
    }
    上例中我们对一个Person对象定义了序列化和反序列化的操作。但如果Person类是不能序列化的话,即对不能序列化的类进行序列化操作,则会抛出 java.io.NotSerializableException异常。
    JVM中有一个预定义的序列化实现机制,即默认调用 ObjectOutputStream.defaultWriteObject() 和 ObjectInputStream.defaultReadObject() 来执行序列化操作。如果想自定义序列化的实现,则必须在声明了可序列化的类中实现 writeObject()和readObject()方法。
    几种使用情况
    一般在序列化一个类A的时候,有以下三种情况:
    [list=3]
  • 类A没有父类,自己实现了Serializable接口
  • 类A有父类B,且父类实现了Serializable接口
  • 类A有父类B,但父类没有实现Serializable接口

    对于第一种情况,直接实现Serializable接口即可。
    对于第二种情况,因为父类B已经实现了Serializable接口,故类A无需实现此接口;如果父类实现了writeObject()和readObject(),则使用此方法,否则直接使用默认的机制。
    对于第三种情况,则必须在类A中显示实现writeObject()和readObject()方法来处理父类B的状态信息;还有一点要特别注
    意,在父类B中一定要有一个无参的构造函数,这是因为在反序列化的过程中并不会使用声明为可序列化的类A的任何构造函数,而是会调用其没有申明为可序列化
    的父类B的无参构造函数。
    序列化机制的一些问题
    • 性能问题
      为了序列化类A一个实例对象,所需保存的全部信息如下:
      1. 与此实例对象相关的全部类的元数据(metadata)信息;因为继承关系,类A的实例对象也是其任一父类的对象。因而,需要将整个继承链上的每一个类的元数据信息,按照从父到子的顺序依次保存起来。
      2. 类A的描述信息。此描述信息中可能包含有如下这些信息:类的版本ID(version ID)、表示是否自定义了序列化实现机制的标志、可序列化的属性的数目、每个属性的名字和值、及其可序列化的父类的描述信息。
      3. 将实例对象作为其每一个超类的实例对象,并将这些数据信息都保存起来。
      在RMI等远程调用的应用中,每调用一个方法,都需要传递如此多的信息量;久而久之,会对系统的性能照成很大的影响。
    • 版本信息
      当用readObject()方法读取一个序列化对象的byte流信息时,会从中得到所有相关类的描述信息以及示例对象的状态数据;然后将此描
      述信息与其本地要构造的类的描述信息进行比较,如果相同则会创建一个新的实例并恢复其状态,否则会抛出异常。这就是序列化对象的版本检测。JVM中默认的
      描述信息是使用一个长整型的哈希码(hashcode)值来表示,这个值与类的各个方面的信息有关,如类名、类修饰符、所实现的接口名、方法和构造函数的
      信息、属性的信息等。因而,一个类作一些微小的变动都有可能导致不同的哈希码值。例如开始对一个实例对象进行了序列化,接着对类增加了一个方法,或者更改
      了某个属性的名称,当再想根据序列化信息来重构以前那个对象的时候,此时两个类的版本信息已经不匹配,不可能再恢复此对象的状态了。要解决这个问题,可能
      在类中显示定义一个值,如下所示:
      Java代码

    • private static final long serialVersionUID = ALongValue;  private static final long serialVersionUID = ALongValue;
      这样,序列化机制会使用这个值来作为类的版本标识符,从而可以解决不兼容的问题。但是它却引入了一个新的问题,即使一个类作了实质性的改变,如增加或删除了一些可序列化的属性,在这种机制下仍然会认为这两个类是相等的。

    一种更好的选择
    作为实现Serializable接口的一种替代方案,实现java.io.Externalizable接口同样可以标识一个类为可序列化。
    Externalizable接口中定义了以下两个方法:
    Java代码

  • public void readExternal(ObjectInput in);  
  • public void writeExternal(ObjectOutput out);  public void readExternal(ObjectInput in);
    public void writeExternal(ObjectOutput out);
    这两个方法的功能与 readObject()和writeObject()方法相同,任何实现了Externalizable接口的类都需要这实现两个函数来定义其序列化机制。
    使用Externalizable比使用Serializable有着性能上的提高。前者序列化一个对象,所需保存的信息比后者要小,对于后者所
    需保存的第3个方面的信息,前者不需要访问每一个父类并使其保存相关的状态信息,而只需简单地调用类中实现的writeExternal()方法即可。
                   
                   
                   

    本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/26845/showart_478045.html
  • 您需要登录后才可以回帖 登录 | 注册

    本版积分规则 发表回复

      

    北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
    未成年举报专区
    中国互联网协会会员  联系我们:huangweiwei@itpub.net
    感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

    清除 Cookies - ChinaUnix - Archiver - WAP - TOP