Java提供一种机制叫做序列化,通过有序的格式或者字节序列持久化java对象,其中包含对象的数据,还有对象的类型,和保存在对象中的数据类型。 所以,如果我们已经序列化了一个对象,那么它可以被读取并通过对象的类型和其他信息进行反序列化,并最终获取对象的原型。 ObjectInputStream 和 ObjectOutputStream对象是高级别的流对象,包含序列化和反序列化的方法。 ObjectOutputStream 拥有很多序列化对象的方法,最常用的是:
|
enixyu
| ||||
其它翻译版本(1) |
那么哪里会需要序列化呢?序列化通常在需要通过网络传输数据,或者保存对象到文件的场合使用。这里说的数据是对象而不是文本。 现在的问题是,我们的网络架构和硬盘都只能识别二进制和字节,而不能识别Java对象。 序列化就是把Java对象中的value/states翻译为字节,以便通过网络传输或者保存。另外,反序列化就是通过读取字节码,并把它翻译回java对象。 |
enixyu
|
serialVersionUID概念 serialVersionUID 是用于保证同一个对象(在序列化中会被用到)可以在Deserialization过程中被载入。serialVersionUID 是用于对象的版本控制。你可以参考serialVersionUID in java serialization获取更多信息。 对于序列化: 步骤如下: 让我们看一个列子: 在 src->org.arpit.javapostsforlearning 创建Employee.java 1.Employee.java
Java中的标记接口(marker interface)就是一个没有任何字段或者方法的接口,简单的来说,java中把空接口叫做标记接口(marker interface) |
enixyu
|
2.SerializeMain.java
对于反序列化:在包src->org.arpit.javapostsforlearning中,创建 DeserializeMain.java
3.DeserializeMain.java
4.运行:
首先运行SerializeMain.java,然后运行 DeserializeMain.java,你会得到如下的结果:
就这样,我们序列化了一个employee对象,并对它进行反序列化。这看起来和简单,但是如果其中包含对象引用,继承,那么情况就会变得复杂。接下来让我们一个接一个的看一下例子,看看如何在各种场合中实现序列化。
|
enixyu
|
案例1 - 如果对象引用了其他对象,那该如何 我们已经看过最简单的序列化例子,现在看看,如何处理对象中引用了其他对象的场合。我们该如何序列化?引用对象也会被序列化吗?对的,你不需要显式的序列化引用对象。当你序列化任何对象,如果它包含引用对象,那么Java序列化会自动序列化该对象的整个对象图。例如,Employee现在引用了一个address对象,并且Address也引用了其他对象(例如,Home),那么当你序列化Employee对象的时候,所有其他引用对象,例如address和home将会被自动地被序列化。让我们来创建Address类,并它Address的对象作为引用,添加到employee类中。 |
enixyu
|
Employee.java:
在 org.arpit.javapostsforlearning 包中,创建Address.java Address.java:
在包
org.arpit.javapostsforlearning中,创建SerializeDeserializeMain.java
SerializeDeserializeMain.java:
运行它:
当你运行SerializeDeserializeMain.java。你会得到这样的结果:
我们将解释哪里出错了。我忘记了说,Address 类也必须是serializable。那么Address类必须继承serialzable接口。
Address.java:
再次运行:
当你再次运行SerializeDeserializeMain.java。你可以得到如下的结果
|
enixyu
|
案例2:如果我们不能访问引用对象的源代码(例如,你不能访问上面的Address类的源码) 如果我们不能访问到address类,那么我们该如何在Address类中实现serializable 接口?是否有另外的途径来实现呢?对的,你可以创建另外一个类,并继承Address,然后让它继承serializable 接口,但是对于下面的情况,这个方案会失败:
那么,我们该如何序列化Employee对象?解决的办法是,标记transient。如果你不需要序列化任何字段,只需把它标记为transient。
|
enixyu
|
案例3 - 如果我仍然需要保存引用对象的状态呢?(例如address对象) 如果你在反序列化过程中,标记了address为transient,它将会返回null结果。但是如果你仍然需要保存它的状态,你就需要序列化address对象。 Java序列化提供一个机制,如果你有特定签名的private方法,那么它们就会在序列化和反序列化过程中被调用,所以我们将重写Employee类的writeObject和readObject方法,然后它们就会在Employee对象序列化/反序列化过程中被调用。 Employee.java:
|
enixyu
|
在包org.arpit.javapostsforlearning 中创建Address.java
Address.java:
在包org.arpit.javapostsforlearning中创建SerializeDeserializeMain.java
SerializeDeserializeMain.java:
运行 :
当你运行SerializeDeserializeMain.java.你会得到如下的结果:
现在我们就得到了address对象的状态,就如它被序列化前的一样。
|
enixyu
| ||||||
其它翻译版本(1) |
序列化中的继承: 现在我们看看继承是如何影响序列化的。不管父类是不是可序列化,这将引出很多个例子。如果父类是非可序列化的,我们将如何处理,并且它是如何工作的。让我们看看例子。 我们将创建一个Person.java,作为 Employee的父类。 案例4: 如果父类是可序列化的 如果父类可序列化,那么所有的继承类将是可序列化的。 案例5: 如果父类为非可序列化呢? 如果父类为非可序列化的 ,那么我们的处理办法会很不一样。
Person.java
在包org.arpit.javapostsforlearning 中创建Employee.java Employee.java:
在org.arpit.javapostsforlearning包中创建SerializeDeserializeMain.java
SerializeDeserializeMain.java:
当你运行SerializeDeserializeMain.java后,你会得到如下的输出,如果父类是非可序列化的,那么在反序列化过程中,所有继承于父类的实例变量值,将会通过调用非序列化构造函数来初始化。 这里 name继承于person,所以在反序列化过程中,name将会被初始化为默认值。 |
enixyu
|
案例6 - 如果父类是可序列化,但你不需要继承类为可序列化 如果你不希望继承类为可序列化,那么你需要实现 writeObject() 和readObject() 方法,并且需要抛出NotSerializableException 异常。 案例7 - 可否序列化静态变量? 不能。因为静态变量是类级别的,不是对象级别的,当你序列化一个对象的时候,是不能序列化静态变量。 |
enixyu
|
总结:
|
enixyu
|
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
感谢http://www.oschina.net/translate/serialization-in-java