对象序列化

当你创建对象时,只要需要就会一直存在,但是在程序终止时,无论如何都会继续存在。
Java的对象序列化将那些实现了Serializable接口的对象转换为一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。这意味着序列化机制能够自动弥补不同操作系统之间的差异。
利用对象序列化可以实现轻量级持久性,“持久性”意味着对象的生存周期并不取决于程序是否正在执行;它可以生存于程序的调用之间,通过将一个序列化对象写入磁盘,然后再重新调用程序是恢复该对象,就能够实现持久性的效果。之所以称为“轻量级”是因为不能用“persistent”关键字简单的定义一个对象,并让系统自动为维护其他细节问题。
对象序列化主要是支持两种主要特性:一是Java的远程方法调用,时在其他计算机上的对象使用起来就像是存活于本机上一样。当向远程对象发送消息时,需要通过对象序列化来传输参数和返回值。
对于Java Beans,对象的序列化也是必须的,使用一个Bean时,一般情况下是在设计阶段对他的状态信息进行配置,这种状态信息必须保存下来并在程序启动时进行后期恢复。
要序列化一个对象,首先要创建某些OutputStream对象然后将其封装在一个ObjectOutputStream对象内。只需要调用writeObject()即可将对象序列化并将其发送给OutputStream。要反向进行该过程需要将一个InputStream封装在ObjectInputStream内,然后调用ReadObject()。
对象序列化不仅保存对象“全景图”,而且能最总对象内所包含的所有引用,并保存那些对象,接着能对对象内包含的每个这样的引用进行追踪。单个对象可与之建立连接,而且还包含对象的引用数组和成员对象。下面这个例子通过对连接的对象生成一个worm对序列化机制进行测试。每个对象都与worm中的下一段链接,同时与属于不同类的对象引用数组链接:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Worm内的Data对象数组使用随机数组初始化的。每个Worm端都用一个char标记,该char是在递归生成连接的Worm列表时自动产生的。要创建一个Worm,必须告知构造器希望的长度。在产生下一个引用时要调用Worm构造器并将长度减1。组后一个next引用则为null,表示已到达Worm尾部。

  • 寻找类
    在这里插入图片描述
    该程序不但能捕获和处理异常,而且将异常抛出到main()方法之外,以便通过控制台产生报告。
    在这里插入图片描述
    打开文件和读取mystery对象中的内容都需要Alien的Class对象,java虚拟机找不到Alien.class。这样就会得到一个名为ClassNotFoundException异常。
  • 序列化的控制
    在一些特殊的情况下,可通过实现Externalizable接口代替实现Serializable接口,对序列化过程进行控制。这个Externalizable接口继承了Serializable接口,同时增添了两个方法:writeExternal()和readExternal()。这两个方法会在序列化和反序列化还原的过程中被自动调用。
    对于Serializable对象,对象完全以他存储的二进制位为基础来构造而不调用构造器。对于一个Externalizable对象,所有普通的默认构造器都会被调用,然后调用readExternal()。下面这个例子示范了如何完整保存和恢复一个Externalizable对象。
  • transient(瞬时)关键字
    当对序列化进行控制时,可能某个特定子对象不想让java的序列化机制自动保存与恢复。如果子对象表示我们不希望将其序列化的敏感信息,通常面临这种情况。既是对对象中的这些信息是private属性,经过序列化处理,可以通过读取文件或拦截网络传输的方式来访问到它。
    一种方法可以防止对象的敏感部分被序列化,就是将类实现为Externalizable,这样没有任何东西可以自动序列化,并且可以在writeExternal()内部只对所需部分进行显示的序列化。如果我们正在操作一个Serializable对象,则所有序列化操作都会自动进行。为了能够控制,可用transient(瞬时)关键字逐个字段关闭序列化。
    例如假设某个Login对象保存某个特定的登录会话信息,登录的合法性通过校验后,将数据保存下来,但不包括密码。为了做到,最简单的办法是实现Serializable,并将password字段标志为transient。
    在这里插入图片描述
    在这里插入图片描述
    其中的date和username域是一般的(不是transient),因此它们会被自动序列化。而password是transient的,因此不会被自动保存到磁盘上;另外自动序列化机制也不会尝试去恢复它。当对象被恢复时,password域会变成null。虽然toString()是用,重载后的+运算符连接String对象,但是null引用会被自动转换成字符串null。同时date字段被存储到了磁盘并从磁盘上被恢复出来,而且没有再重新生成。由于Externalizable对象在默认情况下不保存任何字段,所以transient关键字只能和Serializable对象一起使用。
    Externalizable的替代方法:
    可以实现Serializable接口,并添加writeObject()和readObject()方法。这样一旦对象被序列化或者被反序列化还原,就会自动分别调用这两个方法。只要提供这两个方法,就会使用他们而不是默认的序列化机制。
    这些方法必须具有准确的方法特征签名:
    在这里插入图片描述
    首先,我们可能会认为由于这些方法不是基类或者Serializable接口的一部分,所以应该在他们自己的接口中进行定义。但是他们被定义为private,这意味着仅能被这个类的其他成员调用。然而实际上并没从这个类的其他方法中调用他们,而是ObjectOutputStream和ObjectInputStream对象的writerObject()和readObject()方法调用对象的writeObject()和readObject()方法。
    在接口中定义的所有东西都自动是public的,因此如果writeObject()和readObject()必须是private的,那么不会是接口的一部分。因此必须完全遵循其方法特征签名,所以其效果就和实现了接口一样。
    在调用ObjectOutputStream.writeObject()时,会见超所传递的Serializable对象,看是否实现了他自己的writeObject()。如果是这样就跳过正常的序列化进程并调用它的writeObject()。在writeObject()内部可以调用defaultWriteObject()选择执行默认的writeObject()。类似的,在readObject()内部,可以调用defaultReadObject().
    下面这个例子延时如何对一个Serializable对象的存储与恢复进行控制:
    在这里插入图片描述
    该例子中,有一个String字段是普通字段,另一个是transient字段,来证明非transient字段由defaultWriteObject()方法保存,而transient字段必须在程序中明确保存和恢复。字段在构造器内部进行初始化,以此证实在反序列化还原期间没有被自动化机制初始化。
    在main()中,创建SerialCtl对象,然后将其序列化到ObjectOutputStream。序列化发生在下面这行代码中:
