java.io.Serializable淺析
?
Java API中java.io.Serializable接口源碼:
1
public
interface
Serializable {
2
}
類通過實現java.io.Serializable接口可以啟用其序列化功能。未實現次接口的類無法使其任何狀態序列化或反序列化。可序列化類的所有子類型本身都是可序列化的。序列化接口沒有方法或字段,僅用于標識可序列化的語義。
Java的"對象序列化"能讓你將一個實現了Serializable接口的對象轉換成byte流,這樣日后要用這個對象時候,你就能把這些byte數據恢復出來,并據此重新構建那個對象了。
要想序列化對象,你必須先創建一個OutputStream,然后把它嵌進ObjectOutputStream。這時,你就能用writeObject()方法把對象寫入OutputStream了。
writeObject()方法負責寫入特定類的對象的狀態,以便相應的 readObject()方法可以還原它。通過調用 out.defaultWriteObject 可以調用保存 Object 的字段的默認機制。該方法本身不需要涉及屬于其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支持的用于基本數據類型的方法將各個字段寫入 ObjectOutputStream 來保存的。
讀的時候,你得把InputStream嵌到ObjectInputStream里面,然后再調用readObject()方法。不過這樣讀出來的,只是一個Object的reference,因此在用之前,還得先下傳。readObject() 方法負責從流中讀取并還原類字段。它可以調用 in.defaultReadObject 來調用默認機制,以還原對象的非靜態和非瞬態字段。 defaultReadObject()方法使用流中的信息來分配流中通過當前對象中相應命名字段保存的對象的字段。這用于處理類發展后需要添加新字段的情形。該方法本身不需要涉及屬于其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支持的用于基本數據類型的方法將各個字段寫入 ObjectOutputStream 來保存的。
在序列化時,有幾點要注意的:
1: 當一個對象被序列化時,只保存對象的非靜態成員變量(包括聲明為private的變量),不能保存任何的成員方法和靜態的成員變量。
2:如果一個對象的成員變量是一個對象,那么這個對象的數據成員也會被序列化。
3:如果一個可序列化的對象包含對某個不可序列化的對象的引用,那么整個序列化操作將會失敗,并且會拋出一個NotSerializableException。我們可以將這個引用標記為transient,那么對象仍然可以序列化。
?
1、序列化是干什么的?
簡單說就是為了 保存在內存中的各種對象的狀態 ,并且可以把保存的對象狀態再讀出來。雖然你可以用你自己的各種各樣的方法來保存Object States,但是Java給你提供一種應該比你自己好的保存對象狀態的機制,那就是序列化。
2、什么情況下需要序列化
a)當你想把的內存中的對象保存到一個文件中或者數據庫中時候;
b)當你想用套接字在網絡上傳送對象的時候;
c)當你想通過RMI傳輸對象的時候;
3、當對一個對象實現序列化時,究竟發生了什么?
在沒有序列化前,每個保存在堆(Heap)中的對象都有相應的狀態(state),即實例變量(instance ariable)比如:
1
Foo myFoo =
new
Foo();
2
myFoo .setWidth(37
);
3
myFoo.setHeight(70);
當通過下面的代碼序列化之后,MyFoo對象中的width和Height實例變量的值(37,70)都被保存到foo.ser文件中,這樣以后又可以把它從文件中讀出來,重新在堆中創建原來的對象。當然保存時候不僅僅是保存對象的實例變量的值,JVM還要保存一些小量信息,比如類的類型等以便恢復原來的對象。
1
FileOutputStream fs =
new
FileOutputStream("foo.ser"
);
2
ObjectOutputStream os =
new
ObjectOutputStream(fs);
3
os.writeObject(myFoo);
4、實現序列化(保存到一個文件)的步驟
a)Make a FileOutputStream
java 代碼
FileOutputStream fs = new FileOutputStream("foo.ser");
b)Make a ObjectOutputStream
java 代碼
ObjectOutputStream os = new ObjectOutputStream(fs);
c)write the object
java 代碼
os.writeObject(myObject1);
os.writeObject(myObject2);
os.writeObject(myObject3);
d) close the ObjectOutputStream
java 代碼
os.close();
5、舉例說明
?
1
public
class
Box
implements
Serializable {
2
private
static
final
long
serialVersionUID = -3450064362986273896L
;
3
4
private
int
width;
5
private
int
height;
6
7
public
static
void
main(String[] args) {
8
Box myBox=
new
Box();
9
myBox.setWidth(50
);
10
myBox.setHeight(30
);
11
try
{
12
FileOutputStream fs=
new
FileOutputStream("F:\\foo.ser"
);
13
ObjectOutputStream os=
new
ObjectOutputStream(fs);
14
os.writeObject(myBox);
15
os.close();
16
FileInputStream fi=
new
FileInputStream("F:\\foo.ser"
);
17
ObjectInputStream oi=
new
ObjectInputStream(fi);
18
Box box=
(Box)oi.readObject();
19
oi.close();
20
System.out.println(box.height+","+
box.width);
21
}
catch
(Exception e) {
22
e.printStackTrace();
23
}
24
}
25
26
public
int
getWidth() {
27
return
width;
28
}
29
public
void
setWidth(
int
width) {
30
this
.width =
width;
31
}
32
public
int
getHeight() {
33
return
height;
34
}
35
public
void
setHeight(
int
height) {
36
this
.height =
height;
37
}
38
}
6、相關注意事項
a)當一個父類實現序列化,子類自動實現序列化,不需要顯式實現Serializable接口;
b)當一個對象的實例變量引用其他對象,序列化該對象時也把引用對象進行序列化;
c)并非所有的對象都可以序列化,至于為什么不可以,有很多原因了,比如:
1.安全方面的原因,比如一個對象擁有private,public等field,對于一個要傳輸的對象,比如寫到文件,或者進行rmi傳輸 等等,在序列化進行傳輸的過程中,這個對象的private等域是不受保護的。
2. 資源分配方面的原因,比如socket,thread類,如果可以序列化,進行傳輸或者保存,也無法對他們進行重新的資源分配,而且,也是沒有必要這樣實現。
?
serialVersionUID
序列化運行時使用一個稱為 serialVersionUID 的版本號與每個可序列化類相關聯,該序列號在反序列化過程中用于驗證序列化對象的發送者和接收者是否為該對象加載了與序列化兼容的類。如果接收者加載的該對象的類的 serialVersionUID 與對應的發送者的類的版本號不同,則反序列化將會導致 ?
InvalidClassException
。可序列化類可以通過聲明名為?
"serialVersionUID"
?的字段(該字段必須是靜態 (static)、最終 (final) 的?
long
?型字段)顯式聲明其自己的 serialVersionUID:
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
如果可序列化類未顯式聲明 serialVersionUID,則序列化運行時將基于該類的各個方面計算該類的默認 serialVersionUID 值,如“Java(TM) 對象序列化規范”中所述。不過,
強烈建議
?所有可序列化類都顯式聲明 serialVersionUID 值
,原因是計算默認的 serialVersionUID 對類的詳細信息具有較高的敏感性,根據編譯器實現的不同可能千差萬別,這樣在反序列化過程中可能會導致意外的?
InvalidClassException
。因此,為保證 serialVersionUID 值跨不同 java 編譯器實現的一致性,序列化類必須聲明一個明確的 serialVersionUID 值。還強烈建議使用?
private
?修飾符顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應用于直接聲明類 -- serialVersionUID 字段作為繼承成員沒有用處。數組類不能聲明一個明確的 serialVersionUID,因此它們總是具有默認的計算值,但是數組類沒有匹配 serialVersionUID 值的要求。?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
