JAVA序列化Serializable及Externalizable区别详解

网友投稿 294 2022-12-01


JAVA序列化Serializable及Externalizable区别详解

序列化简介

java 的对象序列化将那些实现 Serializable 接口的对象转换成一个字节序列,并能在之后将这个字节序列完全恢复为原来的对象。

这就意味着 Java 对象在网络上的传输可以不依赖于当前计算机的操作系fKbgsCdoFx统,就可以将对象进行传递,这也是Java跨平台的一种体现。

Java 对象的序列化主要支持两种特性:

1、Java的远程方法调用(Remote Method Invocation RMI);

2、对于 JavaBean 来说,序列化也是必须的。

要序列化一个对象,需要创建一个 OutputStream 对象,然后将其封装在 ObjectOutputStream 对象中,再调用 writeObject() 方法就可以完成对象的序列化(也是在这一步进行序列化);反序列化(将一个序列还原为一个对象)就是该过程的反过程:创建一个 InputStream 对象,将其封装在 ObjectInputStream 对象中,使用 readObject() 方法将序列反序列化为对象,当然这是一个Object类型的对象,需要向下转型为我们需要的类型(如果该类型不在本地,会导致反序列化失败,ClassNotFoundException )。

先说结论

序列化有以下方式:

1、实现 Serializable 接口:

2、实现 Externalizable 接口,并重写 writeExternal() readExternal() 方法;

3、(即下文中的 Externalizable 的替代方式进行序列化)如果不想实现Externalizable 接口,又想按照自己的规则进行序列化,可以实现 Serializable 接口,并在该类中添加(添加,不是覆盖、实现)名为 writeExternal() readExternal() 方法,且这两个方法必须为下面这两个准确的方法签名:

private void writeObject(ObjectOutputStream stream) throws IOException;

private void readObject(ObjectInputStream stream) throws IOException,ClassNotFoundException;

一、三种方式完成序列化

1、实现 Serializable 接口序列化

这种方式最为常用且常见,只需要对需要序列化的类实现 Serializable 即可,对于不希望进行序列化的,可以使用 transient 关键词进行修饰(即瞬时变量)。

这种方式序列化的特征:

1、 Serializable 接口仅仅是一个标记接口,不包含任何方法;

2、对于Serializable对象来说,对象完全以它存储的二进制位为基础来构造,(反序列化)不会调用构造器。

2、实现 Externalizable 接口序列化

这种方式可以实现序列化的完全自定义:所有成员变量是否序列化都需要在 writeExternal()、readExternal()

方法中写出;且可以完全自定义序列化方式(在 writerExternal()、readExternal()方法中)。当然,实现 Externalizable 接口必须要重写这两个方法。

这种方式序列化的特征:

1、必须重写 writerExternal()、readExternal()两个方法,并在两个方法中写出所有需要序列化的成员变量;

2、对于 Externalizable对象来说,必须要有无参public构造器,不然会报出 InvalidClassException 异常。

3、 Externalizable 的替代方式进行序列化

让 ObjectOutputStream 和 ObjectInputStream 对象的 writeObject() 方法和 readObject() 方法调用我们编写的这两个方法。

如果想在这种方式中也调用原有默认提供的方式,可以在 writeObject() 中调用: s.defaultWriteObject();,在 readObject() 中调用 s.defaultReadObject();。 这部分代码可以查看 ArrayList 源码。

二、测试代码

1、 Serializable 对象反序列化,不调用任何构造器

Serializable 对象反序列化不调用任何构造器,包括默认构造器,整个对象都是从 InputStream 中取得数据恢复过来的

主测试类 Dogs

public class Dogs {

public static void main(String[] args) throws Exception {

// 创建对象

System.out.println("--- 创建对象 ---");

Dog1 d1 = new Dog1("pidan",4.0);

Dog2 d2 = new Dog2("duanwu","black");

// 序列化

System.out.println("--- 序列化 ---");

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/dogs.out"));

oos.writeObject(d1);

oos.writeObject(d2);

System.out.println("--- 反序列化 ---");

// 反序列化 不会调用任何构造器

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/dogs.out"));

Dog1 o1 = (Dog1) ois.readObject();

Dog2 o2 = (Dog2) ois.readObject();

System.out.println("反序列化 o1 : " + o1);

System.out.println("反序列化 o2 : " + o2);

}

}

Serializable 对象 Dog1 Dog2 类

