Java Externalizable 接口
上次修改:2025 年 4 月 26 日
Java 中的 Externalizable 接口提供了对对象序列化和反序列化的完全控制。与 Serializable 不同,它需要显式地实现序列化逻辑。
Externalizable 适用于优化序列化、处理复杂对象或确保跨系统的兼容性。它非常适合需要精细化持久化或网络传输的应用程序。
Externalizable 接口概述
Externalizable 接口是 java.io 包的一部分,它扩展了 Serializable。它强制实现 writeExternal 和 readExternal 方法以进行自定义序列化。
使用 Externalizable 的类必须提供一个无参数构造函数,因为反序列化会在调用 readExternal 之前创建一个实例。 这提供了比 Serializable 更大的灵活性。
基本的 Externalizable 实现
此示例演示了 Externalizable 的基本实现,以序列化和反序列化一个简单的对象,并将其保存到文件中。
package com.zetcode;
import java.io.*;
public class BasicExternalizable {
static class Product implements Externalizable {
private String name;
private double price;
public Product() {} // Required for Externalizable
public Product(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeDouble(price);
}
@Override
public void readExternal(ObjectInput in) throws IOException {
this.name = in.readUTF();
this.price = in.readDouble();
}
@Override
public String toString() {
return "Product{name='" + name + "', price=" + price + "}";
}
}
public static void main(String[] args) {
// Serialize
Product product = new Product("Laptop", 999.99);
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("product.ser"))) {
oos.writeObject(product);
System.out.println("Serialized: " + product);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("product.ser"))) {
Product deserialized = (Product) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Product 类实现了 Externalizable,定义了自定义的序列化逻辑。 无参数构造函数对于反序列化至关重要。
此示例展示了 Externalizable 如何精确控制要序列化的字段,从而确保高效的对象持久化。
序列化复杂对象
此示例说明了如何使用 Externalizable 序列化一个引用其他对象的复杂对象,从而管理整个对象图。
package com.zetcode;
import java.io.*;
public class ComplexExternalizable {
static class Department implements Externalizable {
private String name;
public Department() {}
public Department(String name) {
this.name = name;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
}
@Override
public void readExternal(ObjectInput in) throws IOException {
this.name = in.readUTF();
}
@Override
public String toString() {
return "Department{name='" + name + "'}";
}
}
static class Employee implements Externalizable {
private String name;
private Department department;
public Employee() {}
public Employee(String name, Department department) {
this.name = name;
this.department = department;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeObject(department);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.name = in.readUTF();
this.department = (Department) in.readObject();
}
@Override
public String toString() {
return "Employee{name='" + name + "', department=" + department + "}";
}
}
public static void main(String[] args) {
// Serialize
Department dept = new Department("Engineering");
Employee emp = new Employee("Alice", dept);
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("employee.ser"))) {
oos.writeObject(emp);
System.out.println("Serialized: " + emp);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("employee.ser"))) {
Employee deserialized = (Employee) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Employee 类引用了一个 Department 对象,两者都实现了 Externalizable。自定义逻辑处理对象图序列化。
这演示了 Externalizable 如何管理复杂的关系,从而可以精确控制引用对象的序列化。
优化序列化大小
此示例显示了如何使用 Externalizable 通过选择性地序列化字段或压缩数据来优化序列化大小。
package com.zetcode;
import java.io.*;
public class OptimizedExternalizable {
static class Book implements Externalizable {
private String title;
private String author;
private transient String description; // Not serialized
public Book() {}
public Book(String title, String author, String description) {
this.title = title;
this.author = author;
this.description = description;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(title);
out.writeUTF(author);
// Skip description to reduce size
}
@Override
public void readExternal(ObjectInput in) throws IOException {
this.title = in.readUTF();
this.author = in.readUTF();
this.description = "N/A"; // Default value
}
@Override
public String toString() {
return "Book{title='" + title + "', author='" + author + "', description='" + description + "'}";
}
}
public static void main(String[] args) {
// Serialize
Book book = new Book("Java Guide", "John Doe", "Comprehensive Java tutorial");
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("book.ser"))) {
oos.writeObject(book);
System.out.println("Serialized: " + book);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("book.ser"))) {
Book deserialized = (Book) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Book 类从序列化中排除了 description 字段,从而减小了序列化数据的大小。 在反序列化期间设置默认值。
此优化对于带宽或存储空间受限的应用程序非常有用,展示了 Externalizable 在数据管理方面的灵活性。
处理版本控制
此示例演示了 Externalizable 如何通过实现支持向后兼容的逻辑来处理类版本控制。
package com.zetcode;
import java.io.*;
public class VersionedExternalizable {
static class User implements Externalizable {
private static final long serialVersionUID = 1L;
private String username;
private int version = 1; // Version control
private String email; // Added in version 2
public User() {}
public User(String username, String email) {
this.username = username;
this.email = email;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(version);
out.writeUTF(username);
if (version >= 2) {
out.writeUTF(email);
}
}
@Override
public void readExternal(ObjectInput in) throws IOException {
this.version = in.readInt();
this.username = in.readUTF();
if (version >= 2) {
this.email = in.readUTF();
} else {
this.email = "unknown@example.com"; // Default for older versions
}
}
@Override
public String toString() {
return "User{username='" + username + "', email='" + email + "', version=" + version + "}";
}
}
public static void main(String[] args) {
// Serialize
User user = new User("bob", "bob@example.com");
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("user.ser"))) {
oos.writeObject(user);
System.out.println("Serialized: " + user);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("user.ser"))) {
User deserialized = (User) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
User 类使用版本字段来管理兼容性。 它根据版本有条件地序列化 email 字段。
这种方法确保了向后兼容性,从而可以在支持更新类定义中的新字段的同时,反序列化较旧的对象版本。
带有不可序列化字段的 Externalizable
此示例展示了如何在 Externalizable 类中通过手动管理序列化来处理不可序列化的字段。
package com.zetcode;
import java.io.*;
public class NonSerializableFields {
static class Logger {
private String logLevel;
public Logger(String logLevel) {
this.logLevel = logLevel;
}
public String getLogLevel() {
return logLevel;
}
}
static class Application implements Externalizable {
private String appName;
private transient Logger logger;
public Application() {}
public Application(String appName, Logger logger) {
this.appName = appName;
this.logger = logger;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(appName);
out.writeUTF(logger.getLogLevel());
}
@Override
public void readExternal(ObjectInput in) throws IOException {
this.appName = in.readUTF();
this.logger = new Logger(in.readUTF());
}
@Override
public String toString() {
return "Application{appName='" + appName + "', loggerLevel='" + logger.getLogLevel() + "'}";
}
}
public static void main(String[] args) {
// Serialize
Logger logger = new Logger("INFO");
Application app = new Application("MyApp", logger);
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("app.ser"))) {
oos.writeObject(app);
System.out.println("Serialized: " + app);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("app.ser"))) {
Application deserialized = (Application) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Application 类包含一个不可序列化的 Logger 对象。 自定义序列化逻辑处理其 logLevel 字段。
这演示了 Externalizable 如何通过显式序列化其相关数据来管理不可序列化的字段,从而确保正确的对象恢复。
Externalizable 与 Serializable
此示例通过使用两种方法序列化同一对象来比较 Externalizable 和 Serializable,从而突出显示它们之间的差异。
package com.zetcode;
import java.io.*;
public class ExternalizableVsSerializable {
static class ItemSerializable implements Serializable {
private String name;
private int quantity;
public ItemSerializable(String name, int quantity) {
this.name = name;
this.quantity = quantity;
}
@Override
public String toString() {
return "ItemSerializable{name='" + name + "', quantity=" + quantity + "}";
}
}
static class ItemExternalizable implements Externalizable {
private String name;
private int quantity;
public ItemExternalizable() {}
public ItemExternalizable(String name, int quantity) {
this.name = name;
this.quantity = quantity;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(quantity);
}
@Override
public void readExternal(ObjectInput in) throws IOException {
this.name = in.readUTF();
this.quantity = in.readInt();
}
@Override
public String toString() {
return "ItemExternalizable{name='" + name + "', quantity=" + quantity + "}";
}
}
public static void main(String[] args) {
// Serialize Serializable
ItemSerializable itemSer = new ItemSerializable("Pen", 100);
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("itemSer.ser"))) {
oos.writeObject(itemSer);
System.out.println("Serialized Serializable: " + itemSer);
} catch (IOException e) {
e.printStackTrace();
}
// Serialize Externalizable
ItemExternalizable itemExt = new ItemExternalizable("Pen", 100);
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("itemExt.ser"))) {
oos.writeObject(itemExt);
System.out.println("Serialized Externalizable: " + itemExt);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize Serializable
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("itemSer.ser"))) {
ItemSerializable deserialized = (ItemSerializable) ois.readObject();
System.out.println("Deserialized Serializable: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
// Deserialize Externalizable
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("itemExt.ser"))) {
ItemExternalizable deserialized = (ItemExternalizable) ois.readObject();
System.out.println("Deserialized Externalizable: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
两个类序列化相同的数据:一个使用 Serializable,另一个使用 Externalizable。 Externalizable 需要显式的序列化逻辑。
Externalizable 提供了更多的控制和效率,但需要手动实现,而 Serializable 更简单但灵活性较差。
性能注意事项
此示例比较了 Externalizable 和 Serializable 的性能,突出显示了自定义序列化的效率。
package com.zetcode;
import java.io.*;
public class PerformanceExternalizable {
static class DataSerializable implements Serializable {
private String data;
private int value;
public DataSerializable(String data, int value) {
this.data = data;
this.value = value;
}
}
static class DataExternalizable implements Externalizable {
private String data;
private int value;
public DataExternalizable() {}
public DataExternalizable(String data, int value) {
this.data = data;
this.value = value;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(data);
out.writeInt(value);
}
@Override
public void readExternal(ObjectInput in) throws IOException {
this.data = in.readUTF();
this.value = in.readInt();
}
}
public static void main(String[] args) {
final int COUNT = 10000;
// Test Serializable
long start = System.currentTimeMillis();
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("dataSer.ser"))) {
for (int i = 0; i < COUNT; i++) {
oos.writeObject(new DataSerializable("Test", i));
}
} catch (IOException e) {
e.printStackTrace();
}
long duration = System.currentTimeMillis() - start;
System.out.println("Serializable time: " + duration + "ms");
// Test Externalizable
start = System.currentTimeMillis();
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("dataExt.ser"))) {
for (int i = 0; i < COUNT; i++) {
oos.writeObject(new DataExternalizable("Test", i));
}
} catch (IOException e) {
e.printStackTrace();
}
duration = System.currentTimeMillis() - start;
System.out.println("Externalizable time: " + duration + "ms");
}
}
该程序测量 Serializable 和 Externalizable 的序列化时间。 由于其最小的开销,Externalizable 通常更快。
对于性能至关重要的应用程序,请使用 Externalizable,但要考虑实现自定义序列化逻辑的额外复杂性。
来源
本教程全面探讨了 Java Externalizable 接口,涵盖了基本用法、复杂对象、优化和版本控制。 它是自定义序列化的关键。
作者
列出所有Java教程。