JavaRush /Blog Java /Random-VI /Tuần tự hóa như nó vốn có. Phần 1
articles
Mức độ

Tuần tự hóa như nó vốn có. Phần 1

Xuất bản trong nhóm
Thoạt nhìn, việc xê-ri hóa có vẻ như là một quá trình tầm thường. Thực sự, điều gì có thể đơn giản hơn? Khai báo lớp để triển khai giao diện java.io.Serializable- và thế là xong. Bạn có thể tuần tự hóa lớp mà không gặp vấn đề gì. Tuần tự hóa như nó vốn có.  Phần 1 - 1Về mặt lý thuyết, điều này là đúng. Trong thực tế, có rất nhiều sự tinh tế. Chúng liên quan đến hiệu suất, quá trình khử tuần tự hóa và sự an toàn của lớp. Và với nhiều khía cạnh khác. Sự tinh tế như vậy sẽ được thảo luận. Bài viết này có thể được chia thành các phần sau:
  • Sự tinh tế của cơ chế
  • Tại sao nó lại cần thiết?Externalizable
  • Hiệu suất
  • nhưng mặt khác
  • Bảo mật dữ liệu
  • Tuần tự hóa đối tượngSingleton
Hãy chuyển sang phần đầu tiên -

Sự tinh tế của cơ chế

Trước hết, một câu hỏi nhanh. Có bao nhiêu cách để làm cho một đối tượng có thể tuần tự hóa? Thực tiễn cho thấy rằng hơn 90% nhà phát triển trả lời câu hỏi này theo cùng một cách (tùy theo cách diễn đạt) - chỉ có một cách. Trong khi đó, có hai người trong số họ. Không phải ai cũng nhớ cái thứ hai chứ đừng nói đến việc nói bất cứ điều gì dễ hiểu về các tính năng của nó. Vậy những phương pháp này là gì? Mọi người đều nhớ cái đầu tiên. Đây là cách thực hiện đã được đề cập java.io.Serializablevà không yêu cầu bất kỳ nỗ lực nào. Phương pháp thứ hai cũng là việc triển khai một giao diện, nhưng là một giao diện khác: java.io.Externalizable. Không giống như java.io.Serializable, nó chứa hai phương thức cần được triển khai - writeExternal(ObjectOutput)readExternal(ObjectInput). Các phương thức này chứa logic tuần tự hóa/giải tuần tự hóa. Bình luận.SerializableTrong phần tiếp theo , đôi khi tôi sẽ đề cập đến việc tuần tự hóa với việc triển khai là tiêu chuẩn và việc triển khai Externalizablelà mở rộng. Khácbình luận. Bây giờ tôi cố tình không đề cập đến các tùy chọn điều khiển tuần tự hóa tiêu chuẩn như xác định readObjectwriteObject, bởi vì Tôi nghĩ những phương pháp này có phần không chính xác. Các phương thức này không được xác định trong giao diện Serializablevà trên thực tế là các công cụ để khắc phục các hạn chế và làm cho việc tuần tự hóa tiêu chuẩn trở nên linh hoạt. ExternalizableCác phương pháp mang lại sự linh hoạt được tích hợp ngay từ đầu . Hãy hỏi thêm một câu hỏi nữa. Việc tuần tự hóa tiêu chuẩn thực sự hoạt động như thế nào khi sử dụng java.io.Serializable? Và nó hoạt động thông qua API Reflection. Những thứ kia. lớp được phân tích cú pháp dưới dạng một tập hợp các trường, mỗi trường được ghi vào luồng đầu ra. Tôi nghĩ rõ ràng là hoạt động này không tối ưu về mặt hiệu suất. Chúng ta sẽ biết chính xác là bao nhiêu sau đó. Có một sự khác biệt lớn khác giữa hai phương pháp xê-ri hóa được đề cập. Cụ thể là trong cơ chế khử lưu huỳnh. Khi được sử dụng, Serializablequá trình khử lưu huỳnh xảy ra như sau: bộ nhớ được phân bổ cho một đối tượng, sau đó các trường của nó chứa đầy các giá trị từ luồng. Hàm tạo của đối tượng không được gọi. Ở đây chúng ta cần xem xét tình huống này một cách riêng biệt. Được rồi, lớp của chúng ta có thể tuần tự hóa được. Còn bố mẹ anh ấy? Hoàn toàn tùy chọn! Hơn nữa, nếu bạn kế thừa một lớp từ Object- lớp cha chắc chắn KHÔNG được tuần tự hóa. Và mặc dù Objectchúng ta không biết gì về các trường, nhưng chúng có thể tồn tại trong các lớp cha của chúng ta. Điều gì sẽ xảy ra với họ? Họ sẽ không tham gia vào luồng tuần tự hóa. Họ sẽ nhận những giá trị gì khi khử lưu huỳnh? Hãy xem ví dụ này:
package ru.skipy.tests.io;

