java.io.Externalizable接口的主要目标是促进自定义序列化和反序列化。在任务卡中已经学习了Serializable接口的序列化与反序列化的用法,下面再来看看Externalizable的用法。
用法
任何实现Externalizable接口的类都应该实现writeExternal()
,readExternal()
方法。
- Animal
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class Animal implements Externalizable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.name = in.readUTF();
this.age = in.readInt();
}
}
如上,写了一个Animal类实现了Externalizable
接口,serialVersionUID
设为默认的1L
。有一个问题需注意,当实现序列化接口时,类中须有一个无参的构造函数。Animal类中没有呢定义构造函数,会默认一个无参的构造函数。而当你定义的类含有有参数的构造函数时,你就要再定义一个无参的构造函数了。
- 测试 Test
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Animal animal = new Animal();
animal.setName("小白");
animal.setAge(3);
// 序列化
FileOutputStream fileOutputStream = new FileOutputStream("./Animal.txt"); //创建文件字节输出流对象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
animal.writeExternal(objectOutputStream);
// 关闭资源,objectOutputStream.close()内部已经将fileOutputStream对象资源释放了
objectOutputStream.close();
// 反序列化
FileInputStream fileInputStream = new FileInputStream("./Animal.txt"); // 创建文件字节输入流对象
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Animal animal1 = new Animal();
animal1.readExternal(objectInputStream);
// 关闭资源
objectInputStream.close();
System.out.println("name: " + animal1.getName());
System.out.println("age: " + animal1.getAge());
}
}
输出:
name: 小白
age: 3
继承
写一个Dog类继承Animal,并实现Externalizable
接口:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class Dog extends Animal implements Externalizable {
private static final long serialVersionUID = 1L;
private double height;
private String breed;
public double getHeight() {
return this.height;
}
public void setHeight(double height) {
this.height = height;
}
public String getBreed() {
return this.breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeDouble(height);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
this.height = in.readDouble();
}
}
有两点需要注意:
- Dog的在序列化程序方法中调用了
super.writeExternal(out)
,super.readExternal(in)
来保存/恢复父类字段; - Dog的
breed
属性没有被序列化
测试Test:
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Dog dog = new Dog();
dog.setName("小白");
dog.setAge(3);
dog.setBreed("柯基");
dog.setHeight(18);
// 序列化
FileOutputStream fileOutputStream = new FileOutputStream("./dog.txt"); //创建文件字节输出流对象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
dog.writeExternal(objectOutputStream);
// 关闭资源,objectOutputStream.close()内部已经将fileOutputStream对象资源释放了
objectOutputStream.close();
// 反序列化
FileInputStream fileInputStream = new FileInputStream("./dog.txt"); // 创建文件字节输入流对象
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Dog dog1 = new Dog();
dog1.readExternal(objectInputStream);
// 关闭资源
objectInputStream.close();
System.out.println("name: " + dog1.getName());
System.out.println("age: " + dog1.getAge());
System.out.println("height: " + dog1.getHeight());
System.out.println("breed: " + dog1.getBreed());
}
}
结果:
name: 小白
age: 3
height: 18.0
breed: null
因为Dog类的breed
属性没有被序列化,因此dog1的breed
为null
。
Externalizable与Serializable的区别
-
序列化责任
当类实现java.io.Serializable接口时,JVM完全负责序列化类实例。在Externalizable的情况下,程序员应该负责整个序列化和反序列化过程(因为是自定义的,回想writeExternal
和readExternal
)。 -
执行效率
Serializable所有对象由Java统一保存,性能较低;Externalizable开发人员决定哪个对象保存,可能使得速度提升。 -
保存信息
Serializable保存时占用空间大;Externalizable部分存储,可能使得空间减少。 -
阅读顺序
使用Externalizable时,必须按照写入(writeExternal
)时的确切顺序读取(readExternal
)所有字段状态。否则,会抛出 java.io.EOFException 。Serializable接口没有这个要求。 -
自定义序列化
我们可以通过使用transient
关键字标记字段来使用Serializable接口实现自定义序列化。JVM不会序列化特定字段,但它会将字段添加到具有默认值的文件存储。因此自定义序列化时使用Externalizable更好。
网友评论