class Dog1 implements Serializable {

private static final long serialVersionUID = -7101743601344663182L;

private String name;

private Double weight;

public Dog1(String name, Double weight) {

System.out.println("Dog1 构造器运行 ---");

this.name = name;

this.weight = weight;

System.out.println("Dog1 : " + this);

}

// 省略get、set、toString方法

}

public class Dog2 implements Serializable {

private static final long serialVersionUID = -5462607652670703938L;

private String name;

private String color;

public Dog2(String name, String color) {

System.out.println("Dog2 构造器运行 ---");

this.name = name;

this.color = color;

System.out.println("Dogs2 : " + this);

}

// 省略get、set、toString方法

}

运行结果:

--- 创建对象 ---

Dog1 构造器运行 ---

Dog1 : Dog1{name='pidan', weight=4.0}

Dog2 构造器运行 ---

Dogs2 : Dog2{name='duanwu', color='black'}

--- 序列化 ---

--- 反序列化 ---

反序列化 o1 : Dog1{name='pidan', weight=4.0}

反序列化 o2 : Dog2{name='duanwu', color='black'}

再最后取出对象时,完全没有调用到其任何构造器。

2、无参构造器对 Externalizable 对象序列化的影响

主测试代码:

public class Persons {

public static void main(String[] args) throws Exception {

// 创建对象

System.out.println("Init Objects");

Person1 p1 = new Person1();

Person2 p2 = new Person2();

// 存储在磁盘上

ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d:/person.out"));

os.writeObject(p1);

os.writeObject(p2);

os.flush();

os.close();

// 取出

ObjectInputStream is = new ObjectInputStream(new FileInputStream("d:/person.out"));

System.out.println("取出p1: ");

p1 = (Person1) is.readObject();

p2 = (Person2) is.readObject();

}

}

Externalizable 对象:Perion1 Persion2

public class Person1 implements Externalizable {

public Person1(){

System.out.println("Person1 构造器---");

}

@Override

public void writeExternal(ObjectOutput out) throws IOException {

System.out.println("Person1 writeExternal ---");

}

@Override

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

System.out.println("Person1 readExternal ---");

}

}

class Person2 implements Externalizable{

// 注意不是public

Person2(){

System.out.println("Person2 构造器 ---");

}

@Override

public void writeExternal(ObjectOutput out) throws IOException {

System.out.println("Person2 writeExternal ---");

}

@Override

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

System.out.println("Person2 readExternal ---");

}

}

Person2 默认构造器不是 public 的运行结果:

Init Objects

Person1 构造器---

Person2 构造器 ---

Person1 writeExternal ---

Person2 writeExternal ---

取出p1:

Person1 构造器---

Person1 readExternal ---

Exception in thread "main" java.io.InvalidClassException: ...serializableAndexternalizable.Person2; no valid constructor

at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:169)

at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:874)

at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2043)

at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)

at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)

at ...serializableAndexternalizable.Persons.main(Persons.java:29)

Process finished with exit code 1

将 Person2 构造器改为 public 后:

Init Objects

Person1 构造器---

Person2 构造器 ---

Person1 writeExternal ---

Person2 writeExternal ---

取出p1:

Person1 构造器---

Person1 readExternal ---

Person2 构造器 ---

Person2 readExternal ---

3、使用 Externalizable 对象实现序列化

主测试类 Cats :

public class Cats {

public static void main(String[] args) throws Exception {

// 初始化对象

System.out.println("--- 初始化对象 ---");

Person person = new Person("01", "老王", 30);

Cat2 cat = new Cat2("fugui", person);

// 序列化

System.out.println("--- 序列化对象 ---");

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/cats.out"));

oos.writeObject(cat);

System.out.println("--- 反序列化对象 ---");

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/cats.out"));

cat = (Cat2) ois.readObject();

System.out.println("--- 反序列化对象后 ---");

System.out.println("cat : " + cat);

}

}

Externalizable 对象: Cat2 ;Serializable 对象:Person :

public class Person implements Serializable {

private static final long serialVersionUID = -822166081906894628L;

private transient String id;

private String name;

private int age;

public Person() {

System.out.println("--- Person 无参构造器 ---");

}

public Person(String id, String name, int age) {

System.out.println("--- Person 无参构造器 ---");

this.id = id;

this.name = name;

this.age = age;

System.out.println("Person : " + this);

}

// 省略get、set、toString方法

}

