序列化概念
序列化的概念,可以简单理解为对象 –> 字节的过程,同理,反序列化则是相反的过程。为什么需要序列化?因为网络传输只认字节。所以互信的过程依赖于序列化。一般用于网络传输或者数据存储。其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。
序列化和反序列化步骤
序列化:
步骤一:创建一个对象输出流,它可以包装一个其它类型的目标输出流,如文件输出流:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(“目标地址路径”));
步骤二:通过对象输出流的writeObject()方法写对象:
out.writeObject("Hello");
out.writeObject(new Date());
反序列化:
步骤一:创建一个对象输入流,它可以包装一个其它类型输入流,如文件输入流:
ObjectInputStream in = new ObjectInputStream(new fileInputStream(“目标地址路径”));
步骤二:通过对象输出流的readObject()方法读取对象:
String obj1 = (String)in.readObject();
Date obj2 = (Date)in.readObject();
说明:为了正确读取数据,完成反序列化,必须保证向对象输出流写对象的顺序与从对象输入流中读对象的顺序一致。
注册时为什么要指定int型id
当Kryo输出一个对象的实例时,它首先会输出该对象的Class的标识符。 默认情况下,类的完全限定名称(如org.springframework.util.StringUtils
)会被写入,后面紧接的是对象的字节。 随后出现的是该对象图中的对象类型,它们通过可变长度的整数表示。 将类名写入序列化文件是有点效率低下的,因此类可以事先注册,从而避免写入类名:
Kryo kryo = new Kryo();
kryo.register(SomeClass.class);
// ...
Output output = ...
SomeClass someObject = ...
kryo.writeObject(output, someObject);
这里SomeClass的注册到了Kryo,该类与一个int型的ID相互关联。 当Kryo输出SomeClass实例时,它会输出这个整数ID。 这样比输出类的名称更高效,但要求被序列的类在序列化之前知道。 反序列化过程中,注册类id必须和序列化过程中的该类的id一致。 上面显示的注册方法分配下一个可用,最小的整数ID,这就意味着类的注册顺序非常重要。 该ID也可以明确指定,从而使顺序不重要:
Kryo kryo = new Kryo();
kryo.register(SomeClass.class, 0);
kryo.register(AnotherClass.class, 1);
kryo.register(YetAnotherClass.class, 2);
该ID很小时特别是正整数时,是非常高效的。 负整数ID不能高效的序列化。 -1和-2是保留的,不能使用。
已注册和未注册的类可以混合使用。 所有的基本数据类型,基础对象和String是默认注册的。
如果setRegistrationRequired可以设置为true,那么任何未注册的类在序列化和反序列化都会抛出一个异常。 这样可以防止一个应用程序意外地使用类名的字符串。
如果使用未注册的类,缩短包名是一种优化方案。
serialVersionUID作用
它决定着是否能够成功反序列化!简单来说,java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。
-
序列化ID如何产生:
当我们一个实体类中没有显示的定义一个名为“serialVersionUID”、类型为long的变量时,Java序列化机制会根据编译时的class自动生成一个serialVersionUID作为序列化版本比较,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID。譬如,当我们编写一个类时,随着时间的推移,我们因为需求改动,需要在本地类中添加其他的字段,这个时候再反序列化时便会出现serialVersionUID不一致,导致反序列化失败。那么如何解决呢?便是在本地类中添加一个“serialVersionUID”变量,值保持不变,便可以进行序列化和反序列化。 -
总结:
虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是private static final long serialVersionUID = 1L
)。