序列化

Java 序列化
序列化作用
注意事項(xiàng)
Java 序列化的缺點(diǎn)
舉例說明
項(xiàng)目中曾遇到的一個(gè)小問題
參考

序列化定義:將一個(gè)對(duì)象編碼成一個(gè)字節(jié)流,稱作將該對(duì)象序列化,反之,將字節(jié)流重新構(gòu)建成對(duì)象,則稱作反序列化。

序列化作用
序列化將對(duì)象編碼成字節(jié)流,主要用于對(duì)象的持久化,遠(yuǎn)程通信,跨進(jìn)程訪問等地方。
比如開發(fā)中常用到的 ORM
框架 Mybatis
,或者 JPA
都是需要將實(shí)體類實(shí)現(xiàn)序列化接口才能使用,再者如緩存,緩存的對(duì)象如果沒有實(shí)現(xiàn) Serializable
接口,那么會(huì)拋出異常。
注意事項(xiàng)
父類實(shí)現(xiàn)了序列化,則子類自動(dòng)實(shí)現(xiàn)了序列化,即子類不需要顯式實(shí)現(xiàn) Serializable
接口。
當(dāng)父類沒有實(shí)現(xiàn)序列化,而子類需要實(shí)現(xiàn)時(shí),子類需要顯式實(shí)現(xiàn) Serializable
接口,并且父類中需要有無參的構(gòu)造函數(shù)。
序列化只對(duì)對(duì)象的屬性進(jìn)行保存,而不會(huì)保存其方法。
當(dāng)類中的實(shí)例變量引用了其他對(duì)象,那么在對(duì)該類進(jìn)行序列化時(shí),引用的對(duì)象也會(huì)被序列化。

Java 序列化的缺點(diǎn)
序列化會(huì)讓類變得不靈活。
實(shí)現(xiàn)序列化之后,會(huì)有一個(gè)序列化 ID ,我們可以使用默認(rèn)的 ID ,也可以重寫這 ID,如果沒有顯式指定該序列 ID ,系統(tǒng)會(huì)經(jīng)過一系列復(fù)雜的計(jì)算算出該 ID,那當(dāng)我們改變類中的方法時(shí),這個(gè) ID 就會(huì)變化,這時(shí)候往往就會(huì)報(bào) InvalidClassException
異常。

序列化可以重構(gòu),存在安全隱患。

無法跨語言,Java 進(jìn)行序列化,別的語言無法進(jìn)行反序列化。

序列化后的碼流太大。

Java 序列化性能較低。

舉例說明
父類實(shí)現(xiàn)了序列化,則子類自動(dòng)實(shí)現(xiàn)了序列化,即子類不需要顯式實(shí)現(xiàn) Serializable
接口

Parent.java

public class Parent implements Serializable { 
int age;
 public Parent(int age)
 { 
this.age = age; 
}
 public int getAge()
 {
 return age; 
}
 public void setAge(int age)
 { 
this.age = age;
 } 
@Override public String toString()
 { 
return "Parent{" + "age=" + age + '}'; }
}

Children.java

public class Children extends Parent { 
public Children(int age) 
{ 
super(age);
 } 
public void say() 
{ 
System.out.println("Hello World " + age);
 } 
@Override public String toString() 
{
 return "Children{" + "} " + super.toString(); }
}

Test.java

