K:java中序列化的两种方式—Serializable或Externalizable
在java中,对一个对象进行序列化操作,其有如下两种方式:
第一种: 通过实现java.io.Serializable接口,该接口是一个标志接口,其没有任何抽象方法需要进行重写,实现了Serializable接口的类,其序列化过程是默认的,当然,也可以通过在该类中重写如下四个方法对序列化的过程进行控制:
0. private Object writeReplace() throws ObjectStreamException;
1. private void writeObject(java.io.ObjectOutputStream out) throws IOException;
2. private void readObject(java.io.ObjectInputStream in) throws Exception;
3. private Object readResolve() throws ObjectStreamException;
其中,方法0和方法1是序列化的过程中会进行调用的方法,方法2和方法3是反序列化过程中会进行调用的方法。其执行的顺序是按照以上方法的排列顺序从上到下执行的。
在进行序列化时候,其序列化类java.io.ObjectOutputStream会通过反射调用writeReplace()方法。此时,可以通过返回一个与本类兼容的对象(指的是该类的子类或者本类对象)的方式替换掉需要进行序列化的本类的对象(也就是this),使其序列化所返回的对象,需要注意的是,其返回的对象的类型是需要和所序列化的本类的类型兼容的,否则,其会报ClassCastException异常。在调用完writeReplace()方法之后,其会接着调用writeObject(ObjectOutputStream out)方法进行进一步的序列化操作。在该方法中,可以序列化额外的对象,也可以调用out.defaultWriteObject()进行默认的序列化操作,其中方法的参数out对象是其原先所创建的ObjectOutputStream对象。
对于反序列化的过程,其会先通过反序列化类ObjectInputStream对象去调用readObject(ObjectInputStream in)方法,在调用该方法的时候,其可以通过in对象进行额外的反序列化操作,也可以通过调用in.defaultReadObject()方法进行默认的反序列化操作,其中方法的参数in对象是原先所创建的ObjectInputStream对象。在调用完该方法之后,其会接着调用readResolve()方法,其方法的返回值为一个Object对象,该方法返回的对象将会代替反序列化的结果,直接将其作为反序列化的结果返回给上层调用ObjectInputStream对象readObject方法的结果。
演示代码如下:
package other.serial;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* 该类用于演示说明可序列化类中重写(override)
* writeReplace()方法、writeObject(ObjectOutputStream out)方法、readObject(ObjectInputStream in)方法、readResolve()方法
* 时,其进行序列化和可序列化的调用相关过程
* @author 学徒
*
*/
public class PersonSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private PersonSingleton(String name) {
this.name = name;
}
private static PersonSingleton person = null;
public static synchronized PersonSingleton getInstance() {
if (person == null)
return person = new PersonSingleton("cgl");
return person;
}
private Object writeReplace() throws ObjectStreamException {
System.out.println("1 write replace start");
return this;//可修改为其他对象
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
System.out.println("2 write object start");
out.defaultWriteObject();
//out.writeInt(1);
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
System.out.println("3 read object start");
in.defaultReadObject();
//int i=in.readInt();
}
private Object readResolve() throws ObjectStreamException {
System.out.println("4 read resolve start");
return PersonSingleton.getInstance();//不管序列化的操作是什么,返回的都是本地的单例对象
}
public static void main(String[] args) throws Exception {
FileOutputStream out = new FileOutputStream(new File("H://person.dat"));
ObjectOutputStream op = new ObjectOutputStream(out);
op.writeObject(PersonSingleton.getInstance());
op.close();
FileInputStream in = new FileInputStream(new File("H://person.dat"));
ObjectInputStream oi = new ObjectInputStream(in);
Object person = oi.readObject();
in = new FileInputStream(new File("H://person.dat"));
oi = new ObjectInputStream(in);
PersonSingleton person1 = (PersonSingleton) oi.readObject();
System.out.println("sington person hashcode:" + person.hashCode());
System.out.println("sington person1 hashcode:" + person1.hashCode());
System.out.println("singleton getInstance hashcode:" + PersonSingleton.getInstance().hashCode());
System.out.println("singleton person equals:" + (person == PersonSingleton.getInstance()));
System.out.println("person equals1:" + (person1 == person));
}
}
输出结果:
1 write replace start
2 write object start
3 read object start
4 read resolve start
3 read object start
4 read resolve start
sington person hashcode:13620718
sington person1 hashcode:13620718
singleton getInstance hashcode:13620718
singleton person equals:true
person equals1:true
第二种:
通过实现Externalizable接口,其中Externalizable接口继承自Serializable接口,实现Externalizable接口的类完全由类自身来控制序列化的行为(通过重写writeExternal()与readExternal()方法)
@代码改自:dovecat.iteye.com/blog/66044
package other.serial;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
/**
* 用于演示通过实现Externalizable接口进行序列化和反序列化过程的案例
* @author 学徒
*
*/
public class ExternalizableTest implements Externalizable
{
public ExternalizableTest()
{
System.out.println("反序列化的过程中进行调用");
}
private String message;
public String getFoo()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
private Object writeReplace() throws ObjectStreamException
{
System.out.println("writeReplace invoked");
return this;
}
private void writeObject(ObjectOutputStream out) throws IOException
{
System.out.println("writeObject invoked");
}
private void readObject(ObjectInputStream in) throws IOException
{
System.out.println("readObject invoked");
}
private Object readResolve() throws ObjectStreamException
{
System.out.println("readResolve invoked");
return this;
}
@Override
public void readExternal(ObjectInput arg0) throws IOException,
ClassNotFoundException
{
System.out.println("readExternal invoked");
Object obj = arg0.readObject();
}
@Override
public void writeExternal(ObjectOutput arg0) throws IOException
{
System.out.println("writeExternal invoked");
arg0.writeObject("Hello world");
}
public static void main(String[] args) throws IOException,
ClassNotFoundException
{
ExternalizableTest fooimpl = new ExternalizableTest();
fooimpl.serialize();
}
public Object serialize() throws IOException, ClassNotFoundException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
}
运行结果:
反序列化的过程中进行调用
writeReplace invoked
writeExternal invoked
反序列化的过程中进行调用
readExternal invoked
readResolve invoked
在此writeExternal和readExternal的作用与writeObject和readObject一样,且当同同时存在时候,只会调用writeExternal和readExternal方法。
序列化所需要的注意事项:
反序列化(实现Serializable接口的序列化对象)无需通过构造器初始化对象,实现Externalizable进行序列化时,需要提供一个无参的构造函数供反序列化的过程进行对象创建的调用
如果使用序列化机制向文件中写入了多个对象,那么取出和写入的顺序必须一致
Java对类的对象进行序列化时,若类中存在对象引用(且值不为null),也会对类的引用对象进行序列化(前提是该引用对象的类实现了序列化相关的接口,如果该对象值不为null且没有实现序列化相关的接口且其没有被transient关键字进行修饰时,在序列化时其会抛出java.io.NotSerializableException异常)
反序列化时必须要有序列化对象的类的class文件
当某个字段被声明为transient后,默认序列化机制就会忽略该字段。
如是一个类是可序列化的,那么它的子类也是可序列化的。
当我们同时实现了两个interface的时候,JVM只运行Externalizable接口里面的writeExternal和readExternal方法对序列化内容进行处理.
@汇总内容出处:http://blog.csdn.net/gitar520/article/details/7613122
Serializable接口和Externalizable接口各自的优缺点:
Serializable接口:
优点:内建支持
优点:易于实现
缺点:占用空间过大
缺点:由于额外的开销导致速度变比较慢
Externalizable接口:
优点:开销较少(程序员决定存储什么)
优点:可能的速度提升
缺点:虚拟机不提供任何帮助,也就是说所有的工作都落到了开发人员的肩上
Serializable和Externalizable的比较汇总:
| 序 号 | 区 别 | Serializable | Externalizable |
|---|---|---|---|
| 1 | 实现复杂度 | 实现简单,Java对其有内建支持 | 实现复杂,由开发人员自己完成 |
| 2 | 执行效率 | 所有对象由Java统一保存,性能较低 | 开发人员决定哪个对象保存,可能造成速度提升 |
| 3 | 保存信息 | 保存时占用空间大 | 部分存储,可以减少存储空间 |
关于序列化相关的更多问题,可以参看博文: http://blog.csdn.net/androiddevelop/article/details/17537841
K:java中序列化的两种方式—Serializable或Externalizable的更多相关文章
- Java中实现序列化的两种方式 Serializable 接口和 Externalizable接口
对象的序列化就是将对象写入输出流中. 反序列化就是从输入流中将对象读取出来. 用来实现序列化的类都在java.io包中,我们常用的类或接口有: ObjectOutputStream:提供序列化对象并把 ...
- java中创建多线程两种方式以及实现接口的优点
多线程创建方式有两种 创建线程的第一种方式.继承Thread类 1.继承Thread类 2.重写Thread类中的run方法--目的将自定义代码存储在run方法.让线程执行3.调用线程的start() ...
- 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口
//继承thread类 class PrimeThread extends Thread{ long minPrime; PrimeThread(long minPrime) { this.minPr ...
- java实现序列化的两种方式
1.Serializable接口 2.Externalizable接口 public class Demo2 implements Externalizable{ transient private ...
- 【转载】JAVA中线程的两种实现方法-实现Runnable接口和继承Thread类
转自: http://blog.csdn.net/sunguangran/article/details/6069317 非常感谢原作者,整理的这么详细. 在java中可有两种方式实现多线程,一种是继 ...
- Java中创建对象的几种方式
Java中创建对象的五种方式: 作为java开发者,我们每天创建很多对象,但是我们通常使用依赖注入的方式管理系统,比如:Spring去创建对象,然而这里有很多创建对象的方法:使用New关键字.使用Cl ...
- Java中创建对象的五种方式
我们总是讨论没有对象就去new一个对象,创建对象的方式在我这里变成了根深蒂固的new方式创建,但是其实创建对象的方式还是有很多种的,不单单有new方式创建对象,还有使用反射机制创建对象,使用clone ...
- 【转】Java中创建对象的5种方式
Java中创建对象的5种方式 作为Java开发者,我们每天创建很多对象,但我们通常使用依赖管理系统,比如Spring去创建对象.然而这里有很多创建对象的方法,我们会在这篇文章中学到. Java中有 ...
- 对Java代码加密的两种方式,防止反编译
使用Virbox Protector对Java项目加密有两种方式,一种是对War包加密,一种是对Jar包加密.Virbox Protector支持这两种文件格式加密,可以加密用于解析class文件的j ...
随机推荐
- jQuery开发自定义插件 $.extend()与$.fn.extend()
jQuery extend()和jQuery.fn.extend() jQuery提供两个用于封装扩展的方法: 1.$.extend(); 扩展jQuery类方法,即jQuery全局方法 (在全局可直 ...
- 守护进程VS守护线程
守护(daemon)进程 引入: join()方法可以使一个进程运行完之后再执行下一个进程,而daemon()方法就是主进程的代码执行完毕之后,不需要等待子进程,立即终止子进程. join()方法和d ...
- docker、oci、runc以及kubernetes梳理
容器无疑是近年来云计算中最火热的关键词.随着docker的大热,docker.oci.runc.containerd等等名词也逐渐传播开来.这么多的名词,也容易让人混淆.本文对相关名词和其之间的联系进 ...
- NGUI_Atlas
二.NGUI的图集制作: 1.概述: 将导入的图片资源全部制成一张图集,可以节约资源,当制成图集后,就可以将导入的图片资源进行删除, 再后续的操作直接使用图集中的图片即可,NGUI自带的Atlas M ...
- object-fit?
知道有这个属性存在,是一个很偶然的机会.有一天,设计部的一个小伙伴给了我一个网址,说很有个性,让我看一下,当发现一个很有意思的效果时,作为一个前端小兵的我当然是第一时间开始审查元素,然后看到了这个从没 ...
- 轻松上云,从容实施Office 365项目
这个是我在MVP 社区活动的一节课程,讲述Office 365部署中一些大的挑战和解决的方法 视频URL 例如以下: http://edu.51cto.com/lesson/id-17440.html ...
- char a[] = "ab\0123\098"; 求a的长度
原因: \0表示后面的字符是八进制(\ddd); 8进制=10进制( 10是'\n' 的ASCII码): 当\0后面有数字,且数字范围在0~7之间时,为8进制转义.如'\012': 当\0后面没有 ...
- Express4.x API (一):application (译)
Express4.x API 译文 系列文章 Express4.x API (一):application (译) -- 完成 Express4.x API (二):request (译) -- 完成 ...
- 三、spring cloud 服务提供与调用
如何使用eureka服务注册中心,搭建一个简单的服务端注册服务,客户端去调用服务使用. 案例中有三个角色:服务注册中心.服务提供者.服务消费者,eureka单机版启动既可,流程是首先启动注册中心,服务 ...
- (转)java中的方法区
首先要说明的是,此文章转载自 http://blog.csdn.net/zzhangxiaoyun/article/details/7518917 谢谢作者.是本人认为对方法区解释得比较清楚的一篇文章 ...