Object Graph (đồ thị đối tượng) là tập hợp các object liên kết với nhau qua reference. Khi serialize một object root, Java Serialization tự động serialize toàn bộ object graph reachable từ object đó (deep serialization).
Cơ chế
- Object implements
Serializable(marker interface). - Khi
ObjectOutputStream.writeObject(root)được gọi: - JVM traverse object graph từ root (DFS).
- Mỗi object encountered được ghi vào stream (nếu chưa ghi).
- Reference cycles được xử lý (handle mechanism) – không infinite loop.
- Deserialize: tái tạo graph y hệt, giữ nguyên reference sharing.
Ví dụ: Object Graph lồng nhau
import java.io.*;
class Address implements Serializable {
private String city;
private String street;
public Address(String city, String street) { this.city = city; this.street = street; }
@Override public String toString() { return street + ", " + city; }
}
class Person implements Serializable {
private String name;
private Address address; // Reference to another Serializable object
private List phones; // Collection of Serializable
public Person(String name, Address address, List phones) {
this.name = name; this.address = address; this.phones = phones;
}
@Override public String toString() { return name + " @ " + address + " | Phones: " + phones; }
}
public class ObjectGraphSerialization {
public static void main(String[] args) {
// Tạo object graph
Address addr = new Address("Hanoi", "123 Nguyen Trai");
Person person = new Person("Nguyen Van A", addr, List.of("0901234567", "0987654321"));
// Serialize
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
oos.writeObject(person);
System.out.println("Serialized: " + person);
} catch (IOException e) { e.printStackTrace(); }
// Deserialize
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person restored = (Person) ois.readObject();
System.out.println("Deserialized: " + restored);
System.out.println("Same address object? " + (restored.address == addr)); // false (new object)
System.out.println("Address content equal? " + restored.address.equals(addr)); // true
} catch (IOException | ClassNotFoundException e) { e.printStackTrace(); }
}
}
Điều kiện: Tất cả object trong graph phải Serializable
- Nếu một field reference object không implements Serializable →
NotSerializableException. - Giải pháp: đánh dấu field
transient(bỏ qua khi serialize) hoặc implementSerializablecho class đó.
class Person {
private transient ThreadLocal threadLocal; // Không serialize
private Logger logger = Logger.getLogger(...); // Không Serializable → transient
// Custom serialization cho field phức tạp
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(logger.getName()); // Chỉ ghi tên logger
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
this.logger = Logger.getLogger((String) in.readObject());
}
}
Lưu ý quan trọng
- serialVersionUID: Khai báo explicit
private static final long serialVersionUID = 1L;để tránh InvalidClassException khi class thay đổi. - Performance: Java Serialization chậm, verbose, không secure. Production nên dùng: JSON (Jackson/Gson), Protocol Buffers, Avro, MessagePack.
- Security: Deserialize untrusted data → RCE (Remote Code Execution). Không bao giờ deserialize data từ nguồn không tin cậy.
- Inheritance: Superclass không Serializable → fields của superclass không được serialize (trừ khi superclass có no-arg constructor).
Alternatives hiện đại
| Format | Thư viện | Ưu điểm |
|---|---|---|
| JSON | Jackson, Gson | Human-readable, web standard, schema-less |
| Protocol Buffers | protobuf-java | Binary, compact, schema evolution, fast, multi-lang |
| Avro | Apache Avro | Schema-based, compact, splittable, Hadoop ecosystem |
| MessagePack | msgpack-java | Binary JSON-like, no schema needed |