序列化的含義和意義
含義:
把對象轉換為位元組序列的過程稱為對象的序列化
把位元組序列恢複為對象的過程稱為對象的反序列化。
意義:
1.把對象的位元組序列永久的儲存到硬碟上,通常存在一個檔案中
2.在網絡上傳送對象的位元組序列
》》》》》
》對象序列化的目标是将對象儲存到磁盤中,或允許在網絡中直接傳輸對象,對象序列化機制允許把記憶體中的java對象轉換成與平台無關的二進制流,進而允許把這種二進制流持久持久的儲存在磁盤上,通過網絡将這種二進制流傳輸到另一個網絡節點。其他程式一旦獲得了這種二進制流(無論是從磁盤中擷取的,還是通過網絡擷取的)都可以将這種二進制流恢複成原來的java對象。
》 序列化機制允許将實作序列化的java對象轉換成位元組序列,這些位元組序列可以儲存在磁盤中,或通過網絡傳輸,以備以後重新恢複成原來的對象,序列化機制使得對象可以脫離程式的運作而獨立存在。
》 對象的序列化是指将一個java對象寫入IO流中,于此對應的是對象的反序列化是指從IO流中恢複該java對象。
》 如果需要讓某個對象支援序列化機制,則必須讓他的類是可序列化的。為了讓某個類是可序列化的,該類必須實作如下二個接口之一:
》 Serializable或Externalizable
》 java中的含多類已經實作了Serializable,該接口是一個标記接口,實作該接口無需事先任何方法,它隻是表明該類的執行個體是可序列化的。
》 所有可能在網絡上傳輸的對象的類都應該是可序列化的,否則,程式将會出現異常,比如,RMI(遠端調用方法)過程中的參數和傳回值;所有需要儲存在磁盤的對象都必須是可序列化的。比如Web應用中需要儲存到HttpSession或ServleContext屬性的java對象。
》因為序列化是RMI‘過程中的參數和傳回值都必須實作的機制,而RMI又是javaEE技術的基礎,所有的分布式應用常常都需要跨平台、跨網絡,是以要求所有傳遞的參數,傳回值必須實作序列化,是以,序列化機制是javaEE平台的基礎,通常建議:程式建立的每個javaBean類都實作Serializable。
序列化及反序列化相關知識
1、在Java中,隻要一個類實作了java.io.Serializable接口,那麼它就可以被序列化。
2、通過ObjectOutputStream和ObjectInputStream對對象進行序列化及反序列化
3、虛拟機是否允許反序列化,不僅取決于類路徑和功能代碼是否一緻,一個非常重要的一點是兩個類的序列化 ID 是否一緻(就是 private static final long serialVersionUID)
4、序列化并不儲存靜态變量,因為靜态變量屬于類的狀态,不是對象的狀态。
5、要想将父類對象也序列化,就需要讓父類也實作Serializable 接口。
6、Transient 關鍵字的作用是控制變量的序列化,在變量聲明前加上該關鍵字,可以阻止該變量被序列化到檔案中,在被反序列化後,transient 變量的值被設為初始值,如 int 型的是 0,對象型的是 null。
7、伺服器端給用戶端發送序列化對象資料,對象中有一些資料是敏感的,比如密碼字元串等,希望對該密碼字段在序列化時,進行加密,而用戶端如果擁有解密的密鑰,隻有在用戶端進行反序列化時,才可以對密碼進行讀取,這樣可以一定程度保證序列化對象的資料安全。
序列化 ID 問題
1.虛拟機是否允許反序列化,不僅取決于類路徑和功能代碼是否一緻,一個非常重要的一點是兩個類的序列化 ID 是否一緻(就是 private static final long serialVersionUID = 1L)
2.序列化 ID 在 Eclipse 下提供了兩種生成政策,一個是固定的 1L,一個是随機生成一個不重複的 long 類型資料(實際上是使用 JDK 工具生成),在這裡有一個建議,如果沒有特殊需求,就是用預設的 1L 就可以,這樣可以確定代碼一緻時反序列化成功。那麼随機生成的序列化 ID 有什麼作用呢,有些時候,通過改變序列化 ID 可以用來限制某些使用者的使用。
ID工作機制:
序列化的時候系統會把目前類的serialVersionUID 寫入序列化的檔案中(也可能是其他中介),當反序列化的時候系統會去檢測檔案中的serialVersionUID ,看它是否和目前類的serialVersionUID 一緻,如果一緻就說明序列化的類的版本和目前類的版本是相同的,這個時候可以成功反序列化,否則就說明目前類和序列化的類相比發生了某些變換,比如成員變量的數量,類型可能發生了改變,這個時候就會抛異常,反序列化失敗。
serialVersionUID作用:
作用:
序列化時為了保持版本的相容性,即在版本更新時反序列化仍保持對象的唯一性。
那麼serialVersionUID 是如何生成,生成規則是怎麼樣的呢?
預設情況下,也就是不聲明serialVersionUID 屬性情況下,系統會按目前類的成員變量計算hash值并指派給serialVersionUID 。
是以,結論就出來了。聲明serialVersionUID ,可以很大程度上避免反序列化過程的失敗。比如當版本更新後,我們可能删除了某個成員變量,也可能增加了一些新的成員變量,這個時候我們的反序列化依然能夠成功,程式依然能夠最大程度地恢複資料,相反,如果不指定serialVersionUID ,程式就會挂掉。
如果類結構發生了非正常性改變,比如修改了類名,類型等,這個時候盡管serialVersionUID 驗證通過了,但是反序列化過程還是會失敗,因為類結構有了毀滅性的改變。
序列化的使用時機:
一:對象序列化可以實作分布式對象。
主要應用例如:RMI(即遠端調用Remote Method Invocation)要利用對象序列化運作遠端主機上的服務,就像在本地機上運作對象時一樣。
二:java對象序列化不僅保留一個對象的資料,而且遞歸儲存對象引用的每個對象的資料。可以将整個對象層次寫入位元組流中,可以儲存在檔案中或在網絡連接配接上傳遞。利用對象序列化可以進行對象的”深複制”,即複制對象本身及引用的對象本身。序列化一個對象可能得到整個對象序列。
三:序列化可以将記憶體中的類寫入檔案或資料庫中。
比如将某個類序列化後存為檔案,下次讀取時隻需将檔案中的資料反序列化就可以将原先的類還原到記憶體中。也可以将類序列化為流資料進行傳輸。總的來說就是将一個已經執行個體化的類轉成檔案存儲,下次需要執行個體化的時候隻要反序列化即可将類執行個體化到記憶體中并保留序列化時類中的所有變量和狀态。
四: 對象、檔案、資料,有許多不同的格式,很難統一傳輸和儲存序列化以後就都是位元組流了,無論原來是什麼東西,都能變成一樣的東西,就可以進行通用的格式傳輸或儲存,傳輸結束以後,要再次使用,就進行反序列化還原,這樣對象還是對象,檔案還是檔案
因為JAVA中要将對象序列化 為 流 的 形式進行傳輸
import java.io.Serializable;
public class FlyPig implements Serializable {
/**
*
*/
private static final long serialVersionUID = L;
private static String age = "269";
private String name;
private String color;
transient private String car;
private String weight;
public void show() {
System.out.println("nisfisf");
}
public FlyPig(String name, String color, String car, String weight) {
super();
this.name = name;
this.color = color;
this.car = car;
this.weight = weight;
}
public FlyPig() {
super();
// TODO Auto-generated constructor stub
}
public static String getAge() {
return age;
}
public static void setAge(String age) {
FlyPig.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getCar() {
return car;
}
public void setCar(String car) {
this.car = car;
}
@Override
public String toString() {
return "FlyPig [name=" + name + ", color=" + color + ", car=" + car + "]";
}
public String getWeight() {
return weight;
}
public void setWeight(String weight) {
this.weight = weight;
}
}
**import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.junit.Before;
import org.junit.Test;
public class Test1 {
private FlyPig flypig;
@Before
public void te() {
flypig = new FlyPig();
}
@Test
public void test() throws FileNotFoundException, IOException {
flypig.setColor("black");
flypig.setName("na");
flypig.setCar("333");
flypig.setWeight("sdfs");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("f:/flypif.txt")));
oos.writeObject(flypig);
System.err.println("序列化成功");
oos.close();
}
@Test
public void test1() throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("f:/flyPig.txt")));
FlyPig person = (FlyPig) ois.readObject();
System.out.println("FlyPig 對象反序列化成功!");
System.out.println(person);
person.show();
ois.close();
}
}**