序列化与反序列化

序列化:将对象转化为字节的过程称为序列化
反序列化:将字节转化成对象的过程称为反序列化

序列化需要的接口
java.io.Serializable、java.io.Externalizable、ObjectOutput、ObjectInput、ObjectInoutStream、ObjectOutputStream

对象的序列化保存的是对象的“状态”,即它的成员变量,由此可知,对象的序列化不会关注类中的静态变量。

除了在持久化对象时会用到对象序列化之外,当使用RMI(远程方法调用),或在网络中传递对象时,都会用到对象的序列化

对象的序列化和反序列化需要通过ObjectInputStream 和 ObjectOutputStream

java.io.Serializable

类通过实现java.io.Serializable 接口启动其序列化功能,未实现此接口的对象无法实现任何状态序列化或者反序列化。
可序列化类的所有子类型都是可以序列化的。序列化接口没有任何方法或者字段,仅用于标识可序列化的语义。
注意

  • 当试图对一个对象进行序列化的时候,如果遇到不支持 Serializable 接口的对象。在此情况下,将抛出NotSerializableException。
  • 如果要序列化的类有父类,要想同时将在父类中定义过的变量持久化下来,那么父类也应该集成java.io.Serializable接口。

java.io.Externalizable

  • 对实现了Externalizable 接口的类进行序列化及反序列化之后得到的对象的所有属性和值都变成了默认值。也就是说,之前对象的状态并没有被持久化下来,这也是java.io.Externalizable 和java.io.Serializable 的区别。
  • Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法。
  • 还有一点值得注意:在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器。

默认序列化

如果只是让某个类实现Serializable 接口,那么就是使用默认的序列化机制。默认序列化机制不仅会序列化当前对象本身,还会对该对象引用的其它对象也进行序列化,同样的,这些对象引用的其它对象也会被序列化,如果一个对象包含的成员变量是一个容器类对象,而这些容器包含的变量也是容器类对象,那么这个序列化的过程就会比较复杂,开销比较大。

影响序列化

有时候默认的序列化机制开销比较大,我们需要忽略掉某些数据。

  • 使用transient 关键字
    当某个数据使用transient关键字的时候,默认序列化机制就会忽略这个字段
  • writeObject 和 ReadObject 方法
    在实现Serialiable 接口的方法里添加这两个方法,重写这两个方法,自定义序列化。
    参考:http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html

单例模式和序列化

在实现Serializable 或Externalizable 接口的类中添加 readResolve(),在方法体中直接返回单例的那个对象。无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时, readResolve()方法都会被调用到。实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象, 而被创建的对象则会被垃圾回收掉。

序列化ID

每一个对象都有一个唯一的表示serialVersionUID。
虚拟机是否允许序列化与反序列化,不仅仅取决于类路径和类功能是否一致,还取决于序列化ID是否一致。
生成方式
1.默认生成1L
2.根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段 。
UID的默认值依赖JAVA编译器,对同一个类,不同编译器编译可能导致UID不同
使用场景
通过改变序列化 ID 可以限制某些用户的访问

父类的序列化

要想将父类对象也序列化,就需要让父类也实现Serializable 接口。如果父类不实现的话的,就 需要有默认的无参的构造函数。在父类没有实现 Serializable 接口时,虚拟机是不会序列化父对象的,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值。如果你考虑到这种序列化的情况,在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都是默认声明的值,如 int 型的默认是 0,string 型的默认是 null。

序列化存储规则

Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,上面增加的 5 字节的存储空间就是新增引用和一些控制信息的空间。反序列化时,恢复引用关系,使得清单 3 中的 t1 和 t2 指向唯一的对象,二者相等,输出 true。该存储规则极大的节省了存储空间。

序列化的作用

  • 网络中存储到本地磁盘需要序列化
  • 数据在网络中传输也需要序列化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值