2009-05-21 45 views
59

Trong java, nếu một lớp triển khai Serializable nhưng là trừu tượng, nó có phải là một serialVersionUID được khai báo dài hay các lớp con chỉ yêu cầu nó?Nếu một lớp trừu tượng có serialVersionUID

Trong trường hợp này, nó thực sự là ý định rằng tất cả các lớp con đối phó với tuần tự hóa vì mục đích của loại này sẽ được sử dụng trong các cuộc gọi RMI.

+3

Tôi tiếp tục viết câu trả lời, sau đó nhận ra tôi không biết, mặc dù tôi có linh cảm. 1 cho một câu hỏi tôi không thể trả lời. –

Trả lời

40

SerialVersionUID được cung cấp để xác định khả năng tương thích giữa đối tượng đã được loại bỏ và phiên bản hiện tại của lớp. Như vậy, nó không thực sự cần thiết trong phiên bản đầu tiên của một lớp, hoặc trong trường hợp này, trong một lớp cơ sở trừu tượng. Bạn sẽ không bao giờ có một thể hiện của lớp trừu tượng đó để tuần tự hóa/deserialize, vì vậy nó không cần một serialVersionUID.

(Tất nhiên, nó tạo ra một cảnh báo trình biên dịch, mà bạn muốn để có được thoát khỏi, phải không?)

Hóa comment james' là đúng. SerialVersionUID của lớp cơ sở trừu tượng không được truyền cho các lớp con. Do đó, bạn cần làm cần serialVersionUID trong lớp cơ sở của mình.

Mã để kiểm tra:

import java.io.Serializable; 

public abstract class Base implements Serializable { 

    private int x = 0; 
    private int y = 0; 

    private static final long serialVersionUID = 1L; 

    public String toString() 
    { 
     return "Base X: " + x + ", Base Y: " + y; 
    } 
} 



import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 

public class Sub extends Base { 

    private int z = 0; 

    private static final long serialVersionUID = 1000L; 

    public String toString() 
    { 
     return super.toString() + ", Sub Z: " + z; 
    } 

    public static void main(String[] args) 
    { 
     Sub s1 = new Sub(); 
     System.out.println(s1.toString()); 

     // Serialize the object and save it to a file 
     try { 
      FileOutputStream fout = new FileOutputStream("object.dat"); 
      ObjectOutputStream oos = new ObjectOutputStream(fout); 
      oos.writeObject(s1); 
      oos.close(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     Sub s2 = null; 
     // Load the file and deserialize the object 
     try { 
      FileInputStream fin = new FileInputStream("object.dat"); 
      ObjectInputStream ois = new ObjectInputStream(fin); 
      s2 = (Sub) ois.readObject(); 
      ois.close(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     System.out.println(s2.toString()); 
    } 
} 

Chạy chính trong Sub một lần để có được nó để tạo và lưu một đối tượng. Sau đó thay đổi serialVersionUID trong lớp Base, chú thích các dòng trong chính để lưu đối tượng (vì vậy nó không lưu lại nó, bạn chỉ muốn tải một cái cũ), và chạy lại nó. Điều này sẽ dẫn đến một ngoại lệ là

java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 
+0

Câu trả lời hay ... @SuppressWarnings ("nối tiếp") sẽ chặn thông điệp cảnh báo –

+1

@Ryan: Cảm ơn, nhưng tôi thường coi cảnh báo như lỗi và xử lý trực tiếp. –

+1

... nhưng tôi hiểu rằng không phải ai cũng có tính giáo điều về nó như tôi, vì vậy bình luận của bạn được đánh giá cao. –

6

Có, lý do tương tự mà bất kỳ lớp nào khác cần có ID nối tiếp - để tránh bị tạo ra. Về cơ bản bất kỳ lớp nào (không phải giao diện) thực hiện serializable nên định nghĩa id phiên bản nối tiếp hoặc bạn có nguy cơ lỗi de-serialization khi cùng một biên dịch .class không có trong máy chủ và máy khách JVM.

Có các tùy chọn khác nếu bạn đang cố gắng làm điều gì đó lạ mắt. Tôi không chắc ý bạn là gì bởi "đó là ý định của các lớp phụ ...". Bạn sẽ viết các phương thức tuần tự hóa tùy chỉnh (ví dụ: writeObject, readObject)? Nếu có các tùy chọn khác để xử lý một lớp siêu hạng.

see: http://java.sun.com/javase/6/docs/api/java/io/Serializable.html

HTH Tom

2

Trên thực tế, chỉ ra các liên kết của Tom nếu thiếu serialVersionID thực sự được tính bằng tuần tự thời gian chạy tức là không trong biên soạn

Nếu một lớp serializable không khai báo rõ ràng serialVersionUID, sau đó thời gian chạy tuần tự hóa sẽ tính giá trị serialVersionUID mặc định là cho cla đó ss dựa trên các khía cạnh khác nhau của lớp học ...

Điều này làm cho mọi thứ phức tạp hơn với các phiên bản JRE khác nhau.

0

Về mặt lý thuyết, các dữ liệu tuần tự giống như thế này:

subClassData(className + version + fieldNames + fieldValues) 
parentClassData(className + version + fieldNames + fieldValues) 
... (up to the first parent, that implements Serializable) 

Vì vậy, khi bạn deserialize, không phù hợp trong phiên bản trong bất kỳ các lớp trong hệ thống phân cấp làm cho deserialization thất bại. Không có gì được lưu trữ cho các giao diện, vì vậy không cần phải chỉ định phiên bản cho chúng.

Trả lời: có, bạn cũng cần phải cung cấp serialVersionUID trong lớp trừu tượng cơ bản. Ngay cả khi nó không có các trường (className + version được lưu trữ ngay cả khi không có trường nào).

Cũng lưu ý những điều sau:

  1. Nếu lớp không có một trường, được tìm thấy trong các dữ liệu tuần tự (một lĩnh vực loại bỏ), nó được bỏ qua.
  2. Nếu lớp có trường, không có trong dữ liệu tuần tự (trường mới), nó được đặt thành 0/false/null (không phải giá trị mặc định như mong đợi).
  3. nếu trường thay đổi kiểu dữ liệu, giá trị được deserialized phải được gán cho loại mới. Ví dụ. nếu bạn có trường Object với giá trị String, việc thay đổi loại trường thành String sẽ thành công nhưng thay đổi thành Integer thì không. Tuy nhiên, việc thay đổi trường từ int thành long sẽ không hoạt động, mặc dù bạn có thể gán giá trị int cho biến số long.
  4. Nếu lớp con không còn mở rộng lớp cha, mà nó mở rộng trong dữ liệu tuần tự hóa, nó sẽ bị bỏ qua (như trong trường hợp 1).
  5. Nếu một lớp con bây giờ mở rộng một lớp, không tìm thấy trong dữ liệu tuần tự, các trường lớp cha được khôi phục với giá trị 0/false/null (như trong trường hợp 2).

Nói cách đơn giản: bạn có thể sắp xếp lại các trường, thêm và xóa chúng, thậm chí thay đổi thứ bậc lớp. Bạn không nên đổi tên các trường hoặc các lớp (nó sẽ không thất bại, nhưng các giá trị sẽ không được deserialized). Bạn không thể thay đổi loại trường có kiểu nguyên thủy và bạn có thể thay đổi các trường loại tham chiếu được cung cấp loại mới có thể gán từ tất cả các giá trị.

Lưu ý: nếu lớp cơ sở không triển khai Serializable và chỉ có lớp con thì trường từ lớp cơ sở sẽ hoạt động như transient.

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