序列化是什么?
一个Java对象的表示有各种各样的方式,Java本身也提供给了用户一种表示对象的方式,那就是序列化。
换句话说,序列化只是表示对象的一种方式而已。
序列化
:将一个对象转换成一串二进制表示的字节数组,通过保存或转移这些字节数据来达到持久化的目的。
反序列化
:将字节数组重新构造成对象
序列化对象写入文件后,可以从文件中读取并反序列化。
整个过程独立JVM,意味着一个对象可以在一个平台上序列化,并在完全不同的平台上反序列化。
类实现序列化需要满足两个条件
- 类必须实现
java.io.serializable
接口 - 类中的所有字段都必须是可序列化的。如果字段不可序列化,则必须将其标记为
transient
如果你想知道Java标准类是否可序列化,请检查类的文档。测试很简单,如果类实现java.io.serializable
接口,那么它是可序列化的;否则,它不是。
序列化对象
ObjectOutputStream
类用于序列化一个对象
这里用Person
对象来演示:
Person实体类:
序列化测试:
将对象序列化存储到文件中
反序列化对象
ObjectInputStream
类用于序列化一个对象,将对象从文件中反序列化出。
Ps: 当然这个地方需要注意类型转换。
为了演示方便,我们就对上文中已经被序列化的对象反序列化,
反序列化对象测试:
Console控制台:
这是你可能会有疑惑,为什么李如花
的age
是0,而不是18。这个时候就要说说transient
的作用了。
transient是什么?
被声明为transient
的属性不会被序列化,所以上文中person
对象在序列化的时候就没有存储age
这个属性,所以在反序列化时age属性值为默认初始值0。
serialVersionUID是什么?
Java序列化机制是通过在运行时判断类的serialVersionUID
来验证版本一致性的。
在进行反序列化,Java虚拟机会把传过来的字节流中的servialVersionUID
和本地相应实体类的servialVersionUID
进行比较,
如果相同就认为是一致的实体类,可以进行反序列化,否则JVM会拒绝对这个实体类进行反序列化并抛出异常。
serialVersionUID的生成方式
- 默认的1L
- 根据类名、接口名、成员方法以及属性等来生成一个64位的Hash字段
如果实现 java.io.Serializable
接口的实体类没有显式定义一个名为serialVersionUID
、类型为long的变量时,Java序列化 机制会根据编译的.class文件自动生成一个serialVersionUID
,如果.class
文件没有变化,那么就算编译再多 次,serialVersionUID
也不会变化。
序列化会保存哪些信息
- 序列化保存的是对象的状态
- 不会保存静态属性
- 不会保存对象的方法
什么样的类可以被序列化
要成功序列化类,必须满足两个条件:
- 类必须实现
java.io.serializable
接口 - 类中的所有字段都必须是可序列化的。如果字段不可序列化,则必须将其标记为
transient
父类序列化情况
- 当父类实现了
Serializable
接口时,所有子类都可以被序列化。 - 当一个对象的实例变量引用其他对象时,序列化该对象时也会把引用对象进行序列化。
- 子类实现了
Serializable
接口,父类没有,父类中的属性不能序列化(不报错,数据丢失),但是在子类中属性仍能正确序列化。 - 反序列化时,如果对象的属性有修改或删减,则修改的部分属性会丢失,但不会报错
- 反序列化时,如果serialVersionUID被修改,则反序列化时会失败
参考文章: