1. 程式人生 > 實用技巧 >java> java核心卷2讀書筆記 - 物件序列化

java> java核心卷2讀書筆記 - 物件序列化

物件序列化(object serialization)是java支援的通用機制,可以將任何物件寫出到輸出流中,並且也可以回讀。簡單來說,就是可以將物件資料儲存為檔案,甚至可以通過網路傳輸,在這之後或者別的主機上恢復當前儲存的資料狀態。

序列化方式:Serializable介面和Externalizable介面兩種方式。

Serializable介面方式

特點

需要結合ObjectInputStream和ObjectOutputStream使用,ObjectOutputStream用於儲存物件,ObjectInputStream用於恢復物件。

對於想要支援序列化的物件,需要實現Serializable介面,這個介面沒有任何方法,只是用於標記。

對於不想儲存或者無法序列化的物件、屬性,使用transient修飾會讓被修飾的屬性直接繞過序列化。

儲存和恢復資料具體的方法

恢復/儲存物件 readObject(), saveObject

恢復/儲存基本資料型別(非java物件),例如整型資料:readInt(), writeInt()

對引用相同物件的處理

如果2個要序列化的不同物件,其屬性都指向了同一個物件引用,如何處理?

對此,Java核心卷2的解釋是:每個物件都是用一個序列號(serial number)來儲存的。也就是說,序列化的每個物件關聯的都是一個序列號,而不再是執行時的RAM地址。

序列號

序列號是序列化版本唯一的ID,是資料域型別和方法簽名的指紋,通過對類、介面、域型別、和方法簽名按規範方式排序,然後將SHA(安全雜湊演算法)應用於這些資料而來。不過,序列號只用到SHA碼前8個byte。也就是說,只要類有任何變化,序列號很可能會不一樣。

具體演算法描述:

儲存物件

  • 每個物件引用都關聯一個序列號;
  • 對每個物件,第一次遇到時,儲存其輸出物件到輸出流中;
  • 如果某個物件之前已經被儲存過,那麼只寫出“與之前儲存過的序列號為x的物件相同”;

恢復物件

通過讀回恢復物件,整個過程反過來。

  • 對於輸入流中的物件,在第一次遇到其序列號時,構建它,並使用流中資料初始化它,然後記錄這個順序號和新物件之間的關聯。
  • 當遇到“與之前儲存過的序列號為x的物件相同”標記時,獲取與這個順序號相關聯的物件引用。

示例

現有員工類Employee和經理類Manager,Manager繼承自Emolyee,每個Manager擁有一個祕書屬於secretary引用屬於Employee。

如果要序列化Employee[3]陣列物件,其中,Employee[0]和Employee[1]是Employee子類Manager物件,都包含了同一個祕書(Employee物件Employee[2])。

測試類TestObjectStream

public class TestObjectStream {
    public static void main(String[] args) throws IOException {
     // 建立3個Employee及其子類物件, 其中2個Manager的secretary都指向了同一個Employee Employee harry
= new Employee("Harry Hacker", 50000, 1989, 10, 1); Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15); carl.setSecretary(harry); Manager tony = new Manager("Tony Tester", 40000, 1990, 3, 15); tony.setSecretary(harry); Employee[] staff = new Employee[3]; staff[0] = harry; staff[1] = carl; staff[2] = tony; // save all employee records to the file employee.dat try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.dat"))) { out.writeObject(staff); } try(ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.dat"))) { Employee[] newStaff = (Employee[]) in.readObject(); newStaff[1].raiseSalary(10); for (Employee e: newStaff) { System.out.println(e); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } }

Employee類

public class Employee implements Serializable { // 需要序列化的物件, 一定要實現Seriablizable介面
    String name;
    double salary;
    LocalDate hireDay;

    Employee(String name, double salary, int year, int month, int day) {
        this.name = name;
        this.salary = salary;
        this.hireDay = LocalDate.of(year, month, day);
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public LocalDate getHireDay() {
        return hireDay;
    }

    public void raiseSalary(double raiseValue) {
        this.salary += raiseValue;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", hireDay=" + hireDay +
                '}';
    }
}

Manager類

public class Manager extends Employee{
    private Employee secretary; // 祕書

