2009-08-29 26 views
22

Tôi đang sử dụng giao diện Java SerializableObjectOutputStream để sắp xếp các đối tượng (cho đến bây giờ, phương pháp này đã đủ cho các mục đích của tôi).Việc tuần tự hóa có duy trì nhận dạng đối tượng không?

API của tôi dựa vào nhận dạng đối tượng cho một số hoạt động và tôi tự hỏi liệu nó có được bảo tồn theo tuần tự hóa hay không. Đó là để nói: nếu, đối với hai đối tượng tùy ý ab, nó giữ a == b trước khi tuần tự hóa, nó vẫn giữ sau khi deserialization?

Tôi đã tìm thấy một số văn bản claim the contrary - nhưng họ đã viết về phiên bản cũ hơn của JRE (Tôi chỉ quan tâm đến 1.6 và có thể 1.5) hoặc quan tâm đến RMI (không liên quan đến tôi)).

documentation không phải là rất sắp tới về nhận dạng đối tượng. Một technical article trên sun.com đề cập rằng ObjectOutputStream sử dụng bộ nhớ đệm trên các đối tượng, mà đối với tôi chỉ có ý nghĩa nếu nhận dạng đối tượng thực sự được giữ nguyên nhưng tôi không đủ tự tin để dựa vào bằng chứng mỏng manh này.

Tôi đã thử nó ra (Java 1.6, OS X) và thấy rằng , danh tính của đối tượng vẫn không thay đổi bởi serialization. Nhưng tôi có thể ngoại suy từ những kết quả này hay chúng không đáng tin cậy?

Đối với thử nghiệm của tôi, tôi đã đăng đồ thị đối tượng sau đây:

C----------+ 
| b1 b2 | 
+----------+ 
    |  | 
    v  v 
B---+ B---+ 
| a | | a | 
+---+ +---+ 
    \ /
    \/
    \/ 
    A----+ 
    | | 
    +----+ 

Một mã sinh sản tối thiểu:

import java.io.*; 

public class SerializeTest { 
    static class A implements Serializable {} 

    static class B implements Serializable { 
     final A a; 

     public B(A a) { 
      this.a = a; 
     } 
    } 

    static class C implements Serializable { 
     final B b1, b2; 

     public C() { 
      A object = new A(); 
      b1 = b2 = new B(object); 
     } 
    } 

    public static void main(String[] args) throws IOException, 
      ClassNotFoundException { 
     C before = new C(); 
     System.out.print("Before: "); 
     System.out.println(before.b1.a == before.b2.a); 

     // Serialization. 
     ByteArrayOutputStream data = new ByteArrayOutputStream(); 
     ObjectOutputStream out = new ObjectOutputStream(data); 
     out.writeObject(before); 
     out.close(); 

     // Deserialization. 
     ObjectInputStream in = 
      new ObjectInputStream(new ByteArrayInputStream(data.toByteArray())); 
     C after = (C) in.readObject(); 
     System.out.print("After: "); 
     System.out.println(after.b1.a == after.b2.a); 
    } 
} 
+0

Nếu nó giữ rằng == b thì chúng không phải là đối tượng tùy ý. Tôi tin rằng câu hỏi của bạn đi cho các tài liệu tham khảo. –

Trả lời

18

Đối với hai đối tượng a và b tùy ý, nếu nó giữ a == b trước khi serialization, nó sẽ vẫn giữ đúng sau khi deserialization NẾU:

  1. Cả a và b được viết như sau và sau đó đọc từ như các bộ phận của cùng một luồng.Dưới đây là trích dẫn từ tài liệu ObjectInputStream: "Đồ thị của đối tượng được khôi phục chính xác bằng cách sử dụng cơ chế chia sẻ tham chiếu cơ chế".
  2. Loại a và b không ghi đè readResolve() có khả năng thay đổi cách tham chiếu được khôi phục lại; không làm các lớp giữ a và b.

Đối với tất cả các trường hợp khác, nhận dạng đối tượng sẽ KHÔNG được giữ nguyên.

+0

đặt tốt hơn tôi nghĩ rằng –

+2

Có thể muốn thêm hậu quả của việc sử dụng phương thức writeUnshared() của lớp ObjectOutputStream, cho câu trả lời của bạn. Nó kết thúc việc tạo ra các đối tượng độc đáo mới trên luồng. Có thêm thông tin tại Đặc điểm kỹ thuật tuần tự hóa đối tượng Java tại http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf –

+0

Cảm ơn, đó chính xác là những gì tôi đang tìm kiếm. Trong trường hợp của tôi, tôi may mắn. Tuy nhiên, tài liệu 'ObjectInputStream' có thể rõ ràng hơn ở đây. Không có định nghĩa, "chia sẻ tham chiếu" là một thuật ngữ khá mờ đục. @Vineet: cảm ơn vì đã liên kết các thông số kỹ thuật. –

10

Câu trả lời là không , bởi nhận dạng đối tượng mặc định không được bảo quản thông qua serialization nếu bạn đang xem xét 2 serializations riêng biệt của một đối tượng/đồ thị nhất định. Ví dụ, nếu tôi tuần tự hóa một đối tượng trên dây (có lẽ tôi gửi nó từ máy khách đến máy chủ qua RMI), và sau đó thực hiện lại (trong các cuộc gọi RMI riêng biệt), thì hai đối tượng được deserialized trên máy chủ sẽ không be ==.

Tuy nhiên, trong một "tuần tự hóa đơn", ví dụ: một thông điệp máy khách-máy chủ duy nhất là một biểu đồ chứa cùng một đối tượng nhiều lần sau đó khi deserialization, danh tính được giữ nguyên.

Trong trường hợp đầu tiên, bạn có thể cung cấp phương pháp readResolve để đảm bảo rằng bản sao chính xác được trả lại (ví dụ: trong mẫu an toàn enum an toàn). readResolve là một phương thức riêng sẽ được gọi bởi JVM trên một đối tượng Java được tuần tự hóa, cho đối tượng cơ hội trả về một cá thể khác. Ví dụ, đây là cách TimeUnitenum có thể đã được thực hiện trước khi enum 's đã được thêm vào ngôn ngữ:

public class TimeUnit extends Serializable { 

    private int id; 
    public TimeUnit(int i) { id = i; } 
    public static TimeUnit SECONDS = new TimeUnit(0); 

    //Implement method and return the relevant static Instance 
    private Object readResolve() throws ObjectStreamException { 
     if (id == 0) return SECONDS; 
     else return this; 
    } 
} 

.

+1

Cảm ơn. Sử dụng 'readResolve' là chính xác những gì tôi muốn tránh vì sổ sách kế toán sẽ trở nên phức tạp hơn nhiều so với mẫu enum an toàn trong trường hợp của tôi. May mắn cho tôi, tôi chỉ quan tâm đến việc tuần tự hóa đơn. –

Các vấn đề liên quan