public class Test {
 public static void main(String[] args) throws IOException, ClassNotFoundException { 
// 持久化到文件中 Children children = new Children(12);
FileOutputStream outputStream = new FileOutputStream("test.txt"); ObjectOutputStream objectOutputStream = new
ObjectOutputStream(outputStream); objectOutputStream.writeObject(children); 
outputStream.close();
objectOutputStream.close();
// 從文件中讀取 FileInputStream inputStream = new FileInputStream("test.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); 
Children o = (Children) objectInputStream.readObject(); 
o.say();
inputStream.close();
objectInputStream.close();
 System.out.println(o);
// new File("test.txt").delete(); }
}

控制臺(tái)打印出

children

在這個(gè)例子中,父類 Parent實(shí)現(xiàn)了 Serializable接口,子類序列化時(shí)并不需要顯式實(shí)現(xiàn) Serializable。當(dāng)父類沒有實(shí)現(xiàn)序列化,而子類需要實(shí)現(xiàn)時(shí),子類需要顯式實(shí)現(xiàn) Serializable接口,并且父類中需要有無參的構(gòu)造函數(shù)。

如果將 Parent改寫,不實(shí)現(xiàn)序列化,讓子類自己來實(shí)現(xiàn)會(huì)怎樣呢?

Parent.java

public class Parent { int age; public Parent(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Parent{" + "age=" + age + '}'; }}

Children.java

public class Children extends Parent implements Serializable{ public Children(int age) { super(age); } public void say() { System.out.println("Hello World " + age); } @Override public String toString() { return "Children{" + "} " + super.toString(); }}

然后運(yùn)行 Test.java

error
控制臺(tái)會(huì)打印出沒有合適的構(gòu)造方法。
序列化會(huì)讓類變得不靈活。

繼續(xù)改寫上述代碼(注意:我們上述代碼沒有重寫 serialVersionUID),我們先將該 Children保存到文件中,然后再對(duì) Children進(jìn)行修改,修改之后,再?gòu)奈募邪褎偛疟4娴淖止?jié)碼反序列化,看看會(huì)出現(xiàn)什么問題。步驟:
運(yùn)行 Test.java

改寫 Children.java,新增字段 name,并改寫 say()方法注釋掉 Test.java
中的序列化方法,執(zhí)行其中的反序列化

改寫后的 Children.java

public class Children extends Parent {
 private String name; public String getName()
 { 
return name; 
} 
public void setName(String name) {
 this.name = name;
 } 
public Children(int age) { 
super(age);
 }
 public void say() { 
System.out.println("Hello World " + age + ",name: " + name); 
} 
@Override public String toString() {
 return "Children{" + "name='" + name + '\'' + "} " + super.toString(); }
}

控制臺(tái)打印出:

serial_version_error

為了解決這個(gè)問題,我們需要在 Children.java中重寫序列化 ID在 Children.java
加入 private static final long serialVersionUID = -1;
即可。
序列化只對(duì)對(duì)象的屬性進(jìn)行保存,而不會(huì)保存其方法。****序列化可以重構(gòu),存在安全隱患。

然后重新運(yùn)行按照上訴步驟重新執(zhí)行一遍,會(huì)發(fā)現(xiàn)不會(huì)報(bào)異常了,并且會(huì)發(fā)現(xiàn)當(dāng)我們?cè)谶\(yùn)行時(shí)期改變 Children
中的 say()方法時(shí),打印出的 say() 方法變了,而 Children中的屬性 age
不會(huì)改變,且會(huì)發(fā)現(xiàn) name默認(rèn)為 null,這里在運(yùn)行時(shí)我們重構(gòu)了 Children
類,改變了一些屬性及方法,這也就存在了安全隱患。當(dāng)類中的實(shí)例變量引用了其他對(duì)象,那么在對(duì)該類進(jìn)行序列化時(shí),引用的對(duì)象也會(huì)被序列化。如果該引用的對(duì)象沒有實(shí)例化,則不需要序列化。

創(chuàng)建一個(gè) XiaoMing類, 改寫 Children ,讓 Children包含 XiaoMing的引用,并且在 Children的構(gòu)造函數(shù)中初始化 XiaoMing,然后運(yùn)行 Test.java。
控制臺(tái)打印出:

xiaoming_error
表示 XiaoMing
沒有實(shí)現(xiàn)序列化接口,這說明在序列化對(duì)象的時(shí)候,對(duì)象的引用對(duì)象也會(huì)被序列化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容