《Java程式設計思想》學習筆記15——物件序列化
Java的物件序列化是一個輕量級的持久化過程,序列化時,可以將java物件以位元組序列形式寫入硬碟、資料庫或者通過網路傳輸到另一個JVM等等,反序列化是讀取序列化的檔案,將其在JVM中還原為java物件的過程。
1.簡單的序列化和反序列化:
最簡單的序列化是物件實現Serializable序列化介面,這個介面是一個標記介面,不需要實現任何方法。
下面的小例子簡單展示java物件序列化和反序列化的過程:
- import java.io.*;
- //簡單的測試物件
- publicclass TestObject implements Serializable{}
-
import java.io.*;
- //序列化
- publicclass SerializeObject{
- publicstaticvoid main(String[] args)throws Exception{
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(“x.file”));
- TestObject obj = new TestObject();
- //將TestObject物件序列化到x.file檔案中
- out.write(obj);
- }
-
}
- import java.io.*;
- //反序列化
- publicclass DeserializeObject{
- publicstaticvoid main(String[] args)throw Exception{
- ObjectInputStream in = new ObjectInputStream(new FileInputStream(“x.file”));
- Object myObj = in.readObject();
- System.out.println(myObj.getClass());
- }
-
}
輸出結果:
class TestObject
在序列化時,使用ObjectOutputStream將物件序列化寫出,反序列化時,使用ObjectInputStream將物件反序列化讀入。
注意:反序列化物件使用時,物件的class檔案必須在classpath中,否則,JVM找不到物件的class檔案會丟擲ClassNotFoundException。
2.序列化控制:
預設的Serializable序列化是將物件整體序列化,但是對於一些特殊的需求例如:序列化部分物件或者反序列化部分物件的情況,就必須使用Externalizable介面來代替Serializable介面,Externalizable的writeExternal()和readExternal()方法可以實現對序列化的控制,這兩個方法在物件序列化和反序列化時自動呼叫,例子如下:
- import java.io.*;
- class Blip1 implements Externalizable{
- public Blip1(){
- System.out.println(“Blip1 Constructor”);
- }
- publicvoid writeExternal(ObjectOutput out)throws IOException{
- System.out.println(“Blip1.writeExternal”);
- }
- publicvoid readExternal(ObjectInput in) throws IOException, ClassNotFoundException{
- System.out.println(Blip1.readExternal);
- }
- }
- class Blip2 implements Externalizable{
- Blip1(){
- System.out.println(“Blip2 Constructor”);
- }
- publicvoid writeExternal(ObjectOutput out)throws IOException{
- System.out.println(“Blip2.writeExternal”);
- }
- publicvoid readExternal(ObjectInput in) throws IOException, ClassNotFoundException{
- System.out.println(Blip2.readExternal);
- }
- }
- publicclass Blips{
- publicstaticvoid main(String[] args)throws IOException, ClassNotFoundException{
- System.out.println(“Constructing objects:”);
- Blip1 b1 = new Blip1();
- Blip2 b2 = new Blip2();
- //序列化
- ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(“Blips.out”));
- System.out.println(“Saving objects:”);
- o.writeObject(b1);
- o.writeObject(b2);
- //反序列化
- ObjectInputStream in = new ObjectInputStream(new FileInputStream(“Blips.out”));
- System.out.println(“Recovering b1:”);
- b1 = (Blip1)in.readObject();
- //由於Blip2的預設無引數構造方法不是public的,所以會拋異常
- //System.out.println(“Recovering b2:”);
- //b2 = (Blip2)in.readObject();
- }
- }
輸出結果:
Constructing objects:
Blip1 Constructor
Blip2 Constructor
Saving objects:
Blip1.writeExternal
Blip2.writeExternal
Recovering b1:
Blip1 Constructor
Blip1.readExternal
注意:由於物件在反序列化時需要呼叫預設的public的無引數構造方法建立物件,所以Blip2非public型別無引數構造方法無法反序列化。所以使用Externalizable序列化和反序列化時,物件必須要有public型別的無引數構造方法,這一點是Externalizable和Serializable的區別。
Serializable和Externlizable反序列化的區別:
(1).Serializable的反序列化是通過序列化的位元組陣列序列進行反序列化的。物件的序列化的反序列化都是自動進行的。
(2).Externalizable的反序列化是通過呼叫預設的構造方法進行的。物件的序列化需要使用writeExternal()顯式控制,物件的反序列化需要使用readExternal()顯式控制,不是自動進行的。
3. Externlizable序列化高階:
使用Externlizable序列化和反序列化物件時,readExternal()和writeExternal()方法可以物件中指定欄位進行初始化,例子如下:
- import java.io.*;
- publicclass Test implements Externalizable{
- privateint i;
- private String s;
- public Test(){
- System.out.println(“Test Constructor”);
- }
- public Test(String x, int a){
- System.out.println(“Test(String x, int a)”);
- i = a;
- s = x;
- }
- publicvoid toString(){
- return s + i;
- }
- publicvoid writeExternal(ObjectOutput out)throws IOException{
- System.out.println(“Test.writeExternal”);
- out.writeObject(s);
- out.writeInt(i);
- }
- publicvoid readExternal(ObjectInput in)throws IOException, ClassNotFoundException{
- System.out.println(“Test.readExternal”);
- s = (String)in.readObject();
- i = in.readInt();
- }
- publicstaticvoid main()throws IOException, ClassNotFoundException{
- System.out.println(“Constructing objects:”);
- Test t1 = new Test(“A String”, 47);
- Test t2 = new Test();
- System.out.println(t1);
- System.out.println(t2);
- //序列化
- ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(“Test.out”));
- System.out.println(“Saving objects:”);
- o.writeObject(t1);\
- o.writeObject(t2);
- o.close();
- //反序列化
- ObjectInputStream in = new ObjectInputStream(new FileInputStream(“Test.out”));
- System.out.println(“Recovering objects:”);
- t1 = (Test)in.readObject();
- System.out.println(t1);
- t2 = (Test)in.readObject();
- System.out.println(t2);
- }
- }
輸出結果:
Constructing objects:
Test(String x, int a)
A String 47
Test Constructor
null0
Saving objects:
Test.writeExternal
Test.writeExternal
Recovering objects:
Test Constructor
Test.readExternal
A String 47
Test Constructor
Test.readExternal
null0
該例子中,當建立物件使用非無引數構造方法時,物件欄位被初始化,因此序列化時可以將初始化的值儲存。當建立物件使用無引數的構造方法時,物件的欄位沒有被賦值而使用預設的初始化值。反序列化時只會呼叫預設的無引數構造方法。
因此,為了使序列化和反序列化更高效使用,在序列化時,呼叫writeExternal()只寫有用的重要資料,而在反序列化時,readObject()只讀取有用的重要資料。
4.transient關鍵字:
當物件實現Serializable介面進行自動序列化時,類中某些欄位不想被序列化,需要使用transient關鍵字,雖然Externalizable通過writeExternal()方法也可以實現此功能,但是序列化不是自動進行的,使用Serializable和transient關鍵字更加方便。
例子如下:
- import java.io.*;
- publicclass User implements Serializable{
- private String username;
- privatetransient String password;
- public User(String name, String pwd){
- username = name;
- password = pwd;
- }
- public String toString(){
- return “username:” + username + “, password:” + password;
- }
- publicstaticvoid main(String[] args)throws Exception{
- User user =