Java > Java Input/Output (I/O) > Serialization and Deserialization > Externalizable Interface
Custom Serialization with Externalizable Interface
This example demonstrates how to use the Externalizable
interface in Java to customize the serialization and deserialization process. Unlike Serializable
, Externalizable
gives you complete control over what fields are written and read during serialization, allowing you to optimize the process and handle versioning effectively. This is crucial when dealing with sensitive data or when needing fine-grained control over object persistence.
Defining the Externalizable Class
This code defines a class ExternalizableExample
that implements the Externalizable
interface. It contains a name
, age
, and a secret
field. The writeExternal
method explicitly writes the name
and age
, but intentionally excludes the secret
, demonstrating how you can control what is serialized. The readExternal
method reads the name
and age
, and then sets secret to an empty string to avoid a null value.
import java.io.*;
public class ExternalizableExample implements Externalizable {
private String name;
private int age;
private String secret;
// Required: Public no-arg constructor
public ExternalizableExample() {
}
public ExternalizableExample(String name, int age, String secret) {
this.name = name;
this.age = age;
this.secret = secret; // Simulate a sensitive field
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
// We intentionally don't provide a getter for 'secret' to demonstrate hiding during serialization.
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
// 'secret' is deliberately NOT written. Consider encryption here for real secrets.
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
age = in.readInt();
// 'secret' is deliberately NOT read. It will remain null or its default value.
// We may need to initialize it here based on name/age or some other logic.
secret = ""; //default value instead of null
}
@Override
public String toString() {
return "ExternalizableExample{" +
"name='" + name + '\'' +
", age=" + age +
", secret='" + secret + '\'' +
'}';
}
public static void main(String[] args) {
ExternalizableExample original = new ExternalizableExample("Alice", 30, "TopSecret");
// Serialization
try (FileOutputStream fileOut = new FileOutputStream("externalizable.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(original);
System.out.println("Serialized data is saved in externalizable.ser");
} catch (IOException i) {
i.printStackTrace();
}
// Deserialization
ExternalizableExample deserialized = null;
try (FileInputStream fileIn = new FileInputStream("externalizable.ser");
ObjectInputStream in = new ObjectInputStream(fileIn)) {
deserialized = (ExternalizableExample) in.readObject();
} catch (IOException i) {
i.printStackTrace();
return;
} catch (ClassNotFoundException c) {
System.out.println("ExternalizableExample class not found");
c.printStackTrace();
return;
}
System.out.println("Deserialized ExternalizableExample: " + deserialized);
}
}
Concepts Behind Externalizable
The Externalizable
interface extends Serializable
and provides two methods: writeExternal(ObjectOutput out)
and readExternal(ObjectInput in)
. Implementing this interface provides full control over the serialization and deserialization process. Unlike Serializable
, the default serialization mechanism is bypassed. This requires you to handle all aspects of saving and restoring the object's state. An important point is that when deserializing an Externalizable
object, the no-arg constructor is called first. Therefore, it's crucial to provide a public no-argument constructor in your Externalizable
class.
Real-Life Use Case
Consider a scenario where you are serializing objects that contain sensitive information like passwords or API keys. Using Externalizable
, you can selectively choose not to serialize those sensitive fields. Instead, you might encrypt them before serialization or retrieve them from a secure source after deserialization. Another use case is versioning. If the structure of your class changes significantly between versions, Externalizable
allows you to handle the migration of data from older versions to newer versions gracefully during deserialization.
Best Practices
Externalizable
.readExternal
can handle older serialized formats.writeExternal
to ensure compatibility.
Interview Tip
During interviews, be prepared to explain the difference between Serializable
and Externalizable
. Highlight the advantages and disadvantages of each, focusing on the control Externalizable
provides and the requirement for a no-arg constructor. Be ready to discuss scenarios where Externalizable
would be the preferred choice due to security or versioning needs.
When to Use Externalizable
Use Externalizable
when:
Memory Footprint
Externalizable
can potentially reduce the memory footprint of serialized data because you explicitly choose which fields to serialize. By excluding unnecessary fields, you reduce the size of the serialized representation. However, the manual implementation of writeExternal
and readExternal
adds to the code size.
Alternatives
writeObject
and readObject
: Offers a middle ground between Serializable
and Externalizable
, allowing you to customize the serialization process without implementing the entire serialization logic yourself.
Pros of Externalizable
Cons of Externalizable
FAQ
-
Why do I need a no-arg constructor for Externalizable?
When deserializing anExternalizable
object, the JVM first creates an instance of the class using its public no-argument constructor. Then, it calls thereadExternal
method to populate the object's fields. If a no-arg constructor is not available or not public, deserialization will fail. -
What happens if I don't read or write all the fields in readExternal/writeExternal?
If you don't write a field inwriteExternal
, its value won't be serialized. During deserialization, if you don't read it inreadExternal
, it will retain its default value (null for objects, 0 for int, etc.) after the object is deserialized. This behavior is intended and allows you to selectively exclude fields or provide default values. -
How does Externalizable handle inheritance?
If a class inherits from another class that implementsExternalizable
, the subclass is responsible for serializing and deserializing the state of its superclass. The subclass'swriteExternal
method should call the superclass'swriteExternal
method first, and similarly, the subclass'sreadExternal
method should call the superclass'sreadExternal
method first.