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(),在此方法中必须手动的恢复数据。
Externalizable接口 序列化的更多相关文章
- Java中实现序列化的两种方式 Serializable 接口和 Externalizable接口
对象的序列化就是将对象写入输出流中. 反序列化就是从输入流中将对象读取出来. 用来实现序列化的类都在java.io包中,我们常用的类或接口有: ObjectOutputStream:提供序列化对象并把 ...
- Java基础(十一)--Serializable和Externalizable接口实现序列化
序列化在日常开发中经常用到,特别是涉及到网络传输的时候,例如调用第三方接口,通过一个约定好的实体进行传输,这时你必须实现序列 化,这些都是大家都了解的内容,所以文章也会讲一下序列化的高级内容. 序列化 ...
- 如何使用Externalizable接口自定义Java中的序列化
Java序列化过程的缺点 我们都知道如何使用Serializable接口序列化/反序列化一个对象,并且如何使用writeObject 和readObject方法自定义序列化过程. 但是这些自定义还不够 ...
- [转]Serializable接口与Externalizable接口区别
被Serializable接口声明的类的对象的内容都将被序列化,如果现在用户希望自己指定序列化的内容,则可以让一个类实现Externalizable接口,此接口定义如下: public interfa ...
- Android Studio酷炫插件(一)——自动化快速实现Parcelable接口序列化
https://blog.csdn.net/kroclin/article/details/40902721 一.前言相信数据序列化大家都多多少少有接触到,比如自定义了一个实体类,需要在activit ...
- 【FAQ】调用接口序列化问题
问题: Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException:Can not ...
- Android Studio自动化快速实现Parcelable接口序列化
1.在线安装 然后打开File -> Settings -> Pugins -> Browse Repositories 如下,输入android parcelable code g ...
- SrpingMVC/SpringBoot中restful接口序列化json的时候使用Jackson将空字段,空字符串不传递给前端
笔者的JSON如下: { "code": 10001, "message": "成功", "nextUrl": null ...
- Java中的文件和流相关知识
1. File File类可以使用文件路径字符串来创建File实例,该文件路径可以是绝对路径或相对路径 File类的list()方法中可以接收一个FilenameFilter参数,通过该参数可以只列出 ...
随机推荐
- 关于python3 OpenCV的安装和配置
开发环境,win7 32bit, Anaconda3.之前尝试pip安装,但是总是不成功.后来,交流群里面废帝大神让我用conda安装,之后按照conda install --channel http ...
- linux下vi命令
Vi共分三种模式,分别是“一般模式”.“编辑模式”与“命令行命令模式”. 1.一般模式:vi处理文件时,一进入该文件就是一般模式.在这个模式中,可以使用“上下左右”键来移动光标,可以使用“删除字符”或 ...
- select2美化下拉单
http://www.51xuediannao.com/js/jquery/select2.html http://www.51xuediannao.com/demo.php
- Inno Setup 安装、卸载前检测进程或服务
[转载]Inno Setup 安装.卸载前检测进程或服务 (2015-04-24 17:37:20) 转载▼ 标签: 转载 原文地址:Inno Setup 安装.卸载前检测进程或服务作者:一去丶二 ...
- 五、CCNode
本将主要介绍下CCNode这个类,CCNode是所有节点的基类,其中包括我们常用的CCScene(场景).CCLayer(图层).CCSprite(精灵)等,它是一个不能够可视化显示的抽象类,只是用来 ...
- MySQL表名、列名区分大小写详解
前言:出现的问题 在本地数据库上执行修改银行卡没有报错 但线上执行报错 发现是表找不到,发现表名不对应该是card_cardinfo,但本地上没有问题,能正常修改,然后在数据库里测试,发现本地库(wi ...
- Python CSV模块处理文件读写
下面是一个简单的csv文件 Title,Release Date,Director And Now For Something Completely Different,1971,Ian MacNau ...
- JavaScript中指针和地址理解
个人理解:指针只是指向内存的一个索引:而地址则是内存中确切的位置. 下面是函数中关于指针和地址一个小例子: function sum(num1,num2){ return num1+num2; } a ...
- poj 1475 uva 589 - Pushing Boxes
题目大意 人推箱子从起点到终点,要求推箱子的次数最少,并打印出来人移动的路径. 题目分析 对于箱子进行宽搜的同时,要兼顾人是否能够把箱子推到相应的位置 每一次对箱子bfs 然后对人再bfs #incl ...
- ubuntu 状态栏不显示时间
有时候我们会看到我们电脑的状态栏那里并没有显示时间,一个原因是日期时间指示器没有工作,另一个可能的原因是用户禁用了时间显示. 方法一: 首先我们用下面的命令来确认一下是否安装了日期时间指示器: sud ...