import java.io.*;

/**
 * ParentDeserializationTest
 *
 * @author Eugene Matyushkin aka Skipy
 * @since 05.08.2010
 */
public class ParentDeserializationTest {

    public static void main(String[] args){
        try {
            System.out.println("Creating...");
            Child c = new Child(1);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            c.field = 10;
            System.out.println("Serializing...");
            oos.writeObject(c);
            oos.flush();
            baos.flush();
            oos.close();
            baos.close();
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            System.out.println("Deserializing...");
            Child c1 = (Child)ois.readObject();
            System.out.println("c1.i="+c1.getI());
            System.out.println("c1.field="+c1.getField());
        } catch (IOException ex){
            ex.printStackTrace();
        } catch (ClassNotFoundException ex){
            ex.printStackTrace();
        }
    }

    public static class Parent {
        protected int field;
        protected Parent(){
            field = 5;
            System.out.println("Parent::Constructor");
        }
        public int getField() {
            return field;
        }
    }

    public static class Child extends Parent implements Serializable{
        protected int i;
        public Child(int i){
            this.i = i;
            System.out.println("Child::Constructor");
        }
        public int getI() {
            return i;
        }
    }
}
Nó minh bạch - chúng ta có một lớp cha không thể tuần tự hóa và một lớp con có thể tuần tự hóa. Và đây là những gì xảy ra:
Creating...
Parent::Constructor
Child::Constructor
Serializing...
Deserializing...
Parent::Constructor
c1.i=1
c1.field=5
Nghĩa là, trong quá trình khử tuần tự hóa, hàm tạo không có tham số của lớp cha KHÔNG tuần tự hóa được gọi là . Và nếu không có hàm tạo như vậy, sẽ xảy ra lỗi trong quá trình khử lưu huỳnh. Hàm tạo của đối tượng con, đối tượng mà chúng ta đang giải tuần tự hóa, không được gọi, như đã nói ở trên. Đây là cách các cơ chế tiêu chuẩn hoạt động khi được sử dụng Serializable. Khi sử dụng nó, Externalizabletình hình lại khác. Đầu tiên, hàm tạo không có tham số được gọi, sau đó phương thức readExternal được gọi trên đối tượng đã tạo, đối tượng này thực sự đọc tất cả dữ liệu của nó. Do đó, bất kỳ lớp nào triển khai giao diện Bên ngoài đều phải có một hàm tạo công khai không có tham số! Hơn nữa, vì tất cả các lớp con của một lớp như vậy cũng sẽ được xem xét để triển khai giao diện Externalizable, nên chúng cũng phải có một hàm tạo không tham số! Hãy đi xa hơn nữa. Có một công cụ sửa đổi trường như transient. Nó có nghĩa là trường này không nên được tuần tự hóa. Tuy nhiên, như bạn đã hiểu, hướng dẫn này chỉ ảnh hưởng đến cơ chế xê-ri hóa tiêu chuẩn. Khi sử dụng, Externalizablekhông ai bận tâm đến việc tuần tự hóa trường này cũng như trừ đi nó. Nếu một trường được khai báo là tạm thời thì khi đối tượng được giải tuần tự hóa, nó sẽ nhận giá trị mặc định. Một điểm khá tinh tế khác. Với cách tuần tự hóa tiêu chuẩn, các trường có phần sửa đổi staticsẽ không được tuần tự hóa. Theo đó, sau khi khử lưu huỳnh, trường này không thay đổi giá trị. Tất nhiên, trong quá trình triển khai, Externalizablekhông ai bận tâm đến việc tuần tự hóa và giải tuần tự hóa trường này, nhưng tôi thực sự khuyên bạn không nên làm điều này, bởi vì điều này có thể dẫn đến những sai sót tinh vi. Các trường có công cụ sửa đổi finalđược tuần tự hóa giống như các trường thông thường. Với một ngoại lệ - chúng không thể được giải tuần tự hóa khi sử dụng Bên ngoài. Bởi vì final-поляchúng phải được khởi tạo trong hàm tạo và sau đó sẽ không thể thay đổi giá trị của trường này trong readExternal. Theo đó, nếu bạn cần tuần tự hóa một đối tượng có finaltrường -, bạn sẽ chỉ phải sử dụng cách tuần tự hóa tiêu chuẩn. Một điểm nữa mà nhiều người chưa biết. Việc tuần tự hóa tiêu chuẩn tính đến thứ tự các trường được khai báo trong một lớp. Trong mọi trường hợp, đây là trường hợp của các phiên bản trước; trong phiên bản JVM 1.6 của triển khai Oracle, thứ tự không còn quan trọng nữa mà loại và tên của trường mới quan trọng. Thành phần của các phương pháp rất có thể ảnh hưởng đến cơ chế tiêu chuẩn, mặc dù thực tế là các trường nhìn chung có thể giữ nguyên. Để tránh điều này, có cơ chế sau. Đối với mỗi lớp triển khai giao diện Serializable, một trường nữa sẽ được thêm vào ở giai đoạn biên dịch -private static final long serialVersionUID. Trường này chứa mã định danh phiên bản duy nhất của lớp được xê-ri hóa. Nó được tính toán dựa trên nội dung của lớp - các trường, thứ tự khai báo, phương thức, thứ tự khai báo của chúng. Theo đó, với bất kỳ sự thay đổi nào trong lớp, trường này sẽ thay đổi giá trị của nó. Trường này được ghi vào luồng khi lớp được tuần tự hóa. Nhân tiện, đây có lẽ là trường hợp duy nhất tôi biết khi staticmột trường - được xuất bản. Trong quá trình khử lưu huỳnh, giá trị của trường này được so sánh với giá trị của lớp trong máy ảo. Nếu các giá trị không khớp, một ngoại lệ như thế này sẽ được đưa ra:
java.io.InvalidClassException: test.ser2.ChildExt;
    local class incompatible: stream classdesc serialVersionUID = 8218484765288926197,
                                   local class serialVersionUID = 1465687698753363969
