java序列化(转)

当我们需要序列化一个JAVA对象时需要实现Serializable接口。这个接口仅仅是一个tag接口,并不需要你真正实现一些方法,因为这个接口没有方法。他作用仅仅是告诉默认JAVA序列化工具,这个对象是可以序列化的。

1.serialVersionUID的作用
当我们的类实现了Serializable接口后,会有一个警告,告诉你需要生成一个serialVersionUID属性。这个serialVersionUID是做什么用的呢?其实这是JAVA序列化的版本控制功能。当序列化对象时会把这个属性写入,当反序列化时则会把这个属性取出,然后与JAVA类中的serialVersionUID属性值对比,如果一致,则认为是同一个版本,正常反序列化,如果不一致则认为版本不同,抛出InvalidClassException异常。

很多时候我们忽略这个警告,并不写这个serialVersionUID属性,但仍然可以正常序列化。那是因为如果没有这个属性,JVM将会根据这个类的属性和方法,计算出一个值作为serialVersionUID的值。这种做法会带来潜在的风险。不同的JVM产生serialVersionUID的算法可能会不一致,如果在不同的环境下产生的serialVersionUID不一致,将导致反序列化失败!

当一个类的结构发生变化,需要改变serialVersionUID,以通知序列化机制此类发生了变化,不兼容原来的版本了!其实并不是只要类结构变化,就必须更改serialVersionUID,JAVA的序列化机制提供了部分变化的兼容机制,有如下几种:
• 添加新的属性 反序列时发现没有此属性,则会赋予该属性默认的类型值
• 添加 writeObject/readObject 方法 因为此方法是用于自定义序列化,不影响序列化
• 删除 writeObject/readObject 方法 同上原因
• 改变属性的访问权限(private protected package public)
• 将一个属性从static变为nonstatic 或者 transient 或者 nontransient

如果你的类改动属于以上范围,默认的JAVA序列化机制可以保证兼容性,也就是说你不需要改动serialVersionUID值。
更多情况参见http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf 5.6.2 Compatible Changes

其实大部分情况下我们不需要深究哪些改动会影响兼容性。如果我们的序列化仅仅是用作缓存的话,我们可以简单处理,只要改动了类结构,即修改serialVersionUID值,抛弃原先的序列化结果,重新生成!

JAVA序列化过程
JAVA默认的序列化类是ObjectOutputStream,反序列化类是ObjectInputStream。
Java代码
1. public static void main(String[] args) throws Exception {
2.
3. Object o=new Object();
4.
5. try {
6. //序列化
7. FileOutputStream ostream = new FileOutputStream("t.txt");
8. ObjectOutputStream p = new ObjectOutputStream(ostream);
9. p.writeObject(o); // 序列化对象,在内部通过调用defaultWriteFields(Object obj, ObjectStreamClass desc)来序列化对象的所有属性。
10. p.flush();
11. ostream.close();
12. //反序列化
13. FileInputStream fis=new FileInputStream("t.txt");
14. ObjectInputStream istream=new ObjectInputStream(fis);
15. Object s=(ObjectSerializable) istream.readObject();//反序列化对象,内部调用defaultReadFields(Object obj, ObjectStreamClass desc)来反序列化所有属性
16. System.out.println(s);
17. istream.close();
18. fis.close();
19. } catch (IOException ioe) {
20. ioe.printStackTrace();
21. }
22. }


JAVA自定义序列化
自定义序列化根据定制程度的不同,有多种定制方案。
1.Externalizable定制
Externalizable接口继承了Serializable,其中有2个方法:
Java代码
1. void writeExternal(ObjectOutput out) throws IOException;
2. void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

通过实现Externalizable这个接口,我们可以定制需要序列化的属性。
Java代码
1. /*
2. *ObjectOutputStream在调用writeObject()方法时,会判断需要序列化的类是否继承了
3. *Externalizable接口,如果是,则会调用writeExternal(ObjectOutput out)执行我们
4. *自己写的序列化代码
5. */
6. p.writeObject(o);

Java代码
1. /*
2. *同理,ObjectInputStream在调用readObject()方法时,会判断需要反序列化的类是否继承
3. *了Externalizable接口,如果是,则会调用readExternal(ObjectInput in)执行我们自己
4. *写的反序列化代码
5. */
6. Object s=(ObjectSerializable) istream.readObject()

2.重写ObjectOutputStream/ObjectInputStream
实现Externalizable的方式自定义序列化非常方便,只需要在序列化类内部添加2个方法即可,不需要外部的任何要求。
但是如果我们需要更加深度的定制这还是不够的。Externalizable无法定制序列化对象本身的描述,只能定制对象内部属性的描述。
此时我们需要新建一个自己的序列化类来实现。
Java代码
1. /**
2. * 自定义序列化类
3. */
4. public class CustomObjectOutputStream extends ObjectOutputStream {
5.
6. private OutputStream cusOut;
7.
8. public CustomObjectOutputStream(OutputStream out) throws IOException{
9. /*
10. * 通过调用super()可以将父类的enableOverride设置为true
11. * 当调用父类的writeObject(obj)时,因为enableOverride=true,会调用writeObjectOverride(Object obj)方法
12. * 因此我们需要覆写writeObjectOverride(Object obj)如下所示
13. */
14. super();
15. cusOut=out;
16. }
17.
18. @Override
19. protected void writeObjectOverride(Object obj) throws IOException {
20. //自定义序列化方案
21. //cusOut.write(b)...
22. }
23.
24. }

Java代码
1. /**
2. * 自定义反序列化类
3. */
4. public class CustomObjectInputStream extends ObjectInputStream{
5.
6. private InputStream cusIn;
7.
8. public CustomObjectInputStream(InputStream in)throws IOException {
9. /*
10. * 通过调用super()可以将父类的enableOverride设置为true
11. * 当调用父类的readObject()时,因为enableOverride=true,会调用readObjectOverride()方法
12. * 因此我们需要覆写readObjectOverride()如下所示
13. */
14. super();
15. cusIn=in;
16. }
17.
18. @Override
19. protected Object readObjectOverride() throws IOException,
20. ClassNotFoundException {
21. //自定义反序列化方案
22. //cusIn.read() ...
23. return null;
24. }
25.
26. }

以上2段代码继承了ObjectOutputStream和ObjectInputStream,完全自定义了序列化方法。
既然是完全自定义序列化方法,其实完全没有必要去继承ObjectOutputStream和ObjectInputStream。
上面的代码仅仅适用于做序列化的适配器。其他序列化机制比如hessian,protobuf等,需要适配JAVA默认的序列化机制,则可采用以上的方法适配
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值