2012年10月18日星期四

Java对象序列化

什么是序列化

        序列化是指将一个对象转换为一个一连串的字节描述的过程。而反序列化是将这个一连串的字节数据转化为对象的过程。在一个分布式的环境中,经常需要将一个Object从一端传递到网络的另外一端,此时就需要在发送端序列化之后,经过网络将字节流传递到另一端,对端再将字节流转换为对象。

场景

持久化存储,将对象持久化为流存储到对象或缓存中;
远程过程调用,将序列化之后的流经过网络传输解析;而对开发人员来说,底层的控制协议都被屏蔽了,它们看到了一个在同一个虚拟机内调用的效果。

如何序列化对象

        只要实现java.io.Serializable接口的对象都可以被JVM序列化,java.io.Serializable是一个标记接口,没有定义任何方法,它只是为了声明一个对象是可以被序列化的。例如:
public class User implements Serializable{
  private long id;
  private String name;
}
Java中,ObjectOutputStream和ObjectInputStream负责序列化java对象,通过ObjectOutputStream的writeObject方法来将一个对象写入到流中,通过ObjectInputStream的readObject方法将一个对象从流中读出,例如:
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bo);
out.writeObject(test);
byte[] bytes = bo.toByteArray();
out.flush();
   
ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(bytes));
KryoTest s = (KryoTest)input.readObject();
AssertUtil.assertNotNull(s);
input.close();
默认对象所有的非静态和非瞬时的域都会被包含进来,而与域的声明没有关系。此时有两个办法来保证某些域不被序列化。第一种就是使用Transient关键字,将域声明为瞬时的,此时序列化将不会包含瞬时域。另外一种方式是通过serialPersistentFields来声明要被序列化的域,例如如果只序列化Person的name域:
private static final ObjectStreamField[] serialPersistentFields = { 
    new ObjectStreamField("name", String.class) 
};  

自定义序列化

自定义序列化是通过在对象中声明四个私有方法来完成的。这四个方法分别是:

private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
private Object readResolve() throws ObjectStreamException;
private Object writeReplace() throws ObjectStreamException;
通过writeObject和readObject方法可以自定义序列化逻辑,例如额外序列化一个字段等。
而通过writeReplace和readResolve方法可以在写对象前和读取流之后对操作对象做自定义修改。这四个方法的执行顺序是writeReplace->writeObject->readObject->readResolve

其他序列化工具

java的序列化性能一直都不被恭维。而当前市面上也有很多开源的序列化工具,例如kyro,protostuff等,这些开源的序列化工具的性能大大超过了java本身的序列化性能。
以kyro举例,将同一个对象序列化反序列化50000次,得出的耗时时间比:

Output output = new Output(1, 4096);
kryo.writeObject(output, test);
byte[] bb = output.toBytes();
output.flush();
   
Input input = new Input(bb);
KryoTest s = (KryoTest) kryo.readObject(input, KryoTest.class);
AssertUtil.assertNotNull(s);
input.close();
耗时时间:654:2056(ms)

没有评论:

发表评论