Tuy nhiên, có một cách để nếu không vượt qua được thì hãy đánh lừa việc kiểm tra này. Điều này có thể hữu ích nếu tập hợp các trường lớp và thứ tự của chúng đã được xác định nhưng các phương thức lớp có thể thay đổi. Trong trường hợp này, việc tuần tự hóa không gặp rủi ro, nhưng cơ chế tiêu chuẩn sẽ không cho phép dữ liệu được giải tuần tự hóa bằng cách sử dụng mã byte của lớp đã sửa đổi. Nhưng, như tôi đã nói, anh ta có thể bị lừa. Cụ thể là xác định thủ công trường trong lớp private static final long serialVersionUID. Về nguyên tắc, giá trị của trường này có thể hoàn toàn là bất kỳ giá trị nào. Một số người thích đặt nó bằng ngày mã được sửa đổi. Một số thậm chí còn sử dụng 1L. Để có được giá trị tiêu chuẩn (giá trị được tính toán nội bộ), bạn có thể sử dụng tiện ích serialver có trong SDK. Sau khi được xác định theo cách này, giá trị của trường sẽ được cố định, do đó việc khử lưu lượng sẽ luôn được cho phép. Ngoài ra, trong phiên bản 5.0, khoảng sau đã xuất hiện trong tài liệu: chúng tôi đặc biệt khuyến nghị tất cả các lớp có thể tuần tự hóa nên khai báo trường này một cách rõ ràng, vì phép tính mặc định rất nhạy cảm với các chi tiết của cấu trúc lớp, có thể thay đổi tùy theo việc triển khai trình biên dịch, và do đó gây ra InvalidClassExceptionnhững hậu quả không lường trước được... deserialization. Tốt hơn là khai báo trường này là private, bởi vì nó chỉ đề cập đến lớp mà nó được khai báo. Mặc dù công cụ sửa đổi không được chỉ định trong thông số kỹ thuật. Bây giờ chúng ta hãy xem xét khía cạnh này. Giả sử chúng ta có cấu trúc lớp này:
public class A{
    public int iPublic;
    protected int iProtected;
    int iPackage;
    private int iPrivate;
}