o.writeObject(sc)

writeObject()方法需检查sc,判断是否拥有自己的writeObject()方法。如果有则会使用它。对readObject()采用类似的方法。

  • 使用“持久性”
    如果将两个对象-都具有指向第三个对象的引用一一进行序列化,会发生什么?
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    我们可以通过一个字节数组使用对象序列化,从而实现对任何可Serializable对象的“深度复制”,意味着我们复制的是整个对象网,而不仅是基本对象及其引用。
    在main()方法中,创建一个Animal类别并将其两次序列化,分别送至不同的流。当其被反序列化还原并被打印时,可以看到所示的执行某次运行后的结果。在animals1和animals2中却出现了相同的地址,包括二者共享的那个指向House对象的引用。另一方面,当恢复animals3时,系统无法知道另一个流内的对象时第一个刘内的对象的别名,因此会产生出完全不同的对象网。
    只要将任何对象序列化到单一流中,就可以恢复出与我们写出时一样的对象网,并且没有任何意外重复复制出的对象。
    如果想保存系统状态,最安全的做法是将其作为“原子”操作进行序列化。如果序列化某些东西,再去做其他一些工作,再来序列化更多地东西,将无法安全的保存系统状态。取而代之的是,将构成系统状态的所有对象都置入单一容器内,并在一个操作中将该容器直接写出。然后同样只需要一次调用,即可将其恢复。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值