2009-06-17 53 views
27

Khi chúng tôi tuần tự hóa các đối tượng, các thành viên tĩnh không được tuần tự hóa, nhưng nếu chúng ta cần làm như vậy, có cách nào không?Làm thế nào để tuần tự hóa các thành viên dữ liệu tĩnh của một lớp Java?

+18

Dừng sử dụng các thành viên tĩnh cho dữ liệu đó phải được tuần tự? – krosenvold

+7

Phụ lục: ngừng sử dụng thành viên tĩnh để giữ trạng thái có thể thay đổi. – Juliet

+0

+1, nếu bạn tuần tự hóa, không sử dụng các thành viên tĩnh – marcgg

Trả lời

18

Câu hỏi đầu tiên là tại sao bạn cần phải tuần tự hóa các thành viên tĩnh?

Thành viên tĩnh được liên kết với lớp học, chứ không phải trường hợp, do đó, không có ý nghĩa khi đưa chúng vào khi sắp xếp một phiên bản.

Giải pháp đầu tiên là làm cho các thành viên đó không tĩnh. Hoặc, nếu những thành viên đó giống nhau trong lớp ban đầu và lớp đích (cùng một lớp, nhưng có thể là môi trường thời gian chạy khác nhau), thì đừng tuần tự hóa chúng.

Tôi có một vài suy nghĩ về cách người dùng có thể gửi qua các thành viên tĩnh, nhưng trước tiên tôi cần xem trường hợp sử dụng, như trong mọi trường hợp có nghĩa là cập nhật lớp đích và tôi không tìm thấy lý do chính đáng vì thế.

+2

giả sử tôi muốn đếm số lượng cá thể của một lớp như Xe hơi. và tôi không sử dụng cơ sở dữ liệu .so trong trường hợp đó sau khi tắt ứng dụng, tôi cần phải lưu trữ thông tin này với các thông tin khác. –

+4

Bạn có cần lưu số đó trực tiếp không? Nếu bạn cập nhật số khi bạn deserialize mỗi chiếc xe (bằng cách ghi đè readObject) nó sẽ phản ánh chính xác numbr của chiếc xe trong serialization. –

+0

Biến tĩnh được chia sẻ giữa tất cả các cá thể của đối tượng của lớp (bộ nhớ dùng chung). Nếu bạn cần lưu một cái gì đó tĩnh, giống như cho phép bạn có một giá trị khác nhau và thỉnh thoảng bạn thay đổi giá trị tĩnh của kiểu enum đó, việc thay đổi giá trị đã lưu có thể đơn giản như tất cả các lớp đã thiết lập một biến cục bộ loại đó.Hoặc bạn có thể tiến thêm một bước nữa, và có một nhóm các lớp mở rộng Dù là gì, và bất cứ thứ gì có thể chỉ chia sẻ một biến cục bộ cho tất cả các con của nó. Khi bạn deserialize bạn có thể tải lại các giá trị tĩnh của bạn trở lại như thế nào. –

2

Thành viên tĩnh thuộc về lớp học, không phải cho các đối tượng riêng lẻ.

Bạn nên xem xét lại cấu trúc dữ liệu của mình.

5

Bạn có thể kiểm soát bằng cách thực hiện tuần tự:

private void writeObject(ObjectOutputStream out) throws IOException; 

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException; 

Có một mô tả đầy đủ của serialization http://java.sun.com/developer/technicalArticles/Programming/serialization/.

Như các câu trả lời khác đã nói, nó không thực sự có ý nghĩa để sắp xếp các số liệu thống kê vì nó là đối tượng không phải lớp bạn đang sắp xếp và cần phải làm như vậy có mùi như bạn đã có các vấn đề khác với mã của bạn.

+0

Bạn có thể kiểm soát tuần tự hóa, nhưng không có nơi nào hợp lý để đặt dữ liệu tĩnh bởi vì nó không có ý nghĩa gì trong ngữ cảnh. –

+0