    Manager(String name, double salary, int year, int month, int day) {
        super(name, salary, year, month, day);
    }

    public Employee getSecretary() {
        return secretary;
    }

    public void setSecretary(Employee secretary) {
        this.secretary = secretary;
    }
}

執行結果

控制檯輸出

Employee{name='Harry Hacker', salary=50000.0, hireDay=1989-10-01}
Manager{name='Carl Cracker', salary=80010.0, hireDay=1987-12-15}
Manager{name='Tony Tester', salary=40000.0, hireDay=1990-03-15}

Employee.dat (2進位制讀取,16進位制顯示)

00000000h: AC ED 00 05 75 72 00 1C 5B 4C 63 68 32 2E 6F 62 ; ..ur..[Lch2.ob
00000010h: 6A 65 63 74 73 74 72 65 61 6D 2E 45 6D 70 6C 6F ; jectstream.Emplo
00000020h: 79 65 65 3B 64 4A B0 C4 18 FD 00 4D 02 00 00 78 ; yee;dJ澳.?M...x
00000030h: 70 00 00 00 03 73 72 00 19 63 68 32 2E 6F 62 6A ; p....sr..ch2.obj
00000040h: 65 63 74 73 74 72 65 61 6D 2E 45 6D 70 6C 6F 79 ; ectstream.Employ
00000050h: 65 65 3F E3 4D FE 56 56 38 26 02 00 03 44 00 06 ; ee?鉓V8&...D..
00000060h: 73 61 6C 61 72 79 4C 00 07 68 69 72 65 44 61 79 ; salaryL..hireDay
00000070h: 74 00 15 4C 6A 61 76 61 2F 74 69 6D 65 2F 4C 6F ; t..Ljava/time/Lo
00000080h: 63 61 6C 44 61 74 65 3B 4C 00 04 6E 61 6D 65 74 ; calDate;L..namet
00000090h: 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 ; ..Ljava/lang/Str
000000a0h: 69 6E 67 3B 78 70 40 E8 6A 00 00 00 00 00 73 72 ; ing;xp@鑚.....sr
000000b0h: 00 0D 6A 61 76 61 2E 74 69 6D 65 2E 53 65 72 95 ; ..java.time.Ser?
000000c0h: 5D 84 BA 1B 22 48 B2 0C 00 00 78 70 77 07 03 00 ; ]労."H?..xpw...
000000d0h: 00 07 C5 0A 01 78 74 00 0C 48 61 72 72 79 20 48 ; ..?.xt..Harry H
000000e0h: 61 63 6B 65 72 73 72 00 18 63 68 32 2E 6F 62 6A ; ackersr..ch2.obj
000000f0h: 65 63 74 73 74 72 65 61 6D 2E 4D 61 6E 61 67 65 ; ectstream.Manage
00000100h: 72 48 55 D0 A8 98 9D 05 2D 02 00 01 4C 00 09 73 ; rHU楔槤.-...L..s
00000110h: 65 63 72 65 74 61 72 79 74 00 1B 4C 63 68 32 2F ; ecretaryt..Lch2/
00000120h: 6F 62 6A 65 63 74 73 74 72 65 61 6D 2F 45 6D 70 ; objectstream/Emp
00000130h: 6C 6F 79 65 65 3B 78 71 00 7E 00 02 40 F3 88 00 ; loyee;xq.~..@髨.
00000140h: 00 00 00 00 73 71 00 7E 00 06 77 07 03 00 00 07 ; ....sq.~..w.....
00000150h: C3 0C 0F 78 74 00 0C 43 61 72 6C 20 43 72 61 63 ; ?.xt..Carl Crac
00000160h: 6B 65 72 71 00 7E 00 05 73 71 00 7E 00 09 40 E3 ; kerq.~..sq.~..@?
00000170h: 88 00 00 00 00 00 73 71 00 7E 00 06 77 07 03 00 ; ?....sq.~..w...
00000180h: 00 07 C6 03 0F 78 74 00 0B 54 6F 6E 79 20 54 65 ; ..?.xt..Tony Te
00000190h: 73 74 65 72 71 00 7E 00 05                      ; sterq.~..