class Cat2 implements Externalizable {

private static final long serialVersionUID = 1102930161606017855L;

private String name;

private Person minion;

public Cat2() {

System.out.println("Cat2 无参构造器 --->");

}

public Cat2(String name, Person minion) {

System.out.println("Cat2 有参构造器 --->");

this.name = name;

this.minion = minion;

System.out.println("Cat2 : " + this);

}

// 省略get、set、toString方法

@Override

public void writeExternal(ObjectOutput out) throws IOException {

System.out.println("--- Cat2:writeExternal ---");

// code1

out.writeObject(this.minion);

out.writeObject(this.name);

}

@Override

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

System.out.println("--- Cat2:readExternal ---");

// code2

this.minion = (Person) in.readObject();

this.name = (String) in.readObject();

}

}

运行结果:

可以注意到Person的成员变量id在使用了 transient 关键词修饰后,就不再序列化该字段了。

--- 初始化对象 ---

--- Person 无参构造器 ---

Person : Person{id='01', name='老王', age=30}

Cat2 有参构造器 --->

Cat2 : Cat2{name='fugui', minion=Person{id='01', name='老王', age=30}}

--- 序列化对象 ---

--- Cat2:writeExternal ---

--- 反序列化对象 ---

Cat2 无参构造器 --->

--- Cat2:readExternal ---

--- 反序列化对象后 ---

cat : Cat2{name='fugui', minion=Person{id='null', name='老王', age=30}}

如果将Cat2类中标注的 code1 与 code2 代码下面的两行代码均注释掉,就不再可以序列化及反序列化了:

注释掉后的运行结果:

--- 初始化对象 ---

--- Person 无参构造器 ---

Person : Person{id='01', name='老王', age=30}

Cat2 有参构造器 --->

Cat2 : Cat2{name='fugui', minion=Person{id='01', name='老王', age=30}}

--- 序列化对象 ---

--- Cat2:writeExternal ---

--- 反序列化对象 ---

Cat2 无参构造器 --->

--- Cat2:readExternal ---

--- 反序列化对象后 ---

cat : Cat2{name='null', minion=null}

4、使用 Externalizable 对象替代方式实现序列化

替代方式就是实现 Serializable 接口,并且添加 writeObject(),readObject() 两个方法注意这两个方法必须有准确的方法特征签名,在这两个方法中编写自定义方式实现序列化和反序列化。

class Mouse implements Serializable {

private static final long serialVersionUID = -3278535893876444138L;

private String name;

private int i;

public Mouse() {

System.out.println("Mouse 无参构造器 ---");

}

public Mouse(String name, int i) {

System.out.println("Mouse 有参构造器 ---");

this.name = name;

this.i = i;

System.out.println("Mouse : " + this);

}

// 方法特征签名必须完全一致

private void writeObject(ObjectOutputStream stream) throws IOException {

// stream.defaultWriteObject();// 可以选择执行默认的writeObject()

System.out.println("--- 这是自定义的writeExternal方法 ---");

stream.writeObject(this.name);

stream.writeInt(this.i);

}

// 方法特征签名必须完全一致

private void readObject(ObjectInputStream stream) throws IOException,ClassNotFoundException {

// stream.defaultReadObject(); // 可以选择执行默认的readObject()

System.out.println("--- 这是自定义的readExternal方法 ---");

this.name = (String)stream.readObject();

this.i = stream.readInt();

}

// 省略get、set、toString方法

}

主测试类:

public class Mouses {

public static void main(String[] args) throws Exception {

// 创建对象

System.out.println("--- 创建对象 ---");

Mouse m1 = new Mouse("zhizhi", 2);

// 序列化

System.out.println("--- 序列化 ---");

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/mouse.out"));

oos.writeObject(m1);

// 反序列化

System.out.println("--- 反序列化 ---");

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/mouse.out"));

// 反序列化结果

System.out.println("--- 反序列化结果 ---");

m1 = (Mouse) ois.readObject();

System.out.println(" zhizhi : " + m1);

}

}

运行结果

--- 创建对象 ---

Mouse 有参构造器 ---

Mouse : Mouse{name='zhizhi', i=2}

--- 序列化 ---

--- 这是自定义的writeExternal方法 ---

--- 反序列化 ---

--- 反序列化结果 ---

--- 这是自定义的readExternal方法 ---

zhizhi : Mouse{name='zhizhi', i=2}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Maven install 报错"程序包不存在"问题的解决方法
下一篇:理解maven命令package、install、deploy的联系与区别
相关文章

 发表评论

暂时没有评论,来抢沙发吧~