java.io.Serializable序列化

来源:互联网 发布:网络教育好考吗 编辑:程序博客网 时间:2024/06/05 07:46

在Java Web应用中,如果希望对HttpSession中存放的Java对象进行持久化,那么这个Java对象所属的类必须 java.io.Serializable接口。

        下面就是序列化的概念。

         序列化是什么:序列化就是将一个对象的状态(各个属性量)保存起来,然后在适当的时候再获得。

         序列化分为两大部分:序列化和反序列化。序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流并重 构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的对象实例序列化的什么特点:如果某个类能够被序列 化,其子类也可以被序列化。声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态, transient代表对象的临时数据。

         public interface Serializable (API5.0)类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段, 仅用于标识可序列化的语义。

  要允许不可序列化类的子类型序列化,可以假定该子类型负责保存和还原超类型的公用 (public)、受保护的 (protected) 和(如果可访问)包 (package) 字段的状态。仅在子类型扩展的类有一个可访问的无参数构造方法来初始化该类的状态时,才可以假定子类型有此责任。如果不是这种情况,则声明一个类为可序列 化类是错误的。该错误将在运行时检测到。

  在反序列化过程中,将使用该类的公用或受保护的无参数构造方法初始化不可序列化类的字段。可序列化的子类必须能够访问无参数的构造方法。可序列化子类的字段将从该流中还原。

在序列化和反序列化过程中需要特殊处理的类必须使用下列准确签名来实现特殊方法:

private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in)throws IOException, ClassNotFoundException;

writeObject 方法负责写入特定类的对象的状态,以便相应的 readObject 方法可以还原它。通过调用 out.defaultWriteObject 可以调用保存 Object 的字段的默认机制。该方法本身不需要涉及属于其超类或子类的状态。状态是通过使用 writeObject 方法或使用 DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。

readObject 方法负责从流中读取并还原类字段。它可以调用 in.defaultReadObject 来调用默认机制,以还原对象的非静态和非瞬态字段。defaultReadObject 方法使用流中的信息来分配流中通过当前对象中相应命名字段保存的对象的字段。这用于处理类发展后需要添加新字段的情形。该方法本身不需要涉及属于其超类或 子类的状态。状态是通过使用 writeObject 方法或使用 DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。

对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;

2) 在网络上传送对象的字节序列。
Java语言中要求只有实现了java.io.Serializable接口的类的对象才能被序列化及反序列化。JDK类库中的有些类(如String类、包装类(Java语言用包装类来把基本类型数据转换为对象,基本类型数据有四类八种)和Date类等)都实现了Serializable接口。
对象的序列化包括以下步骤。
1.创建一个对象输出流,它可以包装一个其他类型的输出流,比如文件输出流。   
2.通过对象输出流的writeObject()写对象。

对象的反序列化包括以下步骤。

1.创建一个对象输入流,它可以包装一个其他类型的输入流,比如文件输入流。
2.通过对象输入流的readObject()方法读取对象
其次在对象的序列化和反序列化过程当中,必须注意的事情是:为了能读出正确的数据,必须保证对象输出流的写对象的顺序与对象输入流读对象的顺序是一致的。

例子:用String,Date类和自己编写的Customer类序列化后,再反序列.
创建一个Customer的类,针对Custmoer重写equals方法

 


  在序列化时,有几点要注意的:
     1:当一个对象被序列化时,只保存对象的非静态成员变量,不能保存任何的成员方法和静态的成员变量。
  2:如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存。
  3:如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个NotSerializableException。我们可以将这个引用标记为transient,那么对象仍然可以序列化。

  还有我们对某个对象进行序列化时候,往往对整个对象全部序列化了,比如说类里有些数据比较敏感,不希望序列化,一个方法可以用 transient来标识,另一个方法我们可以在类里重写,可以通过指定关键transient使对象中的某个数据元素不被还原,这种方式常用于安全上的 保护。比如对象中保存的密码。
  transient 只能用在类的成员变量上,不能用在方法里。transient 变量不能是final和static的 transient(临时)关键字。控制序列化过程时,可能有一个特定的子对象不愿让Java的序列化机制自动保存与恢复。一般地,若那个子对象包含了不 想序列化的敏感信息(如密码),就会面临这种情况。即使那种信息在对象中具有“private”(私有)属性,但一旦经序列化处理,人们就可以通过读取一 个文件,或者拦截网络传输得到它。

 为防止对象的敏感部分被序列化,一个办法是将自己的类实现为Externalizable,就象前面展示的那样。这样一来,没有任何东西可以自动序列化,只能在writeExternal()明确序列化那些需要的部分。
