externalizable 和 serializable 的核心区别在于序列化控制权:serializable 由 java 虚拟机自动处理对象字段的序列化,而 externalizable 允许开发者通过实现 writeexternal 和 readexternal 方法完全自定义序列化逻辑。使用 serializable 时,jvm 会调用无参构造函数创建对象实例,而 externalizable 在反序列化时也会调用无参构造函数,但后续字段填充需手动完成。若需优化性能或控制特定字段的序列化行为,externalizable 提供了更细粒度的控制能力。

Serilizable 的默认序列化机制是如何工作的?
Serilizable 接口是一个标记接口,其默认机制依赖 JVM 内部反射和元数据来保存对象状态。当对象实现该接口后,ObjectOutputStream 会自动遍历所有非静态、非瞬态字段并写入流中。反序列化时,JVM 直接分配内存并填充字段,跳过构造函数(除非类定义了 readObject 钩子)。这种机制简单但可能包含多余数据,且无法灵活控制序列化过程。

import java.io.*;
public class User implements Serializable {
private String name;
private transient int age; // 瞬态字段不被序列化
public User(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) throws Exception {
User user = new User("Alice", 30);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"));
oos.writeObject(user); // 自动序列化
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"));
User restored = (User) ois.readObject(); // 自动反序列化
System.out.println("Name: " + restored.name + ", Age: " + restored.age);
}
}
预期输出:
Name: Alice, Age: 0
说明:age 因 transient 关键字未被序列化,反序列化后恢复为默认值 0。
立即学习“Java免费学习笔记(深入)”;
Externalizable 如何自定义序列化逻辑?
Externalizable 接口要求实现 writeExternal 和 readExternal 方法,开发者需手动指定哪些字段被序列化及顺序。反序列化时,JVM 先调用无参构造函数创建对象,再调用 readExternal 填充数据。这种方式可跳过不必要字段、加密敏感信息或兼容旧版本格式,但增加了实现复杂度。

import java.io.*;
public class Product implements Externalizable {
private String id;
private double price;
public Product() {} // 必须有无参构造函数
public Product(String id, double price) {
this.id = id;
this.price = price;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(id); // 手动序列化 id
out.writeDouble(price); // 手动序列化 price
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
id = (String) in.readObject(); // 手动反序列化 id
price = in.readDouble(); // 手动反序列化 price
}
public static void main(String[] args) throws Exception {
Product product = new Product("P001", 99.9);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("product.ser"));
oos.writeObject(product); // 调用 writeExternal
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("product.ser"));
Product restored = (Product) ois.readObject(); // 调用 readExternal
System.out.println("ID: " + restored.id + ", Price: " + restored.price);
}
}
预期输出:
ID: P001, Price: 99.9
说明:通过 writeExternal 和 readExternal 精确控制序列化内容,避免自动机制的冗余。
Externalizable 反序列化时为何必须有无参构造函数?
Externalizable 在反序列化过程中,JVM 首先通过反射调用类的无参构造函数创建对象实例,随后才执行 readExternal 方法填充字段。若无无参构造函数,反序列化将抛出 InvalidClassException。这是因为 JVM 需要初始对象状态以支持后续的自定义读取逻辑,而 Serializable 则可通过 readObject 钩子绕过此限制。


















