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教程。