然而,若操作的是一个Serializable对象,所有序列化操作都会自动进行。为解决这个问题,可以用transient(临时)逐个字段地关闭序列化,它的意思是“不要麻烦你(指自动机制)保存或恢复它了——我会自己处理的”。
  例如,一个对象包含了与一个特定的登录会话有关的信息。校验登录的合法性时,一般都想将数据保存下来,但不包括密码。为做到这一点,最简单的办法是实现Serializable,并将password字段设为transient。password被设为transient,所以不会自动保存到磁盘;另外,自动序列化机制也不会作恢复它的尝试。一旦对象恢复成原来的样子,password字段就会变成null。由于Externalizable对象默认时不保存的任何字段,所以transient关键字只能伴随 Serializable使用。

 
 

  还有我们对某个对象进行序列化时候,往往对整个对象全部序列化了,比如说类里有些数据比较敏感,不希望序列化,一个方法可以用transient来标识,另一个方法我们可以在类里重写。

  1、实现Serializable会导致发布的API难以更改,并且使得package-private和private这两个本来封装的较好的咚咚也不能得到保障
  2、Serializable会为每个类生成一个序列号,生成依据是类名、类实现的接口名、public和protected方法,所以只要你一不小 心改了一个已经publish的API,并且没有自己定义一个long类型的叫做serialVersionUID的field,哪怕只是添加一个 getXX,就会让你读原来的序列化到文件中的东西读不出来(不知道为什么要把方法名算进去?)
  3、不用构造函数用Serializable就可以构造对象,看起来不大合理,这被称为extralinguistic mechanism,所以当实现Serializable时应该注意维持构造函数中所维持的那些不变状态。
  4、增加了发布新版本的类时的测试负担
  5、1.4版本后,JavaBeans的持久化采用基于XML的机制,不再需要Serializable

  6、设计用来被继承的类时,尽量不实现Serializable,用来被继承的interface也不要继承Serializable。但是如 果父类不实现Serializable接口,子类很难实现它,特别是对于父类没有可以访问的不含参数的构造函数的时候。所以,一旦你决定不实现 Serializable接口并且类被用来继承的时候记得提供一个无参数的构造函数。
  7、内部类还是不要实现Serializable好了,除非是static的,(偶也觉得内部类不适合
用来干这类活的)
  8、使用一个自定义的序列化方法。

  9、不管你选择什么序列化形式,声明一个显式的UID:
private static final long serialVersionUID = randomLongValue;

  10、不需要序列化的东西使用transient注掉它吧,别什么都留着
  11、writeObject/readObject重载以完成更好的序列化readResolve 与 writeReplace重载以完成更好的维护invariant controllers    完全定制序列化过程:如果一个类要完全负责自己的序列化,则实现Externalizable接口而不是Serializable接口。 Externalizable接口定义包括两个方法writeExternal()与readExternal()。利用这些方法可以控制对象数据成员如 何写入字节流.类实现Externalizable时,头写入对象流中,然后类完全负责序列化和恢复数据成员,除了头以外,根本没有自动序列化。这里要注 意了。声明类实现Externalizable接口会有重大的安全风险。writeExternal()与readExternal()方法声明为 public,恶意类可以用这些方法读取和写入对象数据。如果对象包含敏感信息,则要格外小心。这包括使用安全套接或加密整个字节流。