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.~..