Java 升级之路(一)Java 序列化与反序列化


Java 升级之路(一)Java 序列化与反序列化序列化Java序列化Java反序列化更彻底的自定义序列化强制自定义序列化

序列化

抱砖引玉:

以上是一段简单的php序列化代码,输出为

php序列化后的字符串可以很容易看清楚参数和其赋值

Java序列化

序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。

现在我们利用java来实现序列化

创建Serialize.java来序列化(ObjectOutputStream类用来序列化一个对象),并将序列化结果存入Serialize.ser文件

步骤一:创建一个ObjectOutputStream输出流;

步骤二:调用ObjectOutputStream对象的writeObject输出可序列化对象

输出文件不能直接可读,利用xxd命令查看文件,查看输出结果:

通过对比,可以看出java和php序列化结果的一些异同。值得注意的是,Java序列化中对字段进行封装时,会按原始和非原始数据类型排序,其中又会按字段名再排序。

PS. 如果想让某个变量不被序列化,可使用transient修饰。

Java反序列化

恢复对象,反序列化读取的仅仅是Java对象的数据,而不是Java类,因此采用反序列化恢复Java对象时,必须提供该Java对象所属类的class文件,否则将会引发ClassNotFoundException异常

在刚才的Serialize.java目录下创建Unserialize.java

步骤一:读取序列化文件./Serialize.ser并创建一个ObjectInputStream输入流

步骤二:调用ObjectInputStream对象的readObject()得到序列化的对象。

输出为

 

更彻底的自定义序列化

writeReplace: 在序列化时,会先调用此方法,再调用writeObject方法。实际序列化的对象将是作为writeReplace方法返回值的对象

序列化内容:

 

readResolve: 反序列化时替换反序列化出的对象,反序列化出来的对象被立即丢弃。此方法在readeObject后调用。

序列化内容(最后的数据为test):

输出为cccc

 

PS. readResolvewriteReplace的访问修饰符可以是privateprotectedpublic,如果父类重写了这两个方法,子类都需要根据自身需求重写,这显然不是一个好的设计。通常建议对于final修饰的类重写readResolve方法没有问题;否则,重写readResolve使用private修饰。

强制自定义序列化

通过实现Externalizable接口,必须实现writeExternalreadExternal方法。

ExternalizableSerializable的一个子类,但不同于Serializable接口,实现此接口必须实现接口中的两个方法实现自定义序列化;同时还必须提供public的无参构造器,因为在反序列化的时候需要反射创建对象。

按顺序地写入public_int = 7777public_str = "test"

序列化内容(0x1e61 == 7777):

相比之前的序列化,明显可以看出这里并未写入字段名(public_intpublic_str),仅有其值。

下面是按顺序写入public_int = 7777, public_str = "test", public_int2 = 8888. 自定义只读取第一个写入的7777

此时序列化内容为( 0x1e61 == 7777 ;0x22b8 == 8888):

输出:

PS. 使用Externalizable时,必须按照写入时的确切顺序读取所有字段状态。否则会产生异常。