K:java中序列化的两种方式—Serializable或Externalizable

时间:2021-04-18 15:48:30

在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

回到目录|·(工)·)