在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方法的结果。

演示代码如下:

@代码改自:http://www.cnblogs.com/yoohot/p/6019767.html

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方法。

序列化所需要的注意事项:

  1. 反序列化(实现Serializable接口的序列化对象)无需通过构造器初始化对象,实现Externalizable进行序列化时,需要提供一个无参的构造函数供反序列化的过程进行对象创建的调用

  2. 如果使用序列化机制向文件中写入了多个对象,那么取出和写入的顺序必须一致

  3. Java对类的对象进行序列化时,若类中存在对象引用(且值不为null),也会对类的引用对象进行序列化(前提是该引用对象的类实现了序列化相关的接口,如果该对象值不为null且没有实现序列化相关的接口且其没有被transient关键字进行修饰时,在序列化时其会抛出java.io.NotSerializableException异常)

  4. 反序列化时必须要有序列化对象的类的class文件

  5. 当某个字段被声明为transient后,默认序列化机制就会忽略该字段。

  6. 如是一个类是可序列化的,那么它的子类也是可序列化的。

  7. 当我们同时实现了两个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的更多相关文章

  1. Java中实现序列化的两种方式 Serializable 接口和 Externalizable接口

    对象的序列化就是将对象写入输出流中. 反序列化就是从输入流中将对象读取出来. 用来实现序列化的类都在java.io包中,我们常用的类或接口有: ObjectOutputStream:提供序列化对象并把 ...

  2. java中创建多线程两种方式以及实现接口的优点

    多线程创建方式有两种 创建线程的第一种方式.继承Thread类 1.继承Thread类 2.重写Thread类中的run方法--目的将自定义代码存储在run方法.让线程执行3.调用线程的start() ...

  3. 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口

    //继承thread类 class PrimeThread extends Thread{ long minPrime; PrimeThread(long minPrime) { this.minPr ...

  4. java实现序列化的两种方式

    1.Serializable接口 2.Externalizable接口 public class Demo2 implements Externalizable{ transient private ...

  5. 【转载】JAVA中线程的两种实现方法-实现Runnable接口和继承Thread类

    转自: http://blog.csdn.net/sunguangran/article/details/6069317 非常感谢原作者,整理的这么详细. 在java中可有两种方式实现多线程,一种是继 ...

  6. Java中创建对象的几种方式

    Java中创建对象的五种方式: 作为java开发者,我们每天创建很多对象,但是我们通常使用依赖注入的方式管理系统,比如:Spring去创建对象,然而这里有很多创建对象的方法:使用New关键字.使用Cl ...

  7. Java中创建对象的五种方式

    我们总是讨论没有对象就去new一个对象,创建对象的方式在我这里变成了根深蒂固的new方式创建,但是其实创建对象的方式还是有很多种的,不单单有new方式创建对象,还有使用反射机制创建对象,使用clone ...

  8. 【转】Java中创建对象的5种方式

    Java中创建对象的5种方式   作为Java开发者,我们每天创建很多对象,但我们通常使用依赖管理系统,比如Spring去创建对象.然而这里有很多创建对象的方法,我们会在这篇文章中学到. Java中有 ...

  9. 对Java代码加密的两种方式,防止反编译

    使用Virbox Protector对Java项目加密有两种方式,一种是对War包加密,一种是对Jar包加密.Virbox Protector支持这两种文件格式加密,可以加密用于解析class文件的j ...

随机推荐

  1. [C#]使用IFormattable接口来实现字符串格式化

    本文为原创文章.源代码为原创代码,如转载/复制,请在网页/代码处明显位置标明原文名称.作者及网址,谢谢! 开发工具:VS2017 语言:C# DotNet版本:.Net FrameWork 4.0及以 ...

  2. 出现JSONvalue failed .error is Illegal start of token

    出现JSONvalue failed .error is Illegal start of token了? 别着急,抽根烟,喝杯水.开工: 1:判断是请求前报的错还是请求后报的错!!这个很重要,我就是 ...

  3. MyBatis多租户隔离插件开发

    在SASS的大潮流下,相信依然存在很多使用一个数据库为多个租户提供服务的场景,这个情况下一般是多个租户共用同一套表通过sql语句级别来隔离不同租户的资源,比如设置一个租户标识字段,每次查询的时候在后面 ...

  4. MySQL 索引管理与执行计划

    1.1 索引的介绍 索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息.如果想按特定职员的姓来查找他或她,则与在表中搜索所有的行相比,索引有助于更快地获取信息. ...

  5. Codeforces Round #261 (Div. 2)459D. Pashmak and Parmida's problem(求逆序数对)

    题目链接:http://codeforces.com/contest/459/problem/D D. Pashmak and Parmida's problem time limit per tes ...

  6. nat的翻译类型(2)--动态nat

    目的:在1.1 1.2 1.3 三台内网的服务器访问外网的服务器(202.1.1.2)时,将内网ip转换为外网ip. 1.设置内网三台服务器的Ip ,网关,以及外网服务器的ip网关 分别为:192.1 ...

  7. MobileNets总结

    Google在2017年上半年发表了一篇关于可以运行在手机等移动设备上的神经网络结构--MobileNets.MobileNets是基于深度可分离卷积(depthwise separable conv ...

  8. ASP.NET Core 异常重试组件 Polly

    Polly 是一种 .NET 弹性和瞬态故障处理库,允许开发人员以流畅和线程安全的方式表达策略,如重试,断路器,超时,隔离隔离和备用,Polly 适用于 .NET 4.0,.NET 4.5 和 .NE ...

  9. BMC手册 — 第一模块 BMC介绍

    BMC的监控 其实是BPPM与 Patrol 产品的结合.早期它们二个是单独监控产品,后来BPPM被BMC收购.拿来与patrol产口集成产品整合后,BPPM主要用来做展示与告警处理,底层采集采用pa ...

  10. HTML5 Canvas:初始Canvas

    Canvas ,HTML 5中引入它,可以做很多事情:画图.动画.游戏开发等等. Canvas 元素 Canvas 中文翻译为:画布. <canvas id=”yourCanvasId” wid ...