public class B extends A implements Serializable{}
Nói cách khác, chúng ta có một lớp được kế thừa từ lớp cha không thể tuần tự hóa. Có thể tuần tự hóa lớp này không và cần những gì cho việc này? Điều gì sẽ xảy ra với các biến của lớp cha? Câu trả lời là thế này. Có, Bbạn có thể tuần tự hóa một thể hiện của một lớp. Điều gì là cần thiết cho việc này? Nhưng lớp cần Acó một hàm tạo không có tham số publichoặc protected. Sau đó, trong quá trình khử tuần tự hóa, tất cả các biến lớp Asẽ được khởi tạo bằng cách sử dụng hàm tạo này. Các biến lớp Bsẽ được khởi tạo với các giá trị từ luồng dữ liệu được tuần tự hóa. Về mặt lý thuyết, có thể định nghĩa trong một lớp Bcác phương thức mà tôi đã nói ở phần đầu - readObjectwriteObject, - ở phần đầu để thực hiện (khử) tuần tự hóa các biến lớp Bthông qua in.defaultReadObject/out.defaultWriteObject, và sau đó (khử) tuần tự hóa các biến có sẵn từ lớp A(trong trường hợp của chúng tôi là iPublic, iProtectediPackage, nếu Bnó nằm trong cùng một gói với A). Tuy nhiên, theo tôi, tốt hơn nên sử dụng tính năng tuần tự hóa mở rộng cho việc này. Điểm tiếp theo tôi muốn đề cập đến là việc tuần tự hóa nhiều đối tượng. Giả sử chúng ta có cấu trúc lớp sau:
public class A implements Serializable{
    private C c;
    private B b;
    public void setC(C c) {this.c = c;}
    public void setB(B b) {this.b = b;}
    public C getC() {return c;}
    public B getB() {return b;}
}
public class B implements Serializable{
    private C c;
    public void setC(C c) {this.c = c;}
    public C getC() {return c;}
}
public class C implements Serializable{
    private A a;
    private B b;
    public void setA(A a) {this.a = a;}
    public void setB(B b) {this.b = b;}
    public B getB() {return b;}
    public A getA() {return a;}
}
Tuần tự hóa như nó vốn có.  Phần 1 - 2Điều gì xảy ra nếu bạn tuần tự hóa một thể hiện của lớp A? Nó sẽ kéo theo một thể hiện của lớp B, đến lượt nó, sẽ kéo theo một thể hiện Ccó tham chiếu đến thể hiện đó A, chính là thể hiện mà tất cả đã bắt đầu. Vòng luẩn quẩn và sự đệ quy vô tận? May mắn thay, không. Hãy xem đoạn mã kiểm tra sau:
// initiaizing
A a = new A();
B b = new B();
C c = new C();
// setting references
a.setB(b);
a.setC(c);
b.setC(c);
c.setA(a);
c.setB(b);
// serializing
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(a);
oos.writeObject(b);
oos.writeObject(c);
oos.flush();
oos.close();
// deserializing
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
A a1 = (A)ois.readObject();
B b1 = (B)ois.readObject();
C c1 = (C)ois.readObject();
// testing
System.out.println("a==a1: "+(a==a1));
System.out.println("b==b1: "+(b==b1));
System.out.println("c==c1: "+(c==c1));
System.out.println("a1.getB()==b1: "+(a1.getB()==b1));
System.out.println("a1.getC()==c1: "+(a1.getC()==c1));
System.out.println("b1.getC()==c1: "+(b1.getC()==c1));
System.out.println("c1.getA()==a1: "+(c1.getA()==a1));
System.out.println("c1.getB()==b1: "+(c1.getB()==b1));
Chúng ta đang làm gì vậy? Chúng tôi tạo một thể hiện của các lớp Avà cung cấp cho Bchúng Ccác liên kết với nhau, sau đó tuần tự hóa từng lớp. Sau đó, chúng tôi giải tuần tự hóa chúng trở lại và chạy một loạt kiểm tra. Kết quả là điều gì sẽ xảy ra:
a==a1: false
b==b1: false
c==c1: false
a1.getB()==b1: true
a1.getC()==c1: true
b1.getC()==c1: true
c1.getA()==a1: true
c1.getB()==b1: true
Vậy bạn có thể học được gì từ bài kiểm tra này? Đầu tiên. Các tham chiếu đối tượng sau khi khử lưu huỳnh sẽ khác với các tham chiếu trước đó. Nói cách khác, trong quá trình tuần tự hóa/giải tuần tự hóa, đối tượng đã được sao chép. Phương pháp này đôi khi được sử dụng để sao chép các đối tượng. Kết luận thứ hai có ý nghĩa hơn. Khi tuần tự hóa/giải tuần tự hóa nhiều đối tượng có tham chiếu chéo, các tham chiếu đó vẫn hợp lệ sau khi giải tuần tự hóa. Nói cách khác, nếu trước khi tuần tự hóa chúng trỏ đến một đối tượng, thì sau khi khử tuần tự hóa chúng cũng sẽ trỏ đến một đối tượng. Một thử nghiệm nhỏ khác để xác nhận điều này:
B b = new B();
C c = new C();
b.setC(c);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(b);
oos.writeObject(c);
oos.writeObject(c);
oos.writeObject(c);
oos.flush();
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
B b1 = (B)ois.readObject();
C c1 = (C)ois.readObject();
C c2 = (C)ois.readObject();
C c3 = (C)ois.readObject();
System.out.println("b1.getC()==c1: "+(b1.getC()==c1));
System.out.println("c1==c2: "+(c1==c2));
System.out.println("c1==c3: "+(c1==c3));
Một đối tượng lớp Bcó một tham chiếu đến một đối tượng lớp C. Khi được tuần tự hóa, bnó được tuần tự hóa cùng với một phiên bản của lớp С, sau đó phiên bản tương tự của c được tuần tự hóa ba lần. Điều gì xảy ra sau khi khử lưu huỳnh?
b1.getC()==c1: true
c1==c2: true
c1==c3: true
Như bạn có thể thấy, cả bốn đối tượng được deserialized thực sự đại diện cho một đối tượng - các tham chiếu đến nó đều bằng nhau. Chính xác như trước khi xuất bản. Một điểm thú vị khác - điều gì sẽ xảy ra nếu chúng ta đồng thời triển khai ExternalizableSerializable? Giống như trong câu hỏi đó - voi đấu với cá voi - ai sẽ đánh bại ai? Sẽ vượt qua Externalizable. Cơ chế tuần tự hóa trước tiên sẽ kiểm tra sự hiện diện của nó và chỉ sau đó mới kiểm tra sự hiện diện của nó. SerializableVì vậy, nếu lớp B thực hiện Serializable, kế thừa từ lớp A thực hiện Externalizable, thì các trường của lớp B sẽ không được tuần tự hóa. Điểm cuối cùng là sự kế thừa. Khi kế thừa từ một lớp triển khai Serializable, không cần thực hiện thêm hành động nào. Việc tuần tự hóa cũng sẽ mở rộng đến lớp con. Khi kế thừa từ một lớp thực hiện Externalizable, bạn phải ghi đè các phương thức readExternal và writeExternal của lớp cha. Nếu không, các trường của lớp con sẽ không được tuần tự hóa. Trong trường hợp này, bạn nên nhớ gọi các phương thức cha, nếu không các trường cha sẽ không được tuần tự hóa. * * * Có lẽ chúng ta đã hoàn thành xong các chi tiết. Tuy nhiên, có một vấn đề mà chúng tôi chưa đề cập đến, mang tính chất toàn cầu. Cụ thể là -

