Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Java

Object Graph ใน Java Serialization

Object Graph ใน Java Serialization

Object Graph คือโครงสร้างของออบเจกต์ที่เชื่อมโยงกันผ่าน Reference เมื่อทำ Serialization ออบเจกต์ราก (Root Object) ทั้งกราฟที่เชื่อมต่อกันจะถูก Serialize อัตโนมัติ (Transitive Serialization)

หลักการทำงาน

  • JVM ติดตาม Object References ทั้งหมดจาก Root Object
  • ทุกออบเจกต์ในกราฟต้อง implement Serializable
  • ใช้ Object Identity (==) เพื่อไม่ให้ Serialize ซ้ำ (Handle sharing)
  • Circular references รองรับได้ (A→B→A)

ตัวอย่างโค้ดสมบูรณ์

import java.io.*;
import java.io.Serializable;

// Level 1
class One implements Serializable {
    private static final long serialVersionUID = 1L;
    Two two = new Two(); // Reference to Level 2
}

// Level 2
class Two implements Serializable {
    private static final long serialVersionUID = 1L;
    Three three = new Three(); // Reference to Level 3
}

// Level 3 (Leaf)
class Three implements Serializable {
    private static final long serialVersionUID = 1L;
    int i = 34;
    int j = 67;
}

public class DemoSerialize {
    public static void main(String[] args) throws Exception {
        // สร้าง Object Graph
        One root = new One();
        
        // Serialize
        try (FileOutputStream fos = new FileOutputStream("object_graph.ser");
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            
            oos.writeObject(root);
            System.out.println("Serialized successfully");
        }
        
        // Deserialize
        try (FileInputStream fis = new FileInputStream("object_graph.ser");
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            
            One restored = (One) ois.readObject();
            
            // เข้าถึงข้อมูลลึกในกราฟ
            System.out.println("ค่า i หลัง Deserialize: " + restored.two.three.i);
            System.out.println("ค่า j หลัง Deserialize: " + restored.two.three.j);
            
            // ตรวจสอบว่าเป็น Object ใหม่ (ไม่ใช่ Same Reference)
            System.out.println("Same object? " + (root == restored)); // false
            System.out.println("Same nested? " + (root.two == restored.two)); // false
        }
    }
}

ผลลัพธ์

Serialized successfully
ค่า i หลัง Deserialize: 34
ค่า j หลัง Deserialize: 67
Same object? false
Same nested? false

การควบคุม Serialization

1. transient - ไม่ Serialize ฟิลด์นี้

class User implements Serializable {
    String username;
    transient String password; // ไม่ serialize (Security)
    transient Thread thread;   // Non-serializable object
}

2. custom writeObject/readObject

class CustomSerialization implements Serializable {
    private transient String cache; // ไม่ serialize
    
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        // เขียน cache อย่างอื่น (เช่น encrypt)
        out.writeObject(encrypt(cache));
    }
    
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        // อ่านและ decrypt
        this.cache = decrypt((String) in.readObject());
    }
}

3. Externalizable Interface (ควบคุมเต็มที่)

class MyData implements Externalizable {
    int id;
    String data;
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(id);
        out.writeUTF(data);
    }
    
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.id = in.readInt();
        this.data = in.readUTF();
    }
}

serialVersionUID

  • Version control สำหรับ Serialization
  • ถ้าไม่ระบุ JVM คำนวณจากโครงสร้างคลาส (อาจเปลี่ยนเมื่อ compile ใหม่)
  • แนะนำ: ประกาศ private static final long serialVersionUID = 1L; เอง
  • เปลี่ยน UID เมื่อโครงสร้างเปลี่ยน incompatible (ลบ field, เปลี่ยน type)

ข้อควรระวัง

  • Security: Deserialization ไม่เชื่อถือ → ใช้ ObjectInputFilter (Java 9+)
  • Performance: Java Serialization ช้า ใหญ่ ใช้ JSON (Jackson/Gson) หรือ Protobuf แทน
  • Versioning: การเปลี่ยนแปลงคลาสต้องระวัง (เพิ่ม field OK, ลบ field ต้องมี default)
  • Circular refs: รองรับได้ แต่อาจ StackOverflow ถ้าลึกมาก

ทางเลือกทันสมัย

  • JSON: Jackson, Gson - Human readable, Cross-language
  • Protocol Buffers: Google Protobuf - Binary, Schema evolution, Fast
  • MessagePack: Binary JSON-like
  • Java Records (Java 16+): Serializable โดยอัตโนมัติ เหมาะกับ Data Carrier