java自定义序列化的具体使用
作者:写不完作业还要玩
1.问题引出
在某些情况下,我们可能不想对于一个对象的所有field进行序列化,例如我们银行信息中的设计账户信息的field,我们不需要进行序列化,或者有些field本省就没有实现Serializable接口。
java中的序列化是递归序列化,也就是你的field的引用类型中也有field可以被序列化,那么就会在序列化当前对象的时候,一同序列化
2.解决办法
使用transient(瞬变现象;过往旅客;候鸟)关键字来修饰,该关键字只能修饰属性,这样在序列化的时候,这个属性就会用默认值,例如int类型用0,引用对象用null;
但是使用transient关键字修饰的field虽然简单方便,但是会被完全隔离在序列化机制之外,这样导致在反序列化回复java对象的时候,无法取得该field的值。
因此我们可以使用自定义序列化机制,可以让程序控制如何序列化各field,甚至完全不序列化某些field(这样就与transient相同),在序列化和反序列化过程中需要特殊处理的类应该提供如下特殊签名的方法,这些特殊的方法用以实现自定义的序列化
private void writeObject(java.io.ObjectOutputStream out) throws IOException; private void readObject(java.io.ObjectInputStream in)throws IOException,ClassNotFoundException; private void readObejctNoData()throws ObejctStreamException;
热爱你所写的每一行的代码
writeObject()方法负责写入特定类的实例状态,通过重写这个方法,程序员可以完全获得对序列化机制的控制,可以自主决定那些field需要序列化,需要怎么序列化,默认情况(函数体为空)该方法会调用out.defaultWriteObject来保存java对象的各field,从而达到实现序列化java对象状态的目的
readObject负责从流中读取并且回复对象的field,通过重写该方法,程序员,可以获得对反序列化机制的控制,对于反序列化各个field的顺序应该和序列化各个field的顺序相同。
至于当序列化流不完整时,readObjectNoData可以正确的初始化反序列化的对象,例如接收方接收到的序列化流残缺,或者序列化版本不同,则使用readObjectNoData来默认的初始化。
例子(对于person的改写):
class Person implements Serializable { private String name; private int age; public Person(String name,int age) { this.name=name; this.age=age; } //自动生成的Get和Set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } private void writeObject(ObjectOutputStream out) throws IOException { //将名字翻转之后写入二进制流 out.writeObject(new StringBuffer(this.name).reverse()); out.writeInt(this.age); } private void readObject(ObjectInputStream in)throws IOException,ClassNotFoundException { this.name=((StringBuffer)in.readObject()).reverse().toString(); //会抛出异常,因为这里的这样写法导致同 this.age=in.readInt(); } }
应该提醒的是,这个自定义的功能十分强大
另外一种替换性的改写:
//注意:这个方法由序列化机制调用,只要该方法存在就,它的访问控制符就可以为private protected package-private中的任意一种 private Object writeReplace() throws ObjectStreamException { ArrayList<Object> list=new ArrayList<>(); list.add(name); list.add(age); //我们这里返回ArrayList return list; }
序列化机制保证在序列化某个对象之前,先调用该对象的writeReplace方法,如果该方法返回另外一个java对象,系统就转换为序列化writeReplace的返回结果。(ps:如果这个返回结果也有writeReplace方法的话,就继续递归替代,直到没有替换)
相应与writeReplace相对的有一个readResolve方法,这个方法保护性的赋值整个对象,这里就不展开讨论了。
3.另外一种自定义序列化机制(介绍Externalizable)
Java还提供了另一种序列化机制,这种序列化方式完全由程序员决定存储和恢复对象数据。要实现该目标,Java类必须实现Externalizable接口,该接口里定义了如下两个方法。
- void readExternal(ObjectInput in):需要序列化的类实现readExternal()方法来实现反序列化。该方法调用DataInput(它是ObjectInput的父接口)的方法来恢复基本类型的Field值,调用ObjectInput的readObject()方法来恢复引用类型的Field值。
- void writeExternal(ObjectOutput out):需要序列化的类实现writeExternal()方法来保存对象的状态。该方法调用DataOutput(它是ObjectOutput的父接口)的方法来保存基本类型的Field值,调用ObjectOutput的writeObject()方法来保存引用类型的Field值。
具体的实现方式与上面自定义Serializable接口的实现类的序列化是相同的操作,这里就不阐述了,下面图是二者的比较。
到此这篇关于java自定义序列化的具体使用的文章就介绍到这了,更多相关java自定义序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!