Tại sao bạn cần có thể ngoại hóa?

Tại sao chúng ta cần tuần tự hóa nâng cao? Đáp án đơn giản. Thứ nhất, nó mang lại sự linh hoạt hơn nhiều. Thứ hai, nó thường có thể mang lại lợi ích đáng kể về khối lượng dữ liệu được tuần tự hóa. Thứ ba, có một khía cạnh như hiệu suất, mà chúng ta sẽ nói đến bên dưới . Mọi thứ dường như rõ ràng với sự linh hoạt. Thật vậy, chúng ta có thể kiểm soát các quá trình tuần tự hóa và giải tuần tự hóa theo ý muốn, điều này khiến chúng ta độc lập với bất kỳ thay đổi nào trong lớp (như tôi đã nói ở trên, những thay đổi trong lớp có thể ảnh hưởng lớn đến quá trình giải tuần tự hóa). Vì vậy, tôi muốn nói đôi lời về mức tăng về khối lượng. Giả sử chúng ta có lớp sau:
public class DateAndTime{

  private short year;
  private byte month;
  private byte day;
  private byte hours;
  private byte minutes;
  private byte seconds;

}
Phần còn lại là không quan trọng. Các trường có thể được tạo theo kiểu int, nhưng điều này sẽ chỉ nâng cao hiệu quả của ví dụ. Mặc dù trong thực tế, các trường có thể được gõ intvì lý do hiệu suất. Trong mọi trường hợp, vấn đề đã rõ ràng. Lớp đại diện cho một ngày và thời gian. Nó thú vị đối với chúng tôi chủ yếu từ quan điểm xuất bản nhiều kỳ. Có lẽ điều dễ dàng nhất có thể làm là lưu trữ một dấu thời gian đơn giản. Nó thuộc loại dài, tức là khi được tuần tự hóa nó sẽ mất 8 byte. Ngoài ra, phương pháp này yêu cầu các phương pháp chuyển đổi các thành phần thành một giá trị và ngược lại, tức là - giảm năng suất. Ưu điểm của phương pháp này là một ngày hoàn toàn điên rồ có thể phù hợp với 64 bit. Đây là một mức độ an toàn rất lớn, thường không cần thiết trong thực tế. Lớp được đưa ra ở trên sẽ mất 2 + 5*1 = 7 byte. Cộng thêm chi phí cho lớp và 6 trường. Có cách nào để nén dữ liệu này? Chắc chắn. Giây và phút nằm trong khoảng 0-59, tức là để biểu thị chúng, 6 bit là đủ thay vì 8. Giờ – 0-23 (5 bit), ngày – 0-30 (5 bit), tháng – 0-11 (4 bit). Tổng cộng, mọi thứ không tính đến năm - 26 bit. Vẫn còn 6 bit cho kích thước của int. Về mặt lý thuyết, trong một số trường hợp, điều này có thể đủ trong một năm. Nếu không, việc thêm một byte khác sẽ tăng kích thước của trường dữ liệu lên 14 bit, mang lại phạm vi từ 0-16383. Điều này là quá đủ trong các ứng dụng thực tế. Tổng cộng, chúng tôi đã giảm kích thước dữ liệu cần thiết để lưu trữ thông tin cần thiết xuống còn 5 byte. Nếu không lên đến 4. Nhược điểm cũng giống như trong trường hợp trước - nếu bạn lưu trữ ngày đóng gói thì cần có phương thức chuyển đổi. Nhưng tôi muốn làm theo cách này: lưu trữ nó trong các trường riêng biệt và tuần tự hóa nó ở dạng đóng gói. Đây là nơi hợp lý để sử dụng Externalizable:
// data is packed into 5 bytes:
//  3         2         1
// 10987654321098765432109876543210
// hhhhhmmmmmmssssssdddddMMMMyyyyyy yyyyyyyy
public void writeExternal(ObjectOutput out){
    int packed = 0;
    packed += ((int)hours) << 27;
    packed += ((int)minutes) << 21;
    packed += ((int)seconds) << 15;
    packed += ((int)day) << 10;
    packed += ((int)month) << 6;
    packed += (((int)year) >> 8) & 0x3F;
    out.writeInt(packed);
    out.writeByte((byte)year);
}

public void readExternal(ObjectInput in){
    int packed = in.readInt();
    year = in.readByte() & 0xFF;
    year += (packed & 0x3F) << 8;
    month = (packed >> 6) & 0x0F;
    day = (packed >> 10) & 0x1F;
    seconds = (packed >> 15) & 0x3F;
    minutes = (packed >> 21) & 0x3F;
    hours = (packed >> 27);
}
Trên thực tế, đó là tất cả. Sau khi tuần tự hóa, chúng tôi nhận được chi phí chung cho mỗi lớp, hai trường (thay vì 6) và 5 byte dữ liệu. Điều này đã tốt hơn đáng kể. Việc đóng gói thêm có thể được để lại cho các thư viện chuyên ngành. Ví dụ được đưa ra rất đơn giản. Mục đích chính của nó là chỉ ra cách sử dụng tính năng xê-ri hóa nâng cao. Theo tôi, mặc dù lợi ích có thể đạt được về khối lượng dữ liệu được tuần tự hóa không phải là lợi thế chính. Ưu điểm chính, ngoài tính linh hoạt... (chuyển sang phần tiếp theo một cách suôn sẻ...) Liên kết với nguồn: Tuần tự hóa như hiện tại
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION