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() {//默认构造函数必须有,而且必须是publicSystem.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 ;}@Overridepublic void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {System.out.println("调用readExternal()方法");s = (String)in.readObject();//在反序列化时,需要初始化s和i,否则只是调用默认构造函数,得不到s和i的值i = in.readInt();}@Overridepublic 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()方法如下:

@Overridepublic 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.OptionalDataExceptionat 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()方法如下:

@Overridepublic 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()方法

    @Overridepublic 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默认构造函数");
//    }
//    orBlip() {//默认构造函数必须有,而且必须是publicSystem.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 constructorat 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 constructorat 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(),在此方法中必须手动的恢复数据。

JAVA 对象序列化(二)——Externalizable相关推荐

  1. Java对象序列化详解6,Java对象的序列化与反序列化详解

    把对象转换为字节序列的过程称为对象的序列化,把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种途径: Ⅰ . 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中 Ⅱ.  在网 ...

  2. java 对象怎么序列化,java对象序列化总结

    java对象序列化小结 百度百科上介绍序列化是这样的: 序列化 (Serialization): 将对象的状态信息转换为可以存储或传输的形式的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储 ...

  3. 代码即财富之我学Java对象序列化与反序列化(2)

    2019独角兽企业重金招聘Python工程师标准>>> 我们在程序创建的Java对象都是存在于JVM内存中的,也就是Java对象的生命周期一定不会长于JVM,所以如何以一种持久化的方 ...

  4. 深入理解Java对象序列化

    关于Java序列化的文章早已是汗牛充栋了,本文是对我个人过往学习,理解及应用Java序列化的一个总结.此文内容涉及Java序列化的基本原理,以及多种方法对序列化形式进行定制.在撰写本文时,既参考了Th ...

  5. 理解Java对象序列化

    理解Java对象序列化 关于Java序列化的文章早已是汗牛充栋了,本文是对我个人过往学习,理解及应用Java序列化的一个总结.此文内容涉及Java序列化的基本原理,以及多种方法对序列化形式进行定制.在 ...

  6. Java对象序列化与反序列化

    什么是序列化与反序列化 当两个进程在进行远程通信时,彼此可以发送各种类型的数据.无论是何种类型的数据,都会以二进制序列的形式在网络上传送.发送方需要把这个Java对象转换为字节序列,才能在网络上传送: ...

  7. 关于 Java 对象序列化您不知道的 5 件事

    数年前,当和一个软件团队一起用 Java 语言编写一个应用程序时,我体会到比一般程序员多知道一点关于 Java 对象序列化的知识所带来的好处. 关于本系列 您觉得自己懂 Java 编程?事实上,大多数 ...

  8. java对象序列化去掉字段_使用序列化查找对象中的脏字段

    java对象序列化去掉字段 假设您正在开发一个将对象自动保存到数据库中的框架. 您需要检测两次保存之间所做的更改,以便仅保存已修改的字段. 如何检测脏场. 最简单的方法是遍历原始数据和当前数据,并分别 ...

  9. Java对象序列化的本机C / C ++类似性能

    您是否曾经希望过像使用C ++这样的本地语言将Java对象转换成字节流一样快的速度? 如果您使用标准的Java序列化,您可能会对性能感到失望. Java序列化的目的是与尽可能快而紧凑地序列化对象的目的 ...

  10. JSON用于多态Java对象序列化

    长期以来,JSON已成为客户端和服务器之间各种数据序列化的事实上的标准. 除其他外,它的优势是简单和易于阅读. 但是,简单起了一些限制,我今天要谈的其中一个限制是:存储和检索多态Java对象. 让我们 ...

最新文章

  1. nyquist图怎么画matlab,用MATLAB绘制Nyquist图
  2. c语言 图的存储邻接矩阵,数据结构之---C语言实现图的数组(邻接矩阵)存储表示...
  3. AngularJS 1.x 国际化——Angular-translate例子
  4. [转载] 杜拉拉升职记——06 预算与排期
  5. 转载:MySQL数据库INSERT、UPDATE、DELETE以及REPLACE语句的用法详解
  6. ROS2官网安装教程补充
  7. 利用pgAgent创建定时任务
  8. 去掉 iOS 导航栏返回按钮文本三种方案
  9. python 入门基础-如何学习Python,以及新手如何入门?
  10. 在线数据库链接字符串查询
  11. 数据库系统概论--课后习题
  12. 2021年A特种设备相关管理(电梯)考试题库及A特种设备相关管理(电梯)证考试
  13. 华为计算机怎么算根号,华为手机计算器的根号是什?
  14. 如何调整图片分辨率大小(适用于电脑/手机)
  15. [spark] spark推测式执行
  16. 刷脸支付便利更好推动普惠金融的落地
  17. Android面试总结-中高级
  18. IP电话系统和VoIP系统使用指南
  19. Linux 命令(208)—— ssh-keygen 命令
  20. python的分支语句if基本用法

热门文章

  1. GLUT及其函数的用法整理
  2. SQL-18 查找当前薪水(to_date='9999-01-01')排名第二多的员工编号emp_no、薪水salary、last_name以及first_name,不准使用order by...
  3. ALGO-185 Trash Removal
  4. zanePerfor 一款完整,高性能,高可用的前端性能监控系统,不要错过
  5. JavaEE Servlet 并发问题
  6. 这13个开源GIS软件,你了解几个?【转】
  7. 模拟简单计算器及比较大小
  8. Hypertext Transfer Protocol -- HTTP/1.1
  9. QT的第一个程序HELLO WORLD
  10. jsp中setAttribute与getAttribute方法使用介绍