zoukankan      html  css  js  c++  java
  • Externalizable接口 序列化

    Java默认的序列化机制非常简单,而且序列化后的对象不需要再次调用构造器重新生成,但是在实际中,我们可以会希望对象的某一部分不需要被序列化,
    或者说一个对象被还原之后, 其内部的某些子对象需要重新创建,从而不必将该子对象序列化。 在这些情况下,我们可以考虑实现Externalizable接口从而代替Serializable接口
    来对序列化过程进行控制 (后面我们会讲到一个更简单的方式,通过transient的方式)。 Externalizable接口extends Serializable接口,而且在其基础上增加了两个方法:writeExternal()和readExternal()。
    这两个方法会在序列化和反序列化还原的过程中被自动调用, 以便执行一些特殊的操作。
    package java.io; import java.io.ObjectOutput; import java.io.ObjectInput; public interface Externalizable extends java.io.Serializable { void writeExternal(ObjectOutput out) throws IOException; void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; } 下面这段代码示范了如何完整的保存和恢复一个Externalizable对象 package test.serializable; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; public class Blip implements Externalizable { private int i ; private String s;//没有初始化 public Blip() { //默认构造函数必须有,而且必须是public System.out.println("Blip默认构造函数"); } public Blip(String s ,int i) { //s,i只是在带参数的构造函数中进行初始化。 System.out.println("Blip带参数构造函数"); this.s = s; this.i = i; } public String toString() { return s + i ; } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { System.out.println("调用readExternal()方法"); s = (String)in.readObject();//在反序列化时,需要初始化s和i,否则只是调用默认构造函数,得不到s和i的值 i = in.readInt(); } @Override public void writeExternal(ObjectOutput out) throws IOException { System.out.println("调用writeExternal()方法"); out.writeObject(s); //如果我们不将s和i的值写入的话,那么在反序列化的时候,就不会得到这些值。 out.writeInt(i); } } package test.serializable; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class ExternalizableTest { /** * @param args * @throws IOException * @throws ClassNotFoundException */ public static void main(String[] args) throws IOException, ClassNotFoundException { System.out.println("序列化之前"); Blip b = new Blip("This String is " , 47); System.out.println(b); System.out.println("序列化操作,writeObject"); ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(out); oos.writeObject(b); System.out.println("反序列化之后,readObject"); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream ois = new ObjectInputStream(in); Blip bb = (Blip)ois.readObject(); System.out.println(bb); } }运行结果如下所示: 序列化之前 Blip带参数构造函数 This String is 47 序列化操作,writeObject 调用writeExternal()方法 反序列化之后,readObject Blip默认构造函数 调用readExternal()方法 This String is 47分析结果: 在Blip类中,字段s和i只在第二个构造器中初始化,而不是在默认的构造器其中初始化的,每次writeObject时,都会调用WriteExtenal()方法,
    而在WriteExtenal() 方法中我们需要将当前对象的值写入到流中;而每次readObject()时,调用的是默认的构造函数,如果我们不在 readExternal()
    方法中初始化s和i,那么s就会为null,而i就会为0。 下面分几种情况讨论:
    1) 如果我们只修改writeExternal()方法如下: @Override public void writeExternal(ObjectOutput out) throws IOException { System.out.println("调用writeExternal()方法"); // out.writeObject(s); // out.writeInt(i); }那么运行的结果为: 序列化之前 Blip带参数构造函数 This String is 47 序列化操作,writeObject 调用writeExternal()方法 反序列化之后,readObject Blip默认构造函数 调用readExternal()方法 Exception in thread "main" java.io.OptionalDataException at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1349) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351) at test.serializable.Blip.readExternal(Blip.java:34) at java.io.ObjectInputStream.readExternalData(ObjectInputStream.java:1792) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1751) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351) at test.serializable.ExternalizableTest.main(ExternalizableTest.java:28)
    原因是因为,我们在ObjectOutPutStream中没有writeObject, 而在ObjectInputStream中readObject导致的
    2)如果我们修改writeExternal()方法如下: @Override public void writeExternal(ObjectOutput out) throws IOException { System.out.println("调用writeExternal()方法"); out.writeObject("自己定义的"); out.writeInt(250); }那么运行的结果为: 序列化之前 Blip带参数构造函数 This String is 47 序列化操作,writeObject 调用writeExternal()方法 反序列化之后,readObject Blip默认构造函数 调用readExternal()方法 自己定义的250看见没,反序列化后得到的s和i是我们在writeExternal()中自定义的数据 3) 如果我们只是修改readExternal()方法 @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { System.out.println("调用readExternal()方法"); // s = (String)in.readObject(); // i = in.readInt(); }那么运行的结果为: 序列化之前 Blip带参数构造函数 This String is 47 序列化操作,writeObject 调用writeExternal()方法 反序列化之后,readObject Blip默认构造函数 调用readExternal()方法 null0 看见没?最后一行打印的是null0,说明没有对s和i进行初始化。 4)如果我们删除Blip的默认构造函数,或者将其权限不设置为public // public Blip() { // //默认构造函数必须有,而且必须是public // System.out.println("Blip默认构造函数"); // } // or Blip() { //默认构造函数必须有,而且必须是public System.out.println("Blip默认构造函数"); }运行结果如下: 序列化之前 Blip带参数构造函数 This String is 47 序列化操作,writeObject 调用writeExternal()方法 反序列化之后,readObject Exception in thread "main" java.io.InvalidClassException: test.serializable.Blip; test.serializable.Blip; no valid constructor at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:713) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1733) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351) at test.serializable.ExternalizableTest.main(ExternalizableTest.java:28) Caused by: java.io.InvalidClassException: test.serializable.Blip; no valid constructor at java.io.ObjectStreamClass.<init>(ObjectStreamClass.java:471) at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:310) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1106) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326) at test.serializable.ExternalizableTest.main(ExternalizableTest.java:24) 在反序列化时,会出现无效的构造函数这个错误,可见必须有权限为public的默认的构造器(如果有非默认的带参数的构造函数,那么必须显示的写出默认的构造函数, 如果没有非默认的构造函数,那么默认构造函数可以不显示的写出来),才能使Externalizable对象产生正确的行为。 总结Externalizable对象的用法: 与Serizable对象不同,使用Externalizabled,就意味着没有任何东西可以自动序列化, 为了正常的运行,我们需要在writeExtenal()方法中将自对象的重要信息写入,从而手动的完成序列化。对于一个Externalizabled对象,
    对象的默认构造函数都会被调用 (包括哪些在定义时已经初始化的字段),然后调用readExternal(),在此方法中必须手动的恢复数据。
  • 相关阅读:
    net core 使用 rabbitmq
    asp.net core WebApi 返回 HttpResponseMessage
    asp.net core 2.1 WebApi 快速入门
    JQuery EasyUI combobox动态添加option
    php截取字符去掉最后一个字符
    JQuery EasyUI Combobox的onChange事件
    对于不返回任何键列信息的 selectcommand 不支持 updatecommand 的动态 sql 生成
    Access2007 操作或事件已被禁用模式阻止解决办法
    Easyui 中 Tabsr的常用方法
    Win 7 IE11不能下载文件,右键另存为也不行
  • 原文地址:https://www.cnblogs.com/shaohz2014/p/3923973.html
Copyright © 2011-2022 走看看