Master Java Serialization & Deserialization: Techniques, Pitfalls, and Security
This article explains Java serialization and deserialization concepts, outlines common use cases such as distributed systems and caching, compares multiple implementation methods including Serializable, Externalizable, JSON, XML, and binary libraries, and provides practical code examples, security tips, and best‑practice guidelines.
With Java’s widespread use in various development scenarios, serialization ( Serialization) and deserialization ( Deserialization) have become essential concepts that developers encounter frequently. Whether for data transmission, object state persistence, network communication, caching, or distributed systems, understanding and applying serialization correctly is crucial.
1. What Are Serialization and Deserialization?
Serialization : The process of converting an object into a byte stream so that it can be saved to disk or transmitted over a network. In short, serialization turns a complex Java object into byte data for storage or transfer.
Deserialization : The reverse process, reconstructing an object from a byte stream. It is typically used to restore previously serialized objects from files, databases, or network sources.
2. Use Cases for Serialization and Deserialization
Distributed Systems : In RPC, clients and servers exchange objects, and serialization plays a key role.
Network Transmission : Objects are turned into byte streams for easy transport.
Object Persistence : Java objects can be saved to disk (e.g., writing a Person object to a file) and later restored.
Caching : Serialized objects are stored in caches and deserialized when needed.
3. How to Implement Serialization and Deserialization in Java
Java’s serialization is not limited to the standard Serializable interface; many other approaches exist, each suited to different scenarios, performance requirements, and data formats.
1. Implement the Serializable Interface
Features : Simple to use; just implement Serializable.
Use Cases : Internal object storage and transmission, short‑term storage, caching, network transfer.
To make a class serializable, it must implement java.io.Serializable, a marker interface without methods. All non‑static, non‑transient fields are serialized automatically.
import java.io.Serializable;
/**
* Test serialization object
*/
public class Person implements Serializable {
private static final long serialVersionUID = 1L; // version UID
private String name;
private int age;
private String addr;
public Person(String name, int age, String addr) {
this.name = name;
this.age = age;
this.addr = addr;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getAddr() { return addr; }
public void setAddr(String addr) { this.addr = addr; }
}Serializing and deserializing a Person object:
import java.io.*;
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// Serialization
Person person = new Person("程序员清宇", 18, "chengdu");
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
oos.writeObject(person);
}
// Deserialization
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person deserializedPerson = (Person) ois.readObject();
System.out.println("name: " + deserializedPerson.getName());
System.out.println("age: " + deserializedPerson.getAge());
System.out.println("addr: " + deserializedPerson.getAddr());
}
}
} name: 程序员清宇
age: 18
addr: chengduBinary content of person.ser (illustrative):
2. Implement the Externalizable Interface
Externalizableextends Serializable and provides finer control by requiring the implementation of writeExternal() and readExternal().
public interface Externalizable extends java.io.Serializable {
// Serialize custom fields
void writeExternal(ObjectOutput out) throws IOException;
// Deserialize custom fields
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}Features : Manual control over the serialization process, higher efficiency.
Use Cases : Custom serialization logic, partial field serialization, encryption, or other security‑related processing.
Example: an Employee class that encrypts the password during serialization.
public class Employee implements Externalizable {
private String username;
private transient String password; // not serialized directly
private static final long serialVersionUID = 1L;
private static SecretKey secretKey; // encryption key
public Employee() {} // required no‑arg constructor
public Employee(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() { return username; }
public String getPassword() { return password; }
public static void setSecretKey(SecretKey key) { secretKey = key; }
@Override
public void writeExternal(ObjectOutput out) throws IOException {
try {
out.writeObject(username);
byte[] encryptedPassword = AESUtil.encrypt(password.getBytes(), secretKey);
out.writeObject(encryptedPassword);
System.out.println("密码已加密:" + encryptedPassword);
} catch (Exception e) {
throw new IOException("加密失败", e);
}
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
try {
username = (String) in.readObject();
byte[] encryptedPassword = (byte[]) in.readObject();
byte[] decryptedPassword = AESUtil.decrypt(encryptedPassword, secretKey);
password = new String(decryptedPassword);
System.out.println("解密后的密码:" + password);
} catch (Exception e) {
throw new IOException("解密失败", e);
}
}
@Override
public String toString() {
return "Employee{username='" + username + "', password='" + password + "'}";
}
}Testing the custom serialization:
public class ExternalizableTest {
public static void main(String[] args) throws Exception {
Employee employee = new Employee("姓名123", "@1234Qingyu");
SecretKey key = AESUtil.generateKey();
Employee.setSecretKey(key);
// Serialize
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employee.ser"))) {
oos.writeObject(employee);
}
// Deserialize
Employee deserializedEmployee;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("employee.ser"))) {
deserializedEmployee = (Employee) ois.readObject();
}
System.out.println("Username: " + deserializedEmployee.getUsername());
System.out.println("Password: " + deserializedEmployee.getPassword());
}
} 密码已加密:[B@b59d31
解密后的密码:@1234Qingyu
Username: 姓名123
Password: @1234QingyuKey consistency is essential; the same secret key must be used for encryption and decryption, typically obtained from configuration or a secure store.
3. JSON Serialization Libraries
JSON is a lightweight data‑exchange format widely used for front‑back communication. In Java, Jackson and Gson are the most common libraries.
Features : Human‑readable, easy debugging, language‑agnostic.
Applicable Scenarios : Front‑end/back‑end interaction, RESTful APIs, cross‑language data exchange.
Jackson dependency (Maven):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.9</version>
</dependency>Example POJO:
public class Book {
private String name;
private double price;
public Book() {}
public Book(String name, double price) { this.name = name; this.price = price; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
@Override
public String toString() { return "Book{name='" + name + "', price=" + price + "}"; }
}Test code:
public class JsonSerializableTest {
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
Book book = new Book("Java并发编程", 121.99);
String jsonString = objectMapper.writeValueAsString(book);
System.out.println("序列化后的JSON:" + jsonString);
Book deserializedBook = objectMapper.readValue(jsonString, Book.class);
System.out.println("反序列化后的对象:" + deserializedBook);
}
} 序列化后的JSON:{"name":"Java并发编程","price":121.99}
反序列化后的对象:Book{name='Java并发编程', price=121.99}4. XML Serialization Libraries
XML is suitable for describing complex data structures. JAXB and XStream are common Java XML binding tools.
Features : Extensible, good for complex structures.
Applicable Scenarios : Configuration files, SOAP web services, cross‑platform data exchange.
JAXB example (requires a no‑arg constructor):
@XmlRootElement
public class Customer {
private String name;
private int age;
public Customer() {}
public Customer(String name, int age) { this.name = name; this.age = age; }
@XmlElement
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@XmlElement
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() { return "Customer{name='" + name + "', age=" + age + "}"; }
}Test code:
public class XmlSerializableTest {
public static void main(String[] args) throws Exception {
Customer customer = new Customer("清宇Java", 18);
// Serialize to XML
JAXBContext context = JAXBContext.newInstance(Customer.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(customer, System.out);
// Deserialize from XML string
Unmarshaller unmarshaller = context.createUnmarshaller();
Customer deserializedCustomer = (Customer) unmarshaller.unmarshal(
new java.io.StringReader("<customer><name>清宇Java</name><age>18</age></customer>")
);
System.out.println("
反序列化对象:" + deserializedCustomer);
}
} <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<age>18</age>
<name>清宇Java</name>
</customer>
反序列化对象:Customer{name='清宇Java', age=18}Why JAXB needs a no‑argument constructor:
JAXB creates object instances via reflection; without a no‑arg constructor it cannot instantiate the class, leading to deserialization failure.
Parameterised constructors cannot be invoked automatically, so JAXB relies on the default constructor and then sets fields via setters or direct field access.
5. Binary Serialization Libraries
For high‑performance and large‑scale data transfer, binary serialization libraries such as Kryo and Protocol Buffers provide compact, fast serialization.
Features : Efficient, small payload, cross‑language support.
Applicable Scenarios : Distributed systems, big‑data pipelines, performance‑critical applications.
6. Other Serialization Methods
Additional frameworks include:
Avro : Apache Avro, widely used in Hadoop ecosystems for big‑data serialization.
Thrift : Apache Thrift, a cross‑language RPC framework with its own binary protocol.
Summary of Serialization Methods
Serialization Method | Features | Use Cases | Advantages | Disadvantages
---------------------|----------|-----------|------------|--------------
Serializable | Native Java serialization | Java object storage & transfer | Simple, direct | Poor performance, large size
Externalizable | Manual control of serialization | Custom serialization logic | Flexible | Requires more code
JSON (Jackson) | Human‑readable JSON | Front‑end/back‑end communication, APIs | Readable, language‑agnostic | Larger payload, slower than binary
XML (JAXB) | Structured XML | Config files, SOAP services | Extensible | Verbose, slower parsing
Binary (Protostuff) | Efficient binary format | Big data, distributed systems | High performance, cross‑language | Complex setup, extra dependencies4. Precautions During Serialization
serialVersionUID : A unique identifier for a serializable class. Declaring it explicitly avoids InvalidClassException when class definitions change.
transient Keyword : Fields marked transient are ignored during serialization, preventing sensitive data (e.g., passwords) from being persisted.
Object Deep Copy : Serialization can be used to create a deep copy of an object by serializing to a byte array and deserializing back.
5. Common Issues and Solutions
Avoid serializing sensitive data : Mark such fields as transient so they are omitted from the byte stream.
Class version changes : Define a stable serialVersionUID to maintain compatibility across versions.
Performance concerns : For high‑throughput scenarios, consider binary formats like Protobuf, Kryo, or Avro instead of default Java serialization.
Static vs Transient in Serialization
static Keyword
Definition : The static keyword marks class‑level variables or methods, meaning they belong to the class itself rather than any instance.
Serialization Process : Static fields are not serialized because they are not part of the object’s state.
Deserialization Process : Upon deserialization, static fields retain the current class‑level values, not values from the serialized data.
transient Keyword
Definition : The transient keyword marks instance fields that should be skipped during serialization.
Serialization Process : Transient fields are omitted from the byte stream.
Deserialization Process : Transient fields are restored to their default values (e.g., 0 for numbers, null for objects).
Understanding these differences helps you control what data gets persisted and transmitted, ensuring both efficiency and security.
Feel free to leave comments or questions for further discussion!
Xuanwu Backend Tech Stack
Primarily covers fundamental Java concepts, mainstream frameworks, deep dives into underlying principles, and JVM internals.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
