1、概述
大家好,我是欧阳方超。在上一次的的博文中——Java中的序列化与反序列化——transient(二),我们提到,在 Java 中,如果一个类想要实现序列化,需要实现Serializable 接口并定义一个 serialVersionUID 字段。然而,如果想要更加灵活地控制序列化过程,可以使用 Externalizable 接口。今天我们就来具体看一下Externalizable。
2、Externalizable 接口
2.1、Externalizable的使用
首先需要注意的是,Externalizable也是一个接口,它继承自 Serializable 接口,并且在序列化和反序列化时提供了更细粒度的控制。与 Serializable 接口不同的是,实现 Externalizable 接口需要手动实现 writeExternal() 和 readExternal() 方法,这两个方法负责实现对象的序列化和反序列化过程。下面是一个实现 Externalizable 接口的示例:
import java.io.*;
public class Person implements Externalizable {
private String name;
private int age;
public Person() {
// 必须提供一个默认的构造函数
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
age = in.readInt();
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
在上面的示例中,Person 类实现了 Externalizable 接口,并覆盖了 writeExternal() 和 readExternal() 方法,分别负责将对象写入输出流和从输入流读取对象。注意,为了实现 Externalizable 接口,必须提供一个默认的构造函数。下面的代码中创建Person类的对象并对其进行序列化及反序列化操作:
import java.io.*;
public class Main {
public static void main(String[] args) {
Person person = new Person("Tom", 19);
//序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\home\\person.ser"))) {
person.writeExternal(oos);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\home\\person.ser"))) {
Person loaded = new Person();
loaded.readExternal(ois);
System.out.println(loaded.getName() + " " + loaded.getAge());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
使用ObjectOutputStream将person对象序列化到person.ser文件中。在接下来的程序中,使用ObjectInputStream从person.ser中恢复Person对象loaded。通过调用 loaded.readExternal() 来反序列化对象,并通过获取name和age属性来确保被正确重建。程序输出结果为:
Tom 19
2.2、在继承上的处理
当一个类实现了 Externalizable 接口并且它的超类也实现了 Externalizable 接口时,需要手动序列化和反序列化超类的字段。
具体来说,我们需要在实现 writeExternal() 和 readExternal() 方法时,显式地调用超类的 writeExternal() 和 readExternal() 方法,将超类的字段写入输出流或从输入流中读取超类的字段。
以下是一个示例,假设我们有一个 Person 类继承自 User 类,它们都实现了 Externalizable 接口:
import java.io.*;
public class User implements Externalizable {
private String username;
public User() {}
public User(String username) {
this.username = username;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(username);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
username = (String) in.readObject();
}
public String getUsername() {
return username;
}
}
public class Person extends User implements Externalizable {
private int age;
public Person() {}
public Person(String username, int age) {
super(username);
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
age = in.readInt();
}
public int getAge() {
return age;
}
}
在上面的示例中,Person 类继承自 User 类,并分别实现了 writeExternal() 和 readExternal() 方法。在 writeExternal() 方法中,我们首先调用了超类 User 的 writeExternal() 方法,将 username 字段写入输出流。然后,我们再将 age 字段写入输出流。
在 readExternal() 方法中,我们首先调用了超类 User 的 readExternal() 方法,从输入流中读取 username 字段。然后,我们再从输入流中读取 age 字段。
通过这种方式,我们可以手动序列化和反序列化超类的字段,确保在序列化和反序列化时超类的字段也能正确地被处理。
2.3、要点
使用 Externalizable 接口需要注意以下几点:
- Externalizable 接口的序列化和反序列化过程需要手动实现,相对于 Serializable 接口会更加复杂和繁琐。
- 与 Serializable 接口不同的是,Externalizable 接口中的默认构造函数必须是 public 的,否则在反序列化时会抛出 InvalidClassException 异常。
- 对于实现了 Externalizable 接口的类,如果它的超类也实现了 Externalizable 接口,则需要在子类中手动序列化和反序列化超类的字段。
3、总结
Externalizable接口提供了一种比简单的Serializable更灵活和可控的序列化机制。它使您可以在需要反序列化时采取不同的行动,而不是简单地遵循官方的序列化方法。希望这篇博客有助于大家更好地理解Externalizable和在项目中使用它。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。