題外話:
從事IT要學習的東西太多了蹭睡,有時候會比較浮躁,因為要學的東西太多但又無從下手赶么,甚至有很多基礎都還沒有深入學習肩豁,這個時候應當靜下心來,正所謂不忘初心辫呻,方能始終清钥,之前一直聽說過序列化,但也沒有去深入一點點的了解過放闺,這個時候祟昭,就當好好鞏固下了~
java序列化
常被稱為持久化,將其寫入磁盤中怖侦。
對于一個存在于jvm的對象來說篡悟,內部的狀態(tài)保存在內存中,當jvm停止時這些狀態(tài)就丟失了匾寝,但有些時候對象的內部是需要持久保存的搬葬,對象序列化機制(object serialization)是Java語言內建的一種對象持久化方式,可以很容易的在JVM中的活動對象和字節(jié)數(shù)組(流)之間進行轉換艳悔,該機制中對象可以表示為字節(jié)序列急凰,該字節(jié)序列包括該對象的數(shù)據(jù),有關對象的類型的信息和存儲在對象中數(shù)據(jù)的類型猜年。
數(shù)據(jù)序列化就是將對象或者數(shù)據(jù)結構轉化成特定的格式香府,使其可在網(wǎng)絡中傳輸,或者可存儲在內存或者文件中码倦。反序列化則是相反的操作企孩,將對象從序列化數(shù)據(jù)中還原出來。而對象序列化后的數(shù)據(jù)格式可以是二進制袁稽,可以是XML勿璃,也可以是JSON等任何格式。
【整個過程在jvm獨立的推汽,在一個平臺上序列化的對象可以在另外的平臺反序列化】
java類序列化的條件:
1.該類必須實現(xiàn) java.io.Serializable接口补疑。
2.該類的所有屬性必須是可序列化的。如果有一個屬性不是可序列化的歹撒,則該屬性必須注明是短暫的莲组。如果你想知道一個 Java 標準類是否是可序列化的,請查看該類的文檔暖夭。檢驗一個類的實例是否能序列化十分簡單锹杈, 只需要查看該類有沒有實現(xiàn) java.io.Serializable接口撵孤。
為什么序列化
1.將結構化的對象變?yōu)闊o結構的字節(jié)流,存儲對象在存儲介質中竭望,方便下次使用可以快捷獲取邪码,便于數(shù)據(jù)傳輸。
2.序列化的過程通俗講咬清,就是一個“freeze”的過程闭专,它將一個對象freeze住,然后進行存儲旧烧,等到再次需要的時候影钉,再將這個對象de-freeze就可以立即使用。
jdk內置序列化
java對序列化提供了很好的支持掘剪,當一個對象實現(xiàn)了Serilizable接口斧拍,這個對象就可以被序列化,我們不關心其內在的原理杖小,只需要了解這個類實現(xiàn)了Serilizable接口肆汹,這個類的所有屬性和方法都會自動序列化∮枞ǎ可以說Serilizable只是一個標識昂勉,實際的序列化和反序列化工作是通過java.io.ObjectOuputStream和java.io.ObjectInputStream來完成的。ObjectOutputStream的writeObject方法可以把一個Java對象寫入到流中扫腺,ObjectInputStream的readObject方法可以從流中讀取一個Java對象岗照。
transient關鍵字
在實際開發(fā)過程中可能遇到說一個對象中的屬性有些需要序列化有些則不用,比如說一個用戶有些敏感信息(密碼,銀行卡號)笆环,為了安全考慮不需要在網(wǎng)絡操作中被傳輸攒至。
這種時候使用transient可以使對應的屬性不被寫入磁盤持久化。換句話說躁劣,這個對象的生命周期僅存在調用者的內存中而不被持久化在硬盤中或者網(wǎng)絡傳輸迫吐。
//使用例子
package serializable;
import java.io.*;
public class TransientTest implements Serializable{
static class UserInfo implements Serializable {
private String name; //此處加static反序列化后仍能取到是因為static修飾的變量存在jvm內存中
private transient String psw;//transient 只能修飾變量(屬性)
public UserInfo(String name, String psw) {
this.name = name;
this.psw = psw;
}
public String toString() {
return "name=" + name + ", psw=" + psw;
}
}
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("張三", "123456");
System.out.println(userInfo);
try {
// 序列化將對象屬性寫入到UserInfo.txt文件中,被設置為transient的屬性沒有被序列
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("UserInfo.txt"));
o.writeObject(userInfo);
o.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
// 重新讀取序列化內容
ObjectInputStream in = new ObjectInputStream(new FileInputStream("UserInfo.txt"));
UserInfo readUserInfo = (UserInfo) in.readObject();
System.out.println(readUserInfo.toString()); //修飾transient關鍵字的屬性打印為null
} catch (Exception e) {
e.printStackTrace();
}
}
}
Externalizable接口
在java中账忘,對象的序列化可以通過兩種接口實現(xiàn)志膀,除了Serilizable接口,還有就是Externalizable接口鳖擒。
1.若實現(xiàn)Serializable溉浙,則所有序列化將會自動執(zhí)行。
2.若實現(xiàn)Externalizable蒋荚,序列化的過程需手動執(zhí)行戳稽,需要在writeExternal方法中進行手工指定所要序列化的變量,與是否被transient修飾無關期升。
import java.io.*;
/**
* Created by LJW on 2018/5/28.
* Externalizable接口測試
*/
public class TestExternalizable implements Externalizable {
private transient String content = "就算被transient修飾惊奇,但如果實現(xiàn)的是Externalizable接口互躬,我還是可能被序列化";
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
public static void main(String[] args) throws Exception {
TestExternalizable et = new TestExternalizable();
//將TestExternalizable序列化到test.txt文件中
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
new File("test.txt")));
out.writeObject(et);
//反序列化test.txt中的信息
ObjectInput in = new ObjectInputStream(new FileInputStream(new File(
"test.txt")));
et = (TestExternalizable) in.readObject();
System.out.println(et.content);//成功打印content內容而不是null,說明反序列化有取到被transient修飾的變量屬性
out.close();
in.close();
}
}
serialVersionUID
在查看jdk源碼的時候,經(jīng)成奘保看到這種代碼
private static final long serialVersionUID = 2877471301981509474L; //xxxL
一個類如果使用了java.io.Serializable接口,在序列化到文件時會自動生成一個serialVersionUID吨铸,用于對類進行版本控制(通過判斷實體類的serialVersionUID來驗證版本一致性的行拢。在進行反序列化時祖秒,JVM會把傳來的字節(jié)流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,如果相同就認為是一致的舟奠,可以進行反序列化抬纸,否則就會出現(xiàn)序列化版本不一致InvalidCalssException的異常)
如何生成
Intellij IDEA可以自動為serializable的類生成一個serialVersionUID湿故。
File->Preferences->Inspections->Serializationissues,將其展開后將serialzable class without "serialVersionUID"打上勾墅茉;
之后雙擊下class類名 ALT+ENTER即可生成隨機的serialVersionUID
總結
- Java序列化就是把對象轉換成字節(jié)序列,而Java反序列化就是把字節(jié)序列還原成Java對象洋机,在java中可以通過實現(xiàn)Serializable和Externalizable兩種接口實現(xiàn)序列化。
- 采用Java序列化與反序列化技術刁标,一是可以實現(xiàn)數(shù)據(jù)的持久化膀懈,在MVC模式中很有用启搂;二是可以對象數(shù)據(jù)的遠程通信牢撼,序列化用于通信熏版,服務端把數(shù)據(jù)序列化發(fā)送到客戶端∏幔客戶端收到數(shù)據(jù)反序列化對數(shù)據(jù)操作禾嫉。
- 序列化的好處:通過序列化可以把數(shù)據(jù)永久保存在硬盤上(通常放在文件里)
- transient關鍵字只能修飾屬性,被transient修飾的屬性將不會被序列化(這邊的前提是實現(xiàn)Serializable接口尊惰,還有需注意被static修飾的屬性也無法被序列化弄屡,static修飾的變量存在jvm內存中,如果反序列化后得到static修飾的屬性全庸,是從jvm取而不是反序列化后得到)壶笼。
- serialVersionUID主要用于反序列化的時候驗證版本的一致性,常在jdk,各種jar包中使用炮障。