一、問題的引入:
程序運行過程中,所創建的對象都是保存在內存中的,當程序運行結束時,對象的生命週期就結束了。
如果能夠將這些對象信息保存下來,下次程序再啓動的時候,能夠讀取這些對象信息,還原這些對象的話,使他們與上次結束時持有相同的狀態,那該多好。
可是怎麼能夠做到呢?
方法一:逐一記錄每隔對象的所有屬性(方法),然後逐一寫入文件中,等下次使用某個對象時,再從文件中遍歷調用這個對象。這樣做,儘管實現了對象信息的保存和獲取,但是無形中帶來下面2個突出的弊端:
A、必須對對象的結構非常的熟悉,稍有疏忽就可能出錯,同時需要編寫大量的重複代碼;
B、每次增加配置信息都要重新設計文件的結構,並且修改保存和加載的方法;
爲了解決上述問題,java引入了序列化和反序列化技術,只需簡單幾步,就可以一勞永逸第完成這些對象信息的讀寫操作。
二、序列化概述:
所謂序列化,是指將java對象的狀態存儲到特定的存儲介質中的過程(將對象的狀態轉換爲可保持或可傳輸的格式的過程)。在序列化過程中,會將對象的共有成員、私有成員包括類名等——》字節流——》數據流,存儲(字節序列、二進制狀態)到介質(通常指的是文件)中,必要的時候,再讀取出來。
需要注意的是:
1、轉化後的是字節序列,這些字節序列可以保存在磁盤上、藉助於網絡進行傳輸;
2、序列化後的對象,保存的是其二進制狀態,實現了平臺的無關性,即windows系統中實現序列化一個對象,傳輸到unix機器上,反序列化後可以得到;
3、序列化後的對象,不僅保存了對象的所有成員,而且包含對象內的所有引用,並保存了那些引用對象,構建了對象網。
序列化的好處:序列化機制,使得對象可以脫離程序而獨立存在;
序列化的實現:藉助I/O流來實現。
三、序列化的應用場景:
場景1、登錄某個網站後,設置個人窗體,例如登錄博客後,設置自己的個性空間(背景色、特色佈局等等)。爲了使用戶設置完成後,下次登錄的依然保持有這個界面風格,可以將用戶設置的信息保存在一個對象中,然後將該對象序列化後保存在後臺的數據庫表中或文件中。下次用戶再登錄時,加載頁面時,從表中或文件中讀取這些信息,利用反序列化機制,再生成這個對象應用到用戶界面上。
場景2:點對點聊天系統(如QQ)中,可以自己設置喜愛的字體、大小、顏色,然後同網絡的另一端一個用戶進行聊天。網絡端的另一個永不他接收到的信息原則上講也應該是你發出去的風格。此時,可以將用戶輸入的風格、內容封裝成一個對象,然後序列化——》網絡傳輸,在網絡的另一端,取出這個二進制流並反序列化成對象,然後顯示在他的電腦上。
四、如何實現對象的序列化與反序列化:
1、對象序列化:對象要想能夠被序列化,則必須實現java.io.Serializable接口,例如java中的String類、包裝類、Date類都默認實現了這個接口;
2、對象序列化的步驟:
A、創建一個對象輸出流:ObjectOutputStream,它可以包含一個其他類型的輸出流,比如文件輸出流FileOutputStream
B、通過writeObject()方法輸出序列化對象;
3、反序列化的步驟
A、創建一個對象輸入流:objectInputStream,可以包含一個其他類型的輸入流,比如文件輸入流FileInputStream;
B、通過readObject()方法讀流中的對象,默認返回的是Object對象。
注意:
反序列化機制,並不是通過構造器來初始化對象的(無需使用構造器生成對象);
如果向文件中寫入多個對象時,反序列化的時候,則必須按寫入的順序來反序列化恢復 對象;
如果一個可序列化的類,有多個父類(包括直接或間接父類),則這些父類要麼也是可序列化的,要麼就包含無參數的構造方法,否則拋出異常。
但是涉及到隱私的信息:如用戶名、密碼、卡號等,應禁止序列化某些信息屬性,此時使用transient來修飾
五、代碼示例:
1、定義一個類,實現序列化接口
package com.pb.io.serilable;
import java.io.Serializable;
public class Student implements Serializable{
private String name;
private int age;
private String gender;
private transient String password;
public Student(String name,int age,String gender,String password){
this.name=name;
this.age=age;
this.gender=gender;
this.password=password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
2、序列化與反序列化操作代碼:
package com.pb.io.serilable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 使用對象流序列化和反序列化對象的步驟:
* 1、創建一個對象,這個對象將被序列化並被寫入文件中,前提是當前類創建的時候,實現了Serializable接口
* 2、實例化ObjectOutputStream對象
* 3、調用ObjectOutputStream類的writeObject()方法,將對象寫入到文件中
* ===========================
* 4、實例化ObjectInputStream對象
* 5、調用ObjectinputStream類的readObject()方法,獲取對象的時候要進行強制類型轉換
* 6、關閉相關的流
*
* 注意:transient關鍵字可以保護對象中的隱藏信息不被寫入到對象流中。
*
*/
public class SerializableObj {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
//實例化ObjectOutputStream對象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("c:/mydoc/stu.txt"));
//實例化Student對象
Student student = new Student("安娜",30,"女","123456");
//student對象序列化,並且寫入oos流
oos.writeObject(student);
//關閉流
oos.close();
/**
* 反序列化,即讀取序列化對象的過程
*/
//1.實例化ObjectInputStream對象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("c:/mydoc/stu.txt"));
//實例化Student對象
Student student1=null;
//反序列化讀取ObjectinputStream流中的信息,進行強制類型轉換
student1=(Student)ois.readObject();
//輸出student對象
System.out.println("=========輸出獲取的student的各個信息=========");
System.out.println("姓名爲:"+student1.getName());
System.out.println("性別爲:"+student1.getGender());
System.out.println("年齡爲:"+student1.getAge());
System.out.println("密碼爲:"+student1.getPassword());
//關閉流
ois.close();
}
}
七、序列化機制的算法
爲了確保同一個對象不被多次序列化(例如,可以被序列化的類被另一類引用),則引入了序列化算法:
A、對象序列化的時候,分配一個序列號;
B、當程序試圖序列化一個對象時,將會檢查這個對象是否已經被序列化(就是看序列化編號是佛存在)?如果已經被序列化,則直接輸出當前對象的序列化編號,而不再重新序列化。
C、反序列化的時候,程序會自動查找當前對象的序列化編號,如果發現當前的序列化編號被其他對象引用,則返回同一個對象。