Java Serializable(序列化)的理解和总结、具体实现过程
内存中的对象是怎么存在的? 内存中各种对象的状态是? 实例变量是什么(指的是实例化的对象吗)? 使用序列化的好处是什么?
1、序列化是干什么的?
简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。
2、什么情况下需要序列化
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
3、当对一个对象实现序列化时,究竟发生了什么?
在没有序列化前,每个保存在堆(Heap)中的对象都有相应的状态(state),即实例变量(instance ariable)比如:
Java代码
1. Foo myFoo = new Foo();
2. myFoo .setWidth(37);
3. myFoo.setHeight(70);
当通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.dat文件中,这样以后又可以把它 从文件中读出来,重新在堆中创建原来的对象。当然保存时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等以便恢复原来的对象。
Java代码
1. FileOutputStream fs = new FileOutputStream("foo.dat");
2. ObjectOutputStream os = new ObjectOutputStream(fs);
3. os.writeObject(myFoo);
4、实现序列化(保存到一个文件)的步骤
a)Make a FileOutputStream
Java代码
1. FileOutputStream fs = new FileOutputStream("foo.ser");
b)Make a ObjectOutputStream
Java代码
1. ObjectOutputStream os = new ObjectOutputStream(fs);
c)write the object
Java代码
1. os.writeObject(myObject1);
d) close the ObjectOutputStream
Java代码
1. os.close();
5、举例说明
Java代码
1. import java.io.*;
4. public class Box implements Serializable
5. {
6. private int width;
7. private int height;
8.
9. public void setWidth(int width){
10. this.width = width;
11. }
12. public void setHeight(int height){
13. this.height = height;
14. }
15.
16. public static void main(String[] args){
17. Box myBox = new Box();
18. myBox.setWidth(50);
19. myBox.setHeight(30);
20.
21. try{
22. FileOutputStream fs = new FileOutputStream("foo.ser");
23. ObjectOutputStream os = new ObjectOutputStream(fs);
24. os.writeObject(myBox);
25. os.close();
26. }catch(Exception ex){
27. ex.printStackTrace();
28. }
29. }
30.
31. }
6、相关注意事项
a)序列化时,只对对象的状态进行保存,而不管对象的方法;
b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
d)并非所有的对象都可以序列化,至于为什么不可以,有很多原因了,比如:
1.安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行rmi传输等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。
2. 资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配,而且,也是没有必要这样实现。
说了这么多那么 序列化到底是什么呢?
序列化是什么:
序列化就是将一个对象的状态(各个属性量)保存起来,然后在适当的时候再获得。
序列化分为两大部分:序列化和反序列化。序列化是这个过程的第一部分,将数据分解成字节流,以便
存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的对象实例
序列化的什么特点:
如果某个类能够被序列化,其子类也可以被序列化。声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。
什么时候使用序列化:
一:对象序列化可以实现分布式对象。主要应用例如:rmi要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。
二:java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。
类通过实现java.io.serializable接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
要允许不可序列化类的子类型序列化,可以假定该子类型负责保存和还原超类型的公用(public)、受保护的(protected)和(如果可访问)包(package)字段的状态。仅在子类型扩展的类有一个可访问的无参数构造方法来初始化该类的状态时,才可以假定子类型有此责任。如果不是这种情况,则声明一个类为可序列化类是错误的。该错误将在运行时检测到。
在反序列化过程中,将使用该类的公用或受保护的无参数构造方法初始化不可序列化类的字段。可序列化的子类必须能够访问无参数的构造方法。可序列化子类的字段将从该流中还原。
当遍历一个图形时,可能会遇到不支持可序列化接口的对象。在此情况下,将抛出notserializableexception,并将标识不可序列化对象的类。
在序列化和反序列化过程中需要特殊处理的类必须使用下列准确签名来实现特殊方法:
writeobject方法负责写入特定类的对象的状态,以便相应的readobject方法可以还原它。通过调用out.defaultwriteobject可以调用保存object的字段的默认机制。该方法本身不需要涉及属于其超类或子类的状态。状态是通过使用writeobject方法或使用dataoutput支持的用于基本数据类型的方法将各个字段写入objectoutputstream来保存的。
readobject方法负责从流中读取并还原类字段。它可以调用in.defaultreadobject来调用默认机制,以还原对象的非静态和非瞬态字段。defaultreadobject方法使用流中的信息来分配流中通过当前对象中相应命名字段保存的对象的字段。这用于处理类发展后需要添加新字段的情形。该方法本身不需要涉及属于其超类或子类的状态。状态是通过使用writeobject方法或使用dataoutput支持的用于基本数据类型的方法将各个字段写入objectoutputstream来保存的。
将对象写入流时需要指定要使用的替代对象的可序列化类,应使用准确的签名来实现此特殊方法:
此writereplace方法将由序列化调用,前提是如果此方法存在,而且它可以通过被序列化对象的类中定义的一个方法访问。因此,该方法可以拥有私有(private)、受保护的(protected)和包私有(package-private)访问。子类对此方法的访问遵循java访问规则。
在从流中读取类的一个实例时需要指定替代的类应使用的准确签名来实现此特殊方法。
序列化运行时使用一个称为serialversionuid的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的serialversionuid与对应的发送者的类的版本号不同,则反序列化将会导致invalidclassexception。可序列化类可以通过声明名为"serialversionuid"的字段(该字段必须是静态(static)、最终(final)的long型字段)显式声明其自己的serialversionuid:
如果可序列化类未显式声明serialversionuid,则序列化运行时将基于该类的各个方面计算该类的默认serialversionuid值,如“
java(tm)对象序列化规范”中所述。不过,强烈建议所有可序列化类都显式声明serialversionuid值,原因计算默认的serialversionuid对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的invalidclassexception。因此,为保证serialversionuid值跨不同java编译器实现的一致性,序列化类必须声明一个明确的serialversionuid值。还强烈建议使用private修改器显示声明serialversionuid(如果可能),原因是这种声明仅应用于立即声明类--serialversionuid字段作为继承成员没有用处。
java.io.serializable引发的问题——什么是序列化?在什么情况下将类序列化?
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。序列化的实现:将需要被序列化的类实现serializable接口,该接口没有需要实现的方法,implementsserializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:fileoutputstream)来构造一个objectoutputstream(对象流)对象,接着,使用objectoutputstream对象的writeobject(objectobj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
序列化:序列化是将对象转换为容易传输的格式的过程。例如,可以序列化一个对象,然后使用http通过internet在客户端和
服务器之间传输该对象。在另一端,反序列化将从该流重新构造对象。
是对象永久化的一种机制。
确切的说应该是对象的序列化,一般程序在运行时,产生对象,这些对象随着程序的停止运行而消失,但如果我们想把某些对象(因为是对象,所以有各自不同的特性)保存下来,在程序终止运行后,这些对象仍然存在,可以在程序再次运行时读取这些对象的值,或者在其他程序中利用这些保存下来的对象。这种情况下就要用到对象的序列化。
只有序列化的对象才可以
服务器硬盘上把序列化的对象取出,然后通过网络传到客户端,再由客户端把序列化的对象读入内存,执行相应的处理。
对象序列化是java的一个特征,通过该特征可以将对象写作一组字节码,当在其他位置读到这些字节码时,可以依此创建一个新的对象,而且新对象的状态与原对象完全相同。为了实现对象序列化,要求必须能够访问类的私有变量,从而保证对象状态能够正确的得以保存和恢复。相应的,对象序列化api能够在对象重建时,将这些值还原给私有的数据成员。这是对java语言访问权限的挑战。通常用在服务器客户端的对象
交换上面,另外就是在本机的存储。
对象序列化的最主要的用处就是在传递,和保存对象(object)的时候,保证对象的完整性和可传递性。譬如通过网络传输,或者把一个对象保存成一个文件的时候,要实现序列化接口。
即使你没有用过对象序列化(serialization),你可能也知道它。但你是否知道
java还支持另外一种形式的对象持久化,外部化(externalization)?
下面是序列化和外部化在代码级的关联方式:
序列化和外部化的主要区别
外部化和序列化是实现同一目标的两种不同方法。下面让我们分析一下序列化和外部化之间的主要区别。
通过serializable接口对对象序列化的支持是内建于核心api的,但是java.io.externalizable的所有实现者必须提供读取和写出的实现。java已经具有了对序列化的内建支持,也就是说只要制作自己的类java.io.serializable,java就会试图存储和重组你的对象。如果使用外部化,你就可以选择完全由自己完成读取和写出的工作,java对外部化所提供的唯一支持是接口:
序列化会自动存储必要的信息,用以反序列化被存储的实例,而外部化则只保存被存储的类的标识。当你通过java.io.serializable接口序列化一个对象时,有关类的信息,比如它的属性和这些属性的类型,都与实例数据一起被存储起来。在选择走externalizable这条路时,java只存储有关每个被存储类型的非常少的信息。
每个接口的优点和缺点
JAVA•优点:内建支持
•优点:易于实现
•缺点:占用空间过大
•缺点:由于额外的开销导致速度变比较慢
•优点:开销较少(程序员决定存储什么)
•优点:可能的速度提升
•缺点:虚拟机不提供任何帮助,也就是说所有的工作都落到了开发人员的肩上。
在两者之间如何选择要根据应用程序的需求来定。serializable通常是最简单的解决方案,但是它可能会导致出现不可接受的性能问题或空间问题;在出现这些问题的情况下,externalizable可能是一条可行之路。
要记住一点,如果一个类是可外部化的(externalizable),那么externalizable方法将被用于序列化类的实例,即使这个类型提供了serializable方法