Vì chúng ta không biết bối cảnh, chúng ta không thể nói nó có ý nghĩa hay không. Ví dụ, sau khi sử dụng serialization mặc định, nó có thể có ý nghĩa để thêm thành viên tĩnh vào luồng. Tại deserialization, người ta có thể thiết lập các thành viên tĩnh nếu nó là null, nếu không loại bỏ các đối tượng serialized. – erickson

+0

Tom, bạn đang lạm dụng "bằng chứng lặp lại", phải không? ;) –

2

Câu trả lời và nhận xét hay - đừng làm điều đó. Nhưng bằng cách nào?

Rất có thể bạn nên tắt một đối tượng để giữ tất cả "Statics" của mình. Đối tượng đó có lẽ nên có bất kỳ phương thức tĩnh nào từ lớp của bạn.

Mỗi trường hợp lớp học của bạn có thể tổ chức lớp học khác này - hoặc nếu bạn thực sự có để bạn có thể làm cho nó thành một singleton mà bất kỳ thành viên nào cũng có thể truy cập.

Sau khi bạn thực hiện công cụ này, bạn sẽ thấy rằng nó phải được thực hiện theo cách này. Bạn thậm chí có thể thấy rằng một số hạn chế thiết kế trước đó của bạn đã làm phiền bạn ở cấp độ subconsicionce đã biến mất.

Có thể bạn sẽ thấy rằng giải pháp này cũng giải quyết được các vấn đề khác về Serialization mà bạn chưa từng nhận thấy.

14

Mọi người, tĩnh không có nghĩa là IMMUTABLE. Ví dụ, tôi có thể muốn tuần tự hóa toàn bộ trạng thái tính toán (có, bao gồm các trường tĩnh - bộ đếm, vv) để tiếp tục sau, sau khi JVM và/hoặc máy tính chủ khởi động lại.

Câu trả lời đúng cho điều đó, như đã nói, là sử dụng giao diện Externalizable, chứ không phải Serializable. Sau đó, bạn có quyền kiểm soát hoàn toàn về những gì và cách bạn ngoại giao.

+1

Nếu trạng thái của tính toán không được nhúng trong đối tượng bạn đang tuần tự hóa thì bạn không nên lưu trữ nó như một phần của việc tuần tự hóa đối tượng đó. – DJClayworth

+3

Biến đổi tĩnh thường có nghĩa là bị hỏng. –

+1

Hãy suy nghĩ lại. Ví dụ về các thuật toán di truyền đa luồng? Bộ nhớ chia sẻ (tĩnh) được sử dụng để giữ sổ đăng ký các biến thể được đánh giá. Làm cho nó một singleton? Có thể, nhưng nó có những hạn chế riêng. –

0

Có, chúng tôi có thể tuần tự hóa các biến tĩnh. Nhưng chúng tôi có thể viết riêng của chúng tôi writeObject()readObject(). Tôi nghĩ rằng điều này có thể giải quyết vấn đề.

0

Để có thực hiện nhỏ gọn, thực hiện readObject & writeObject trong cuộc gọi lớp học của bạn defaultReadObject & phương pháp defaultWriteObject trong các phương pháp đó mà xử lý tuần tự bình thường và sau đó tiến hành tuần tự & de-serializing bất cứ lĩnh vực bổ sung mà bạn cần.

Kính trọng, GK

5

Đây là tuần tự cho trường tĩnh: newBookingNumber.

class Booking implements Serializable 
{ 

    /** 
    * Generated serial version ID. 
    */ 

    private static final long serialVersionUID = 5316748056989930874L; 

    // To hold new booking number. 
    private static int newBookingNumber = 0; 

    // The booking number. 
    private int bookingNumber; 


    /* 
    * Default serializable fields of a class are defined to be 
    * the non-transient and non-static fields. So, we have to 
    * write and read the static field separately. 
    */ 
    private void writeObject(ObjectOutputStream oos) 
     throws IOException 
    { 
     oos.defaultWriteObject(); 
     oos.writeObject(new Integer(newBookingNumber)); 
    } 

    private void readObject(ObjectInputStream ois) 
    throws ClassNotFoundException, IOException 
    { 
     ois.defaultReadObject(); 
     newBookingNumber = (Integer)ois.readObject(); 
    } 
} 
2

Bạn có thể thực hiện việc này mà không phải cập nhật lớp học theo cách thủ công mỗi lần bạn chỉ cần thay đổi trường. Bạn có thể muốn thực hiện việc này nếu bạn muốn có các thành viên tĩnh dễ truy cập vào cài đặt trong một ứng dụng, nhưng cũng muốn lưu các cài đặt đó. Trong trường hợp này, bạn cũng sẽ muốn có tùy chọn để áp dụng chúng lúc đầu, không tải theo mặc định vì các giải pháp khác ở đây cần thiết, vì chúng là tĩnh. Điều này cho phép khôi phục cài đặt vì lý do hiển nhiên.

Về cơ bản, sử dụng các phương thức trường để nhận tất cả các thành viên trong lớp, sau đó ánh xạ tên đầy đủ của các trường này vào nội dung. Tên đầy đủ là bắt buộc vì Field không được tự tuần tự hóa. Sắp xếp lại bản đồ này và phục hồi nó để nhận các cài đặt đã lưu.

Phần thứ hai của câu đố là loại áp dụng() của hàm. Điều này đi qua việc lập bản đồ, và áp dụng những gì nó có thể cho lớp tĩnh.

Bạn cũng phải đảm bảo rằng nội dung của các thành viên tĩnh được tự tuần tự.

Như hy vọng có thể thấy từ lớp ví dụ này, các thành viên tĩnh có thể dễ dàng được lưu và trả về. Tôi sẽ để nó cho người triển khai lo lắng về UID của các lớp, các biện pháp bảo vệ, vv isSameAs() được sử dụng để kiểm thử đơn vị. AppSettings là lớp chứa tất cả các trường tĩnh mà bạn muốn tuần tự hóa.

public class AppSettingsReflectorSaver implements Serializable { 

HashMap<String, Object> genericNamesAndContents = new HashMap<String, Object>(); 
private AppSettingsReflectorSaver() { 
} 

static AppSettingsReflectorSaver createAppSettingsSaver() { 
    AppSettingsReflectorSaver ret = new AppSettingsReflectorSaver(); 
    ret.copyAppSettings(); 
    return ret; 
} 

private void copyAppSettings() { 
    Field[] fields = AppSettings.class.getFields(); 
    for (Field field : fields) { 
     mapContentsForSerialization(field); 
    } 
} 

private void mapContentsForSerialization(Field field) { 
    try { 
     Object fieldContents = field.get(AppSettings.class); 
     genericNamesAndContents.put(field.toGenericString(), fieldContents); 
    } catch (IllegalArgumentException ex) { 
     Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex); 
    } catch (IllegalAccessException ex) { 
     Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex); 
    } 
} 

boolean isSameAs(AppSettingsReflectorSaver now) { 
    for(String thisKey : genericNamesAndContents.keySet()){ 
     boolean otherHasThisKey = now.genericNamesAndContents.containsKey(thisKey); 
     Object thisObject = genericNamesAndContents.get(thisKey); 
     Object otherObject = now.genericNamesAndContents.get(thisKey); 
     boolean otherHasThisValue = thisObject.equals(otherObject); 
     if (!otherHasThisKey || !otherHasThisValue){ 
      return false; 
     } 
    } 
    return true; 
} 

void applySavedSettingsToStatic() { 
    Field[] fields = AppSettings.class.getFields(); 
    for (Field field : fields) { 
     if (!genericNamesAndContents.containsKey(field.toGenericString())){ 
      continue; 
     } 
     Object content = genericNamesAndContents.get(field.toGenericString()); 
     try { 
      field.set(AppSettings.class, content); 
     } catch (IllegalArgumentException ex) { 
      Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex); 
     } catch (IllegalAccessException ex) { 
      Logger.getLogger(AppSettingsReflectorSaver.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 
} 

}

này được bài viết đầu tiên của tôi - đi dễ dàng trên tôi: P ~

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