java之对象序列化概述
学习java有一段时间了,这几天学到java对象的序列化这部分的内容,所以自己总结了一下,希望能帮到那些刚接触java的新手,同时也希望各位java高手们多批评指教!大家共同进步!
首先,我先声明,这篇博文是参考以下链接:
http://www.cnblogs.com/xdp-gacl/p/3777987.html
http://developer.51cto.com/art/201202/317181.htm
在这,感谢两位博友的分享。在此基础上,我参看了帮助文档,对java的对象序列化做了简单的总结,主要
涉及以下方面:
1.什么是对象序列化
2.Serializable和Externalizable区别
3.serialVersionUID
话不多说,马山进入正题。
一.什么是对象序列化
主要以下几点:
1.对象序列化实际上就是将实现Serializable(或Externalizable)的对象以字节流的形式进行存取转发。
2.它主要是用在:永久保存对象、进程间通信、网络通信等地方。
3.序列化、反序列化双方必须要有相同的serialVersionUID,否则反序列化不成功。
4.除此之外,在发送方和接收方必须都有序列化过程中所用到的类。
5.利用ObjectOutputStream写入序列化对象,利用ObjectInputStream读取序列化对象
二.Serializable和Externalizable区别
Serializable:
首先,创建两个工程:对象序列化测试发送和对象序列化测试接收。
对象序列化测试发送:
Main类:
对象序列化测试接收:
Main类:
Person类和Sex枚举类两个工程中始终一样:
这个过程中,因为Serializable是一个空接口,所以Person直接实现就行,不必写什么方法就可以实现序化。
在两个工程的Main函数中,分别利用ObjectOutputStram.writeObject(Object obj) 和ObjectInputStream.readObject()
在指定的路径中写入、读出序列化对象。两个工程必须具有相同的Person和Person引用到的枚举类Sex。
运行时,分别运行两个工程中的Main方法。
输出结果为:
反序列化成功!
transient关键字:
由transient修饰的属性会变成瞬态,如何直接使用之前的方法,对象能反序列化成功,但是修饰的值会被赋为空值。
将Person类中的sex属性用transient修饰,其他不变,得到结果:
只实现Serializable接口的话,反序列化时,如果含有transient修饰的属性的话,能反序列化成功,但该属性被
赋为空值!
readObject和writeObjec:
这时,在Person总添加以下代码:
结果为:
对象序列化测试发送:
对象序列化测试接收:
transient不起作用了,因为对象中的writeObject把ObjectOutputStream的writeObject覆盖掉了,所以,就可以把sex属性也写入了。而readObject则是将写入的内容读出来。这里要注意一点:readObject读取对象的顺序要和writeObject写入对象的顺序一致。
writeReplace和readResolve:
接下来,再往Person加以下代码:
运行一下,得到以下结果:
对象序列化测试发送:
对象序列化测试接收:
由这个结果,我们可以得出:
发送(写入)时执行顺序: writeReplace->writeObject
接收(写出)时执行顺序:readObject->readResolve
下面,我们把readResolve部分代码注释掉:
运行结果:
为什么不是:
原因是,不管Main中写入什么对象,都被writeReplace返回的对象给替代了
如果把注释去掉,得到的结果却是:
这里我们可以知道,虽然写入的Person是"小红帽",但是读出来时被readResolve返回的对象给覆盖了!
至此,Serializable的内容讲的差不多了,总结一下,上面主要讲了,实现空Serializable时反序列化是什么情
况、实现Serializable时加入readObject和writeObject时是什么情况、实现时加入writeReplace和readResolve时是什
么情况!
Externalizable:
Externalizable是Serializable的子接口,但是它不是空的,它含有两个抽象方法:readExternal和writeExteal。
实现Externalizable时,必须实现这两个方法。此外,还要添加一个空构造方法。看一下代码:
对象序列化测试接收的Main方法要给对象加入空构造方法初始化。
结果:
对象序列化测试发送:
对象序列化测试发送:
有以上结果可以看出,写入运行顺序变为writeReplace->writeExternal;读出顺序:readObject->readExternal,忽略了readObject
和WriteObject方法。也就是说,Externalizable除了多了writeExternal和readExternal两个抽象方法,其他方面都跟Serializable差不
多。
三.serialVersionUID
序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中
用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的
serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导InvalidClassException
。
也就是说,它可以用作身份验证,确保序列化与反序列化前后的版本一致,从而准确反序列化。比如说:我们将
对象序列化测试发送和对象序列化测试接收中的serialVersionUID赋不同的值,如下:
对象序列化测试发送:
对象序列化测试接收:
反序列化结果:
反序列化失败!
正常情况下,如果不指定serialVersionUID是有很大的可能会反序列化失败的。因为,如果不指serialVersionUID
的话,java编译器会自动生成一个serialVersionUID的,然而,计算默认的 serialVersionUID 对类的详细信息具有较
高的敏感性,根据编译器实现的不同可能千差万别,所以,反序列化时极有可能会抛出InvalidClassException。所
以,强烈建议以:private static final long serialVersionUID=1L的形式指定一个serialVersionUID。