java 序列化初探
参考资料:
http://www.ibm.com/developerworks/cn/java/j-5things1/index.html?ca=drs-cn-0504 以及文中的参考资料
java 核心技术 高级卷第一章----序列化技术
http://www.java2s.com/Code/Java/File-Input-Output/Reconstructinganexternalizableobject.htm 以及其中的相关序列化知识
最后给出一个简单的序列化工具类:
http://www.java2s.com/Code/Java/File-Input-Output/SerializationUtilities.htm
java 序列化前提: Serilizable接口的实现.
1 序列化一个对象(记为obj1),再读取该对象(记为obj2), 两个对象obj1 不等于(引用) obj2 . 并且obj2的构造并不调用构造函数. 在java 核心技术2中
作者提到,可以将obj2看做是obj1的一个深拷贝.
2 如果有一段代码是这样的:
Employee lily = new Employee("Lily")
Manager tom = new Manager("Tom");
Manager jack = new Manager("Jack");
//两个经理吃乐谱同一个秘书的引用
tom.secretary = lily;
jack.secretary = lily;
序列化以后再一次读出tom 和 jack
经过测试, 两者持有的秘书依然是同一个引用,但是和序列化前的lily并不是同一个引用.
3 序列化并不会序列transient字段和static字段.
4 超类的默认序列化: 序列化一个类的时候,默认会序列化其超类的属性(除transient和static意外的属性), 反序列化的时候,如果是实现了Externalizable.类的构造是通过类自身的空的构造函数来实现的(超类的构造也是通过超类自身的构造函数).否则是不会调用子类或者超类的构造函数的.
5 自定义的序列化(方法1): readObject方法和writeObject方法的实现
String name;
Employee(String name){
this.name = name;
}
private void writeObject(ObjectOutputStream obj){
//空的实现
}
private void readObject(ObjectInputStream obj){
//空的实现
}
当去序列化上面的类时候, 会保存一些相关的类信息,但是不会去序列化name.
实现readObject方法如下:
private void readObject(ObjectInputStream obj){
name = "readObject";
}
反序列化这个类后, name属性将会被赋值为"readObject';
一般来说,可以使用下面的代码来参考自己的序列化:
private void writeObject(ObjectOutputStream obj)throws IOException{
obj.defaultWriteObject();
//然后做自己的扩展序列化
}
private void readObject(ObjectInputStream obj) throws IOException,ClassNotFoundException{
obj.defaultReadObject();
//然后做自己的拓展反序列化
}
其实关于readObject和writeObject的方法实现可以参考JDK ArrayList,因为它的内置数组元素是transient的, 可以看看它是如何实现ArrayList的序列化.
注意点: 在实现了Serilizable接口,然后实现了private void readObject和private void writeObject两个方法的前提下, readObject和writeObject只需要关心
本类的属性序列/反序列化. 而不用去考虑超类的序列化.
5 序列化方法(2) : 实现Externalizable接口并实现public readExternal()方法和public writeExternal()方法
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
// TODO Auto-generated method stub
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
// TODO Auto-generated method stub
}
注意,这种方法在反序列化的时候是会去调用类的默认构造函数来构造类的,如果此时将类的默认构造函数的可见性设置为包默认或者其他序列化机制不能
调用的访问方式,会抛出:java.io.InvalidClassException:************* no valid constructor 异常;
在上面的空实现中, 序列化机制既不会去序列化任何本类域或者超类的域, 只是去记录一些属性的说明类型信息. 这和实现Serializable接口重写readObject
和writeObject的自定义序列化机制是不一样的. 后者在调用readObject和writeObject之前就对超类进行了必要的处理.
6 注意特殊形式的序列化:
//注意下述的形式,大部分的情况是这个类不会提供public的构造函数, 并且这个类属于"枚举类型". 这种方式下,默认的序列化机制是不合适的,或者
说这种方式可能并不合适去序列化.
public class TestSpecialClass implements Serializable{
static TestSpecialClass specialOne = new TestSpecialClass("one");
static TestSpecialClass specialTwo = new TestSpecialClass("two");
要实现上述情况的序列化, 可以实现下面的方法:
prtected Object readResolve() throws ObjectStreamException;