序列化的兩種方法:Serializable 接口和 Externalizable接口


控制序列化就是有選擇的序列化對象,而不是把對象的所以內容都序列化,控制對象序列化和反序列方法,可以在序列化過程中儲存其他非this對象包含的數據 。java.io.Externalizable 。繼承自了java.io.Serializable 。

java.io.Externalizable的所有實現者必須提供讀取和寫出的實現。

java.io.Externalizable接口(自己實現):

voidreadExternal(ObjectInput in)                    反序列時被調用
void writeExternal(ObjectOutput out)              序列化時被調用


例子:

import java.io.*;   
import java.util.*;     //本程序通過實現Externalizable接口控制對象序列化和反序列 
public class UserInfo implements Externalizable {       
public String userName;       
public String userPass;       
public int userAge;         
public UserInfo(){     
}         
public UserInfo(String username,String userpass,int userage){           
this.userName=username;          
this.userPass=userpass;           
this.userAge=userage;       
}              
//當序列化對象時,該方法自動調用      
public void writeExternal(ObjectOutput out) throws IOException{          
System.out.println("現在執行序列化方法");           
//可以在序列化時寫非自身的變量           
Date d=new Date();           
out.writeObject(d);           
//只序列化userName,userPass變量           
out.writeObject(userName);           
out.writeObject(userPass);       
}         
//當反序列化對象時,該方法自動調用       
public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException{           
System.out.println("現在執行反序列化方法");           
Date d=(Date)in.readObject();           
System.out.println(d);           
this.userName=(String)in.readObject();           
this.userPass=(String)in.readObject();       
}         
public String toString(){           
return "用戶名: "+this.userName+";密碼:"+this.userPass+";年齡:"+this.userAge;       
}   
}  

import java.io.*; import java.util.*; //本程序通過實現Externalizable接口控制對象序列化和反序列 
public class UserInfo implements Externalizable { 
public String userName; 
public String userPass; 
public int userAge; 
public UserInfo(){ } 
public UserInfo(String username,String userpass,int userage){ 
this.userName=username; 
this.userPass=userpass; 
this.userAge=userage; 
//當序列化對象時,該方法自動調用 
public void writeExternal(ObjectOutput out) throws IOException{ 
System.out.println("現在執行序列化方法"); //可以在序列化時寫非自身的變量 
Date d=new Date(); 
out.writeObject(d); 
//只序列化userName,userPass變量 
out.writeObject(userName); 
out.writeObject(userPass); 
//當反序列化對象時,該方法自動調用 
public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException{ 
System.out.println("現在執行反序列化方法"); 
Date d=(Date)in.readObject(); 
System.out.println(d); 
this.userName=(String)in.readObject(); 
this.userPass=(String)in.readObject(); 
public String toString(){ 
return "用戶名: "+this.userName+";密碼:"+this.userPass+ ";年齡:"+this.userAge; 
}   

改寫測試類 Java代碼 
import java.io.ObjectOutputStream;   
import java.io.ObjectInputStream;   
import java.io.FileInputStream;   
import java.io.FileOutputStream;   
import java.util.Date;   
import java.lang.management.*;   
public class Test {             
//序列化對象到文件       
public static void serialize(String fileName){           
try{               
//創建一個對象輸出流,講對象輸出到文件               
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileName));                 
UserInfo user=new UserInfo("renyanwei","888888",20);               
out.writeObject(user);  //序列化一個會員對象                 
out.close();           
}catch (Exception x){               
System.out.println(x.toString());          
}                  
}       
//從文件反序列化到對象       
public static void deserialize(String fileName){           
try{               
//創建一個對象輸入流,從文件讀取對象               
ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileName));                 
//讀取UserInfo對象並調用它的toString()方法               
UserInfo user=(UserInfo)(in.readObject());                         
System.out.println(user.toString());                 
in.close();          
}catch (Exception x){               
System.out.println(x.toString());           
}                  
}         
public static void main(String[] args) {                 
serialize("D://test.txt");           
System.out.println("序列化完畢");                      
deserialize("D://test.txt");           
System.out.println("反序列化完畢");      
}    
}  

import java.io.ObjectOutputStream; 
import java.io.ObjectInputStream; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.util.Date; 
import java.lang.management.*; 
public class Test { 
//序列化對象到文件 
public static void serialize(String fileName){ 
try { 
//創建一個對象輸出流,講對象輸出到文件 
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileName)); 
UserInfo user=new UserInfo("renyanwei","888888",20); 
out.writeObject(user);
//序列化一個會員對象 
out.close();
} catch (Exception x) { 
System.out.println(x.toString()); 
}
//從文件反序列化到對象 
public static void deserialize(String fileName){ 
try {
 //創建一個對象輸入流,從文件讀取對象 
ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileName)); 
//讀取UserInfo對象並調用它的toString()方法 
UserInfo user=(UserInfo)(in.readObject());
System.out.println(user.toString()); in.close(); 
} catch (Exception x) { 
System.out.println(x.toString()); 
public static void main(String[] args) {
serialize("D://test.txt");
 System.out.println("序列化完畢"); 
deserialize("D://test.txt"); 
System.out.println("反序列化完畢");
} 


執行結果 

現在執行序列化方法 
序列化完畢 
現在執行反序列化方法 
Thu Oct 23 22:18:12 CST 2008 
用戶名: renyanwei;密碼:888888;年齡:0 
反序列化完畢 

總結: 
首先,我們在序列化UserInfo對象的時候,由於這個類實現了Externalizable 接口,在writeExternal()方法里定義了哪些屬性可以序列化,哪些不可以序列化,所以,對象在經過這里就把規定能被序列化的序列化保存文件,不能序列化的不處理,然后在反序列的時候自動調用readExternal()方法,根據序列順序挨個讀取進行反序列,並自動封裝成對象返回,然后在測試類接收,就完成了反序列通過Serializable接口是java內部已經封裝好了,而

序列化會自動存儲必要的信息,用以反序列化被存儲的實例,而外部化則只保存被存儲的類的標識。當你通過java.io.Serializable接口序列化一個對象時,有關類的信息,比如它的屬性和這些屬性的類型,都與實例數據一起被存儲起來。在選擇走Externalizable這條路時,Java 只存儲有關每個被存儲類型的非常少的信息。

每個接口的優點和缺點

Serializable接口

?         優點:內建支持

?         優點:易於實現

?         缺點:占用空間過大

?         缺點:由於額外的開銷導致速度變比較慢

Externalizable接口

?         優點:開銷較少(程序員決定存儲什么)

?         優點:可能的速度提升

?         缺點:虛擬機不提供任何幫助,也就是說所有的工作都落到了開發人員的肩上。

在兩者之間如何選擇要根據應用程序的需求來定。Serializable通常是最簡單的解決方案,但是它可能會導致出現不可接受的性能問題或空間問題;在出現這些問題的情況下,Externalizable可能是一條可行之路。

要記住一點,如果一個類是可外部化的(Externalizable),那么Externalizable方法將被用於序列化類的實例,即使這個類型提供了Serializable方法:

private void writeObject()
private void readObject()


 

 

Externalizable 實例

被Serializable接口聲明的類的對象的內容都將被序列化,如果現在用戶希望自己指定序列化的內容,則可以讓一個類實現Externalizable接口,此接口定義如下:

  1. public interface Externalizable extends Serializable {  
  2.     public void writeExternal(ObjectOutput  out) throws IOException ;  
  3.     public void readExternal(ObjectInput in)  throws IOException,  
  4. ClassNot FoundException ;  

Externalizable接口是Serializable接口的子接口,在此接口中定義了兩個方法,這兩個方法的作用如下。

writeExternal(ObjectOutput out):在此方法中指定要保存的屬性信息,對象序列化時調用。

readExternal(ObjectInput in):在此方法中讀取被保存的信息,對象反序列化時調用。

這兩個方法的參數類型是ObjectOutput和ObjectInput,兩個接口的定義如下。

ObjectOutput接口定義:

  1. public interface ObjectOutput extends DataOutput 

ObjectInput接口定義:

  1. public interface ObjectInput extends DataInput 

可以發現以上兩個接口分別繼承DataOutput和DataInput,這樣在這兩個方法中就可以像DataOutputStream和DataInputStream那樣直接輸出和讀取各種類型的數據。

如果一個類要使用Externalizable實現序列化時,在此類中必須存在一個無參構造方法,因為在反序列化時會默認調用無參構造實例化對象,如果沒有此無參構造,則運行時將會出現異常,這一點的實現機制與Serializable接口是不同的。

范例:修改Person類並實現Externalizable接口

  1. package org.lxh.demo12.serdemo;  
  2. import java.io.Externalizable;  
  3. import java.io.IOException;  
  4. import java.io.ObjectInput;  
  5. import java.io.ObjectOutput;  
  6. public class Person implements Externalizable  {// 此類的對象可以被序列化  
  7.     private String name;                       // 聲明name屬性  
  8.     private int age;                           // 聲明age屬性  
  9.     public Person(){}                         // 必須定義無參構造  
  10.     public Person(String name, int age)     // 通過構造方法設置屬性內容  
  11.         this.name name;  
  12.         this.age age;  
  13.     }  
  14.     public String toString()                  // 覆寫toString()方法  
  15.         return "姓名:" this.name ";年齡:" this.age;  
  16.     }  
  17.     // 覆寫此方法,根據需要讀取內容,反序列化時使用  
  18.     public void readExternal(ObjectInput in)  throws IOException,  
  19.             ClassNotFoundException {  
  20.         this.name (String)in.readObject()   // 讀取姓名屬性  
  21.         this.age in.readInt()              // 讀取年齡  
  22.     }  
  23.     // 覆寫此方法,根據需要可以保存屬性或具體內容,序列化時使用  
  24.     public void writeExternal(ObjectOutput out)  throws IOException {  
  25.         out.writeObject(this.name)          // 保存姓名屬性  
  26.         out.writeInt(this.age)             // 保存年齡屬性  
  27.     }  

以上程序中的Person類實現了Externalizable接口,這樣用戶就可以在類中有選擇地保存需要的屬性或者其他的具體數據。在本程序中,為了與之前的程序統一,將全部屬性保存下來。

范例:序列化和反序列化Person對象

  1. package org.lxh.demo12.serdemo;  
  2. import java.io.File;  
  3. import java.io.FileInputStream;  
  4. import java.io.FileOutputStream;  
  5. import java.io.InputStream;  
  6. import java.io.ObjectInputStream;  
  7. import java.io.ObjectOutputStream;  
  8. import java.io.OutputStream;  
  9. public class SerDemo03 {  
  10.     public static void main(String[] args)  throws Exception {  
  11.         ser();                               // 序列化  
  12.         dser();                                // 反序列化  
  13.     }  
  14.     public static void ser() throws Exception  {     // 序列化操作  
  15.         File new File("D:" File.separator "test.txt");  
  16.         ObjectOutputStream oos null;  
  17.         OutputStream out new FileOutputStream(f);  // 文件輸出流  
  18.         oos new ObjectOutputStream(out);        // 為對象輸出流實例化  
  19.         oos.writeObject(new Person("張三"30));   // 保存對象到文件  
  20.         oos.close();                             // 關閉輸出  
  21.     }  
  22.     public static void dser() throws Exception  {        // 反序列化操作  
  23.         File new File("D:" File.separator "test.txt");  
  24.         ObjectInputStream ois null;  
  25.         InputStream input new FileInputStream(f);  // 文件輸出流  
  26.         ois new ObjectInputStream(input);          // 為對象輸出流實例化  
  27.         Object obj ois.readObject();           // 讀取對象  
  28.         ois.close();                            // 關閉輸出  
  29.         System.out.println(obj);  
  30.     }  

從以上代碼中可以發現,使用Externalizable接口實現序列化明顯要比使用Serializable接口實現序列化麻煩得多,除此之外,兩者的實現還有不同,如表12-27所示。

表12-27  Externalizable接口與Serializable接口實現序列化的區別

    號

    別

Serializable

Externalizable

1

實現復雜度

實現簡單,Java對其

有內建支持

實現復雜,

由開發人員自己完成

2

執行效率

所有對象由Java統一保存,

性能較低

開發人員決定哪個對象保存,

可能造成速度提升

3

保存信息

保存時占用空間大

部分存儲,

可能造成空間減少

在一般的開發中,因為Serializable接口的使用較為方便,所以出現較多


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com