`

java序列化的作用

阅读更多
最近在阅读Core J2EE Patterns 的时候发现例子里用于在各个层次里进行传输的TO(Data Transfer Object)都实现了java.io.Serializable接口,看到这些偶突然感到茅塞顿开~困扰了很久的关于Serializable的疑问渐渐解开了,查找相关资料并总结如下:

  序列化是什么:
序列化就是将一个对象的状态(各个属性量)保存起来,然后在适当的时候再获得。
序列化分为两大部分:序列化和反序列化。序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的对象实例


个人看法,不一定正确
你是女生,有没有接触过毛线团
毛线团就是我们实现了Serializable接口的对象
一个毛线团可以抽成很长的毛线
又可以把很长的毛线卷成毛线团
对应起来就是
一个巨大的对象可以抽成很简单的文本表达保存起来
需要使用这个对象的时候
再根据这些文本生成
Serializable 只是一个标志,让JVM能够知道可以安全的把object转化成上面老兄说的毛线团。
你不需要作任何code。
Serializable 对JAVABEANS是必须的接口。
你不需要作任何实现。

序列化是把一个对象的状态写入一个字节流的过程,它执行RMI,RMI允许一台机器上的JAVA对象调用不同机器上的JAVA对象方法,对象可以作为参数提供给那个远程方法,发送机序列化该对象并传送它,接收机执行反序列化。
序列化和反序列化的关系图表可形成包含循环引用的顺序图表。这是整个序列化的总体思想。
而Serializable接口属于支持序列化的一个接口,只有一个实现它的对象可以被序列化工具存储和回复,Serializable接口没有定义任何成员,只用来表示一个累可以被序列化,若该类可以序列化,那么它的所有子类都可以。
下面是关于序列化的一个实例:
[b:a55efb5f91]程序名称:SerializationDemo.java
程序主题:实现对象的序列化和反序列化
程序说明:该程序由实例化一个MyClass类的对象开始,该对象有三个实例变量,类型分别为String、int、double,是希望存储和恢复的信息。[/b:a55efb5f91]

[code:1:a55efb5f91]

import java.io.*;

public class SerializationDemo{

public static void main(String args[]){

//Object serialization
try{
  MyClass object1=new MyClass("Hello",-7,2.7e10);
  System.out.println("object1:"+object1);
  FileOutputStream fos=new FileOutputStream("serial");
  ObjectOutputStream oos=new ObjectOutputStream(fos);
  oos.writeObject(object1);
  oos.flush();
  oos.close();
}
catch(Exception e){
  System.out.println("Exception during serialization:"+e);
  System.exit(0);
}

//Object deserialization
try{
  MyClass object2;
  FileInputStream fis=new FileInputStream("serial");
  ObjectInputStream ois=new ObjectInputStream(fis);
  object2=(MyClass)ois.readObject();
  ois.close();
  System.out.println("object2:"+object2);
}
catch(Exception e){
  System.out.println("Exception during deserialization:"+e);
  System.exit(0);
}
  }
}

class MyClass implements Serializable{
String s;
int i;
double d;
public MyClass(String s,int i,double d){
  this.s=s;
  this.i=i;
  this.d=d;
}
public String toString(){
  return "s="+s+";i="+i+";d="+d;
}
}
[/code:1:a55efb5f91]

程序运行结果:object1和object2的实例变量是一样的,输出如下:[code:1:a55efb5f91]object1:s=Hello;i=-7;d=2.7E10
object2:s=Hello;i=-7;d=2.7E10[/code:1:a55efb5f91]
<!--========================================================-->
hehe
未必是RMI啦
持久化也是Serializable的重点...
<!--========================================================-->
//agree
持久化是Serializable开始的目的。
楼主有什么心得,是不是也response一下?
<!--========================================================-->
现在知道是怎么回事了-----Thank Bruce Eckel a million times for his excellent <<Think in java(Second Edition) >>.书里面讲的好清楚啊。
一。猫小写的不大全,可能是因为我的帖子里写的“一般在什么情况下用”这话作怪。
我补充一下:
Object serialization的定义:
Object serialization 允许你将实现了Serializable接口的对象转换为字节序列,这些字节序列可以被完全存储以备以后重新生成原来的对象。

serialization不但可以在本机做,而且可以经由网络操作(就是猫小说的RMI)。这个好处是很大的----因为它自动屏蔽了操作系统的差异,字节顺序(用Unix下的c开发过网络编程的人应该知道这个概念,我就容易在这上面犯错)等。比如,在Window平台生成一个对象并序列化之,然后通过网络传到一台Unix机器上,然后可以在这台Unix机器上正确地重构这个对象。

Object serialization主要用来支持2种主要的特性:
1。Java的RMI(remote method invocation).RMI允许象在本机上一样操作远程机器上的对象。当发送消息给远程对象时,就需要用到serializaiton机制来发送参数和接收返回直。

2。Java的JavaBeans.   Bean的状态信息通常是在设计时配置的。Bean的状态信息必须被存起来,以便当程序运行时能恢复这些状态信息。这也需要serializaiton机制。

二。sakulagi和rollingpig说的持久化我也说一下。
我觉得你们说的应该是英文里的persistence.但是Java语言里现在只支持lightweight persistence,就是轻量级持久化,这是通过serialization机制来实现的。

persistence是指一个对象的生命周期不由程序是否执行来决定,即使是在程序终止时这个对象也存在。它把一个serializable的对象写到磁盘(本机或其他机器上的非RAM存储器),并在程序重新调用时再读取对象到通常的RAM存储器。

为什么说Java的serialization机制实现的是lightweight persistence?因为你必须显式的序列化和反序列化程序里的对象;而不是直接由一个关键词来定义一个对象是序列化的然后由系统做相应的处理。  如果以后的Java版本出现一个新的关键字来实现这种机制,比如就是persistence,如果我用

persistence  (String s="chinaunix")

然后系统自动做猫小程序里的那些处理,那么Java就实现了persistence.

第一次写这么多,而且是刚看了书不久写的,错误和行文不当之处多包涵。

再次感谢猫小,sakulagi和rollingpig给的提示。
<!--========================================================-->
Joshua Bloch 的 "Effective Java Programming Language Guide" 里面关于 Serializable 的部分讲得也很好,尤其是什么时候该用,什么时候不该用。
<!--========================================================-->
关于序列化处理Bean的状态信息能不能说的详细具体一些呢,谢谢!
<!--========================================================-->
这儿我没有写过,我想是通过序列化bean,进行writeObject方法可以将数据写入数据库的一个表。
oracle对这点有支持。

  序列化的什么特点:
如果某个类能够被序列化,其子类也可以被序列化。声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态, transient代表对象的临时数据。

什么时候使用序列化:
一:对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。
二:java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。



=======================

可以看看接口java.io.serializable的中文解释:

Serializable


public interface Serializable

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

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

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

当遍历一个图形时,可能会遇到不支持可序列化接口的对象。在此情况下,将抛出 NotSerializableException,并将标识不可序列化对象的类。

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



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 来保存的。

将对象写入流时需要指定要使用的替代对象的可序列化类,应使用准确的签名来实现此特殊方法:



ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

此 writeReplace 方法将由序列化调用,前提是如果此方法存在,而且它可以通过被序列化对象的类中定义的一个方法访问。因此,该方法可以拥有私有 (private)、受保护的 (protected) 和包私有 (package-private) 访问。子类对此方法的访问遵循 java 访问规则。

在从流中读取类的一个实例时需要指定替代的类应使用的准确签名来实现此特殊方法。



ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

此 readResolve 方法遵循与 writeReplace 相同的调用规则和访问规则。

序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID:



ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java(TM) 对象序列化规范”中所述。不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修改器显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于立即声明类 -- serialVersionUID 字段作为继承成员没有用处。

引文来源  http://zhangqiang.sc.blog.163.com/blog/static/55622777200957105947872/
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics