----------- android培訓、java培訓、java學習型技術博客、期待與您交流! ------------
當兩個進程在進行遠程通信時,彼此可以發送各種類型的數據。無論是何種類型的數據,都會以二進制序列的形式在網絡上傳送。發送方需要把這個Java對象轉換爲字節序列,才能在網絡上傳送;接收方則需要把字節序列再恢復爲Java對象。
把Java對象轉換爲字節序列的過程稱爲對象的序列化。
把字節序列恢復爲Java對象的過程稱爲對象的反序列化。
對象的序列化主要有兩種用途:
1) 把對象的字節序列永久地保存到硬盤上,通常存放在一個文件中;
2) 在網絡上傳送對象的字節序列。
一. JDK類庫中的序列化API
java.io.ObjectOutputStream代表對象輸出流,它的writeObject(Object obj)方法可對參數指定的obj對象進行序列化,把得到的字節序列寫到一個目標輸出流中。
java.io.ObjectInputStream代表對象輸入流,它的readObject()方法從一個源輸入流中讀取字節序列,再把它們反序列化爲一個對象,並將其返回。、
只有實現了Serializable和Externalizable接口的類的對象才能被序列化。Externalizable接口繼承自Serializable接口,實現Externalizable接口的類完全由自身來控制序列化的行爲,而僅實現Serializable接口的類可以採用默認的序列化方式 。
對象序列化包括如下步驟:
1) 創建一個對象輸出流,它可以包裝一個其他類型的目標輸出流,如文件輸出流;
2) 通過對象輸出流的writeObject()方法寫對象。
對象反序列化的步驟如下:
1) 創建一個對象輸入流,它可以包裝一個其他類型的源輸入流,如文件輸入流;
2) 通過對象輸入流的readObject()方法讀取對象。
二.實現Serializable接口
ObjectOutputStream只能對Serializable接口的類的對象進行序列化。默認情況下,ObjectOutputStream按照默認方式序列化,這種序列化方式僅僅對對象的非transient的實例變量進行序列化,而不會序列化對象的transient的實例變量,也不會序列化靜態變量。
當ObjectOutputStream按照默認方式反序列化時,具有如下特點:
1) 如果在內存中對象所屬的類還沒有被加載,那麼會先加載並初始化這個類。如果在classpath中不存在相應的類文件,那麼會拋出ClassNotFoundException;
2) 在反序列化時不會調用類的任何構造方法。
三. 可序列化類的不同版本的序列化兼容性
凡是實現Serializable接口的類都有一個表示序列化版本標識符的靜態變量:
serialVersionUID的取值是Java運行時環境根據類的內部細節自動生成的。如果對類的源代碼作了修改,再重新編譯,新生成的類文件的serialVersionUID的取值有可能也會發生變化。
類的serialVersionUID的默認值完全依賴於Java編譯器的實現,對於同一個類,用不同的Java編譯器編譯,有可能會導致不同的serialVersionUID,也有可能相同。爲了提高哦啊serialVersionUID的獨立性和確定性,強烈建議在一個可序列化類中顯示的定義serialVersionUID,爲它賦予明確的值。顯式地定義serialVersionUID有兩種用途:
1) 在某些場合,希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有相同的serialVersionUID;
2) 在某些場合,不希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有不同的serialVersionUID。
package com.itcast.test1;
import java.util.*;
import java.io.*;
public class SerializableTest {
/**
* 對象序列化與反序列化
* 進行對象序列化主要目的是爲了保存對象的狀態(成員變量)。
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Ball> list = new ArrayList<Ball>();
Ball ball1 = new Ball("basketball",1);
Ball ball2 = new Ball("football",2);
Ball ball3 = new Ball("badminton",3);
list.add(ball1);
list.add(ball2);
list.add(ball3);
try {
serialize(list);
deserialize("obj.ser");
} catch(Exception e) {
e.printStackTrace();
}
}
/**
* 進行序列化主要用到的流是FileOutputStream和ObjectOutputStream。
* FileOutputStream主要用於連接磁盤文件,並把字節寫出到該磁盤文件;
* @param list
* @throws IOException
*/
public static void serialize(List<Ball> list) throws IOException {
FileOutputStream fos = new FileOutputStream("obj.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
for(Ball ball : list) {
oos.writeObject(ball);
}
}
/**
* 保存狀態的目的就是爲了在未來的某個時候再恢復保存的內容,
* 這可以通過反序列化來實現。對象的反序列化過程與序列化正好相反,
* 主要用到的兩個流是FileInputstream和ObjectInputStream。
* 反序列化後得到的對象的順序與保存時的順序一致。
* @param fileName
* @throws Exception
*/
public static void deserialize(String fileName) throws Exception {
FileInputStream fis = new FileInputStream(fileName);
ObjectInputStream ois = new ObjectInputStream(fis);
while (true) {
try {
Ball ball = (Ball) ois.readObject();
System.out.println(ball.toString());
} catch (EOFException e) {
System.out.println("文件已到末尾");
break;
}
}
}
}
/**
* 補充一:上面我們舉得例子很簡單,要保存的成員變量要麼是基本類型的要麼是String類型的。
* 但有時成員變量有可能是引用類型的,這是的情況會複雜一點。那就是當要對某對象進行序列化時,
* 該對象中的引用變量所引用的對象也會被同時序列化,並且該對象中如果也有引用變量的話則該對象
* 也將被序列化。總結說來就是在序列化的時候,對象中的所有引用變量所對應的對象將會被同時序列化。
* 這意味着,引用變量類型也都要實現Serializable接口。當然其他對象的序列化都是自動進行的。
* 所以我們只要保證裏面的引用類型是都實現Serializable接口就行了,如果沒有的話,會在編譯時拋出異常。
* 如果序列化的對象中包含沒有實現Serializable的成員變量的話,這時可以使用transient關鍵字,
* 讓序列化的時候跳過該成員變量。使用關鍵字transient可以讓你在序列化的時候自動跳過transient所修飾的成員變量,
* 在反序列化時這些變量會恢復到默認值。
* 補充二:如果某類實現了Serializable接口的話,其子類會自動編程可序列化的,這個好理解,繼承嘛。
* 補充三:在反序列化的時候,並不會調用對象的構造器,這也好理解,如果調用了構造器的話,對象的狀態不就又重新初始化了嗎。
* 補充四:我們說到對象序列化的是爲了保存對象的狀態,即對象的成員變量,所以靜態變量不會被序列化。
*/
package com.itcast.test1;
import java.io.Serializable;
/**
* javabean多用於封裝屬性,而很少具備某些特定的方法功能。
* 試想,javabean的功能就是傳遞數據,那麼類權限爲共有也
* 就不足爲奇了。下面是標準javabean要遵守的一些規範:
*1. 類訪問權限爲公有
*2. 所有屬性爲私有
*3. 每個字段對外提供setter方法和getter方法
*4. 具備一個無參的構造方法
*/
public class Ball implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 實現序列化
* 要將某類的對象序列化,則該類必須實現Serializable接口,該接口
* 僅是一個標誌,告訴JVM該類的對象可以被序列化。如果某類未實現Serializable接口,
* 則該類對象不能實現序列化。
*/
private String ballName;
private int serialNumber;
public Ball(String ballName, int serialNumber) {
super();
this.ballName = ballName;
this.serialNumber = serialNumber;
}
public String getBallName() {
return ballName;
}
public void setBallName(String ballName) {
this.ballName = ballName;
}
public int getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(int serialNumber) {
this.serialNumber = serialNumber;
}
@Override
public String toString() {
return "Ball [ballName=" + ballName + ", serialNumber=" + serialNumber
+ "]";
}
}
----------- android培訓、java培訓、java學習型技術博客、期待與您交流! ------------