摘要:對(duì)象序列化對(duì)象序列化機(jī)制允許把內(nèi)存中的對(duì)象轉(zhuǎn)換成與平臺(tái)無(wú)關(guān)的二進(jìn)制流,從而可以保存到磁盤(pán)或者進(jìn)行網(wǎng)絡(luò)傳輸,其它程序獲得這個(gè)二進(jìn)制流后可以將其恢復(fù)成原來(lái)的對(duì)象。
對(duì)象序列化
對(duì)象序列化機(jī)制允許把內(nèi)存中的Java對(duì)象轉(zhuǎn)換成與平臺(tái)無(wú)關(guān)的二進(jìn)制流,從而可以保存到磁盤(pán)或者進(jìn)行網(wǎng)絡(luò)傳輸,其它程序獲得這個(gè)二進(jìn)制流后可以將其恢復(fù)成原來(lái)的Java對(duì)象。 序列化機(jī)制可以使對(duì)象可以脫離程序的運(yùn)行而對(duì)立存在
序列化的含義和意義 序列化序列化機(jī)制可以使對(duì)象可以脫離程序的運(yùn)行而對(duì)立存在
序列化(Serialize)指將一個(gè)java對(duì)象寫(xiě)入IO流中,與此對(duì)應(yīng)的是,對(duì)象的反序列化(Deserialize)則指從IO流中恢復(fù)該java對(duì)象
如果需要讓某個(gè)對(duì)象可以支持序列化機(jī)制,必須讓它的類(lèi)是可序列化(serializable),為了讓某個(gè)類(lèi)可序列化的,必須實(shí)現(xiàn)如下兩個(gè)接口之一:
Serializable:標(biāo)記接口,實(shí)現(xiàn)該接口無(wú)須實(shí)現(xiàn)任何方法,只是表明該類(lèi)的實(shí)例是可序列化的
Externalizable
所有在網(wǎng)絡(luò)上傳輸?shù)膶?duì)象都應(yīng)該是可序列化的,否則將會(huì)出現(xiàn)異常;所有需要保存到磁盤(pán)里的對(duì)象的類(lèi)都必須可序列化;程序創(chuàng)建的每個(gè)JavaBean類(lèi)都實(shí)現(xiàn)Serializable;
使用對(duì)象流實(shí)現(xiàn)序列化實(shí)現(xiàn)Serializable實(shí)現(xiàn)序列化的類(lèi),程序可以通過(guò)如下兩個(gè)步驟來(lái)序列化該對(duì)象:
1.創(chuàng)建一個(gè)ObjectOutputStream,這個(gè)輸出流是一個(gè)處理流,所以必須建立在其他節(jié)點(diǎn)流的基礎(chǔ)之上
// 創(chuàng)建個(gè)ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
2.調(diào)用ObjectOutputStream對(duì)象的writeObject方法輸出可序列化對(duì)象
// 將一個(gè)Person對(duì)象輸出到輸出流中 oos.writeObject(per);
定義一個(gè)NbaPlayer類(lèi),實(shí)現(xiàn)Serializable接口,該接口標(biāo)識(shí)該類(lèi)的對(duì)象是可序列化的
public class NbaPlayer implements java.io.Serializable { private String name; private int number; // 注意此處沒(méi)有提供無(wú)參數(shù)的構(gòu)造器! public NbaPlayer(String name, int number) { System.out.println("有參數(shù)的構(gòu)造器"); this.name = name; this.number = number; } // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // number的setter和getter方法 public void setNumber(int number) { this.number = number; } public int getNumber() { return this.number; } }
使用ObjectOutputStream將一個(gè)NbaPlayer對(duì)象寫(xiě)入磁盤(pán)文件
import java.io.*; public class WriteObject { public static void main(String[] args) { try( // 創(chuàng)建一個(gè)ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("object.txt"))) { NbaPlayer player = new NbaPlayer("維斯布魯克", 0); // 將player對(duì)象寫(xiě)入輸出流 oos.writeObject(player); } catch (IOException ex) { ex.printStackTrace(); } } }反序列化
從二進(jìn)制流中恢復(fù)Java對(duì)象,則需要使用反序列化,程序可以通過(guò)如下兩個(gè)步驟來(lái)序列化該對(duì)象:
1.創(chuàng)建一個(gè)ObjectInputStream輸入流,這個(gè)輸入流是一個(gè)處理流,所以必須建立在其他節(jié)點(diǎn)流的基礎(chǔ)之上
// 創(chuàng)建個(gè)ObjectInputStream輸入流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
2.調(diào)用ObjectInputStream對(duì)象的readObject()方法讀取流中的對(duì)象,該方法返回一個(gè)Object類(lèi)型的Java對(duì)象,可進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換成其真實(shí)的類(lèi)型
// 從輸入流中讀取一個(gè)Java對(duì)象,并將其強(qiáng)制類(lèi)型轉(zhuǎn)換為Person類(lèi) Person p = (Person)ois.readObject();
從object.txt文件中讀取NbaPlayer對(duì)象的步驟
import java.io.*; public class ReadObject { public static void main(String[] args) { try( // 創(chuàng)建一個(gè)ObjectInputStream輸入流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("object.txt"))) { // 從輸入流中讀取一個(gè)Java對(duì)象,并將其強(qiáng)制類(lèi)型轉(zhuǎn)換為NbaPlayer類(lèi) NbaPlayer player = (NbaPlayer)ois.readObject(); System.out.println("名字為:" + player.getName() + " 號(hào)碼為:" + player.getNumber()); } catch (Exception ex) { ex.printStackTrace(); } } }
反序列化讀取的僅僅是Java對(duì)象的數(shù)據(jù),而不是Java類(lèi),因此采用反序列化恢復(fù)Java對(duì)象時(shí),必須提供Java對(duì)象所屬的class文件,否則會(huì)引發(fā)ClassNotFoundException異常;反序列化機(jī)制無(wú)須通過(guò)構(gòu)造器來(lái)初始化Java對(duì)象
如果使用序列化機(jī)制向文件中寫(xiě)入了多個(gè)Java對(duì)象,使用反序列化機(jī)制恢復(fù)對(duì)象必須按照實(shí)際寫(xiě)入的順序讀取。當(dāng)一個(gè)可序列化類(lèi)有多個(gè)父類(lèi)時(shí)(包括直接父類(lèi)和間接父類(lèi)),這些父類(lèi)要么有無(wú)參的構(gòu)造器,要么也是可序列化的—否則反序列化將拋出InvalidClassException異常。如果父類(lèi)是不可序列化的,只是帶有無(wú)參數(shù)的構(gòu)造器,則該父類(lèi)定義的Field值不會(huì)被序列化到二進(jìn)制流中
對(duì)象引用的序列化如果某個(gè)類(lèi)的Field類(lèi)型不是基本類(lèi)型或者String類(lèi)型,而是另一個(gè)引用類(lèi)型,那么這個(gè)引用類(lèi)型必須是可序列化的,否則有用該類(lèi)型的Field的類(lèi)也是不可序列化的
public class AllStar implements java.io.Serializable { private String name; private NbaPlayer player; public AllStar(String name, NbaPlayer player) { this.name = name; this.player = player; } // name的setter和getter方法 public String getName() { return this.name; } public void setName(String name) { this.name = name; } // player的setter和getter方法 public NbaPlayer getPlayer() { return player; } public void setPlayer(NbaPlayer player) { this.player = player; } }Java特殊的序列化算法
所有保存到磁盤(pán)中的對(duì)象都有一個(gè)序列化編號(hào)
當(dāng)程序試圖序列化一個(gè)對(duì)象時(shí),程序?qū)⑾葯z查該對(duì)象是否已經(jīng)被序列化過(guò),只有該對(duì)象從未(在本次虛擬中機(jī))被序列化過(guò),系統(tǒng)才會(huì)將該對(duì)象轉(zhuǎn)換成字節(jié)序列并輸出
如果某個(gè)對(duì)象已經(jīng)序列化過(guò),程序?qū)⒅皇侵苯虞敵鲆粋€(gè)序列化編號(hào),而不是再次重新序列化該對(duì)象
import java.io.*; public class WriteAllStar { public static void main(String[] args) { try( // 創(chuàng)建一個(gè)ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("allStar.txt"))) { NbaPlayer player = new NbaPlayer("詹姆斯哈登", 13); AllStar allStar1 = new AllStar("西部全明星", player); AllStar allStar2 = new AllStar("首發(fā)后衛(wèi)", player); // 依次將四個(gè)對(duì)象寫(xiě)入輸出流 oos.writeObject(allStar1); oos.writeObject(allStar2); oos.writeObject(player); oos.writeObject(allStar2); } catch (IOException ex) { ex.printStackTrace(); } } }
4個(gè)寫(xiě)入輸出流的對(duì)象,實(shí)際上只序列化了3個(gè),而且序列的兩個(gè)AllStar對(duì)象的player引用實(shí)際是同一個(gè)NbaPlayer對(duì)象。以下程序讀取序列化文件中的對(duì)象
import java.io.*; public class ReadAllStar { public static void main(String[] args) { try( // 創(chuàng)建一個(gè)ObjectInputStream輸出流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("allStar.txt"))) { // 依次讀取ObjectInputStream輸入流中的四個(gè)對(duì)象 AllStar star1 = (AllStar)ois.readObject(); AllStar star2 = (AllStar)ois.readObject(); NbaPlayer player = (NbaPlayer)ois.readObject(); AllStar star3 = (AllStar)ois.readObject(); // 輸出true System.out.println("star1的player引用和player是否相同:" + (star1.getPlayer() == player)); // 輸出true System.out.println("star2的player引用和player是否相同:" + (star2.getPlayer() == player)); // 輸出true System.out.println("star2和star3是否是同一個(gè)對(duì)象:" + (star2 == star3)); } catch (Exception ex) { ex.printStackTrace(); } } }
如果多次序列化同一個(gè)可變Java對(duì)象時(shí),只有第一次序列化時(shí)才會(huì)把該Java對(duì)象轉(zhuǎn)換成字節(jié)序列并輸出
當(dāng)使用Java序列化機(jī)制序列化可變對(duì)象時(shí),只有第一次調(diào)用WriteObject()方法來(lái)輸出對(duì)象時(shí)才會(huì)將對(duì)象轉(zhuǎn)換成字節(jié)序列,并寫(xiě)入到ObjectOutputStream;即使在后面程序中,該對(duì)象的實(shí)例變量發(fā)生了改變,再次調(diào)用WriteObject()方法輸出該對(duì)象時(shí),改變后的實(shí)例變量也不會(huì)被輸出
import java.io.*; public class SerializeMutable { public static void main(String[] args) { try( // 創(chuàng)建一個(gè)ObjectOutputStream輸入流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("mutable.txt")); // 創(chuàng)建一個(gè)ObjectInputStream輸入流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("mutable.txt"))) { NbaPlayer player = new NbaPlayer("斯蒂芬?guī)炖?, 30); // 系統(tǒng)會(huì)player對(duì)象轉(zhuǎn)換字節(jié)序列并輸出 oos.writeObject(player); // 改變per對(duì)象的name實(shí)例變量 player.setName("塞斯庫(kù)里"); // 系統(tǒng)只是輸出序列化編號(hào),所以改變后的name不會(huì)被序列化 oos.writeObject(player); NbaPlayer player1 = (NbaPlayer)ois.readObject(); //① NbaPlayer player2 = (NbaPlayer)ois.readObject(); //② // 下面輸出true,即反序列化后player1等于player2 System.out.println(player1 == player2); // 下面依然看到輸出"斯蒂芬?guī)炖?,即改變后的實(shí)例變量沒(méi)有被序列化 System.out.println(player2.getName()); } catch (Exception ex) { ex.printStackTrace(); } } }自定義序列化
在一些特殊的場(chǎng)景下,如果一個(gè)類(lèi)里包含的某些實(shí)例變量是敏感信息,這時(shí)不希望系統(tǒng)將該實(shí)例變量值進(jìn)行實(shí)例化;或者某個(gè)實(shí)例變量的類(lèi)型是不可序列化的,因此不希望對(duì)該實(shí)例變量進(jìn)行遞歸實(shí)例化,以避免引發(fā)java.io.NotSerializableException異常
當(dāng)對(duì)某個(gè)對(duì)象進(jìn)行序列化時(shí),系統(tǒng)會(huì)自動(dòng)把該對(duì)象的所有實(shí)例變量依次進(jìn)行序列化,如果某個(gè)實(shí)例變量引用到另一個(gè)對(duì)象,則被引用的對(duì)象也會(huì)被序列化;如果被引用的對(duì)象的實(shí)例變量也引用了其他對(duì)象,則被引用的對(duì)象也會(huì)被序列化,這種情況被稱為遞歸序列化
在實(shí)例變量前面使用transient關(guān)鍵字修飾,可以指定java序列化時(shí)無(wú)須理會(huì)該實(shí)例變量
public class NbaPlayer implements java.io.Serializable { private String name; private transient int number; // 注意此處沒(méi)有提供無(wú)參數(shù)的構(gòu)造器! public NbaPlayer(String name, int number) { System.out.println("有參數(shù)的構(gòu)造器"); this.name = name; this.number = number; } // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // number的setter和getter方法 public void setNumber(int number) { this.number = number; } public int getNumber() { return this.number; } }
transient關(guān)鍵字只能用于修飾實(shí)例變量,不可修飾Java程序中的其他成分
import java.io.*; public class TransientTest { public static void main(String[] args) { try( // 創(chuàng)建一個(gè)ObjectOutputStream輸出流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("transient.txt")); // 創(chuàng)建一個(gè)ObjectInputStream輸入流 ObjectInputStream ois = new ObjectInputStream( new FileInputStream("transient.txt"))) { NbaPlayer per = new NbaPlayer("克萊湯普森", 11); // 系統(tǒng)會(huì)per對(duì)象轉(zhuǎn)換字節(jié)序列并輸出 oos.writeObject(per); NbaPlayer p = (NbaPlayer)ois.readObject(); System.out.println(p.getNumber()); } catch (Exception ex) { ex.printStackTrace(); } } }
在序列化和反序列化過(guò)程中需要特殊處理的類(lèi)應(yīng)該提供如下特殊簽名的方法,這些特殊的方法用以實(shí)現(xiàn)自定義:
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
private void readObjectNoData() throws ObjectStreamException
writeObject()方法負(fù)責(zé)寫(xiě)入特定類(lèi)的實(shí)例的狀態(tài),以便相應(yīng)的readObject()方法可以恢復(fù)它。通過(guò)重寫(xiě)該方法,可以完全獲得對(duì)序列化機(jī)制的控制,自主決定哪些實(shí)例變量需要序列化,怎樣序列化。在默認(rèn)情況下,該方法會(huì)調(diào)用out.defaultWriteObject來(lái)保存Java對(duì)象的各實(shí)例變量,從而可以實(shí)現(xiàn)序列化Java對(duì)象狀態(tài)的目的
readObject()方法負(fù)責(zé)從流中讀取并恢復(fù)對(duì)象實(shí)例變量,通過(guò)重寫(xiě)該方法,可以完全獲得對(duì)反序列化機(jī)制的控制,可以自主決定需要反序列化哪些實(shí)例變量,怎樣反序列化。在默認(rèn)情況下,該方法會(huì)調(diào)用in.defaultReadObject來(lái)恢復(fù)Java對(duì)象的非瞬態(tài)實(shí)例變量
通常情況下readObject()方法與writeObject()方法對(duì)應(yīng),如果writeObject()方法中對(duì)Java對(duì)象的實(shí)例變量進(jìn)行了一些處理,則應(yīng)該在readObject()方法中對(duì)該實(shí)例變量進(jìn)行相應(yīng)的反處理,以便正確恢復(fù)該對(duì)象
當(dāng)序列化流不完整時(shí),readObjectNoData()方法可以用來(lái)正確地初始化反序列化的對(duì)象
import java.io.IOException; public class NbaPlayer implements java.io.Serializable { private String name; private int number; // 注意此處沒(méi)有提供無(wú)參數(shù)的構(gòu)造器! public NbaPlayer(String name, int number) { System.out.println("有參數(shù)的構(gòu)造器"); this.name = name; this.number = number; } // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // number的setter和getter方法 public void setNumber(int number) { this.number = number; } public int getNumber() { return this.number; } private void writeObject(java.io.ObjectOutputStream out) throws IOException { // 將name實(shí)例變量值反轉(zhuǎn)后寫(xiě)入二進(jìn)制流 out.writeObject(new StringBuffer(name).reverse()); out.writeInt(number); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { // 將讀取的字符串反轉(zhuǎn)后賦給name實(shí)例變量 this.name = ((StringBuffer)in.readObject()).reverse().toString(); this.number = in.readInt(); } }
writeObject()方法存儲(chǔ)實(shí)例變量的順序應(yīng)該和readObject()方法中恢復(fù)實(shí)例變量的順序一致,否則將不能正常恢復(fù)該Java對(duì)象
ANY-ACCESS-MODIFIER Object writeReplace() 實(shí)現(xiàn)序列化某個(gè)對(duì)象時(shí)替換該對(duì)象
此writeReplace()方法將由序列化機(jī)制調(diào)用,只要該方法存在。因?yàn)樵摲椒梢該碛兴接?private),受保護(hù)的(protected)和包私有(package-private)等訪問(wèn)權(quán)限,所以其子類(lèi)有可能獲得該方法
下面程序的writeReplace()方法,這樣可以在寫(xiě)入NbaPlayer對(duì)象時(shí)將該對(duì)象替換成ArrayList
// 重寫(xiě)writeReplace方法,程序在序列化該對(duì)象之前,先調(diào)用該方法 private Object writeReplace() throws ObjectStreamException { ArrayList
Java的序列化機(jī)制保證在序列化某個(gè)對(duì)象之前,先調(diào)用該對(duì)象的writeReplace()方法,如果該方法返回另一個(gè)Java對(duì)象,則系統(tǒng)轉(zhuǎn)為序列化另一個(gè)對(duì)象。如下程序表面上是序列化NbaPlayer對(duì)象,但實(shí)際上序列化的是ArrayList
// 系統(tǒng)將player對(duì)象轉(zhuǎn)換字節(jié)序列并輸出 oos.writeObject(player); // 反序列化讀取得到的是ArrayList ArrayList list = (ArrayList)ois.readObject(); System.out.println(list);
系統(tǒng)在序列化某個(gè)對(duì)象之前,會(huì)先調(diào)用該對(duì)象的writeReplace()和writeObject()兩個(gè)方法,系統(tǒng)總是先調(diào)用被序列化對(duì)象的writeReplace()方法,如果該方法返回另一個(gè)對(duì)象,系統(tǒng)將再次調(diào)用另一個(gè)對(duì)象的writeReplace()方法,直到該方法不再返回另一個(gè)對(duì)象為止,程序最后將調(diào)用該對(duì)象的writeObject()方法來(lái)保存該對(duì)象的狀態(tài)
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException實(shí)現(xiàn)保護(hù)性復(fù)制整個(gè)對(duì)象,緊挨著readObject()之后被調(diào)用,該方法的返回值將會(huì)代替原來(lái)反序列化的對(duì)象,而原來(lái)readObject()反序列化的對(duì)象將會(huì)立即丟棄
Java的序列化機(jī)制:首先調(diào)用writeReplace(),其次調(diào)用writeObject(),最后調(diào)用writeResolve()
readObject()方法在序列化單例類(lèi),枚舉類(lèi)時(shí)尤其有用
反序列化機(jī)制在恢復(fù)java對(duì)象時(shí)無(wú)須調(diào)用構(gòu)造器來(lái)初始化java對(duì)象。從這個(gè)意義上來(lái)看,序列化機(jī)制可以用來(lái)"克隆"對(duì)象;所有單例類(lèi),枚舉類(lèi)在實(shí)現(xiàn)序列化時(shí)都應(yīng)該提供readResolve()方法,這樣才可以保證反序列化的對(duì)象依然正常;readResolve()方法建議使用final修飾
另一種自定義序列化機(jī)制這種序列化方式完全由程序員決定存儲(chǔ)和恢復(fù)對(duì)象數(shù)據(jù)。要實(shí)現(xiàn)該目標(biāo),必須實(shí)現(xiàn)Externalizable接口,該接口里定義了如下兩個(gè)方法:
void readExternal(ObjectInput in):需要序列化的類(lèi)實(shí)現(xiàn)readExternal()方法來(lái)實(shí)現(xiàn)反序列化。該方法調(diào)用DataInput(它是ObjectInput的父接口)的方法來(lái)恢復(fù)基本類(lèi)型的實(shí)例變量值,調(diào)用ObjectInput的readObject()方法來(lái)恢復(fù)引用類(lèi)型的實(shí)例變量值
void writeExternal(Object out):需要序列化的類(lèi)實(shí)現(xiàn)該方法來(lái)保存對(duì)象的狀態(tài)。該方法調(diào)用DataOutput(它是ObjectOutput的父接口)的方法來(lái)保存基本類(lèi)型的實(shí)例變量值,調(diào)用ObjectOutput的writeObject()方法來(lái)保存引用類(lèi)型的實(shí)例變量值
import java.io.*; public class Player implements java.io.Externalizable { private String name; private int number; // 注意此處沒(méi)有提供無(wú)參數(shù)的構(gòu)造器! public Player(String name, int number) { System.out.println("有參數(shù)的構(gòu)造器"); this.name = name; this.number = number; } // 省略name與number的setter和getter方法 // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // number的setter和getter方法 public void setNumber(int number) { this.number = number; } public int getNumber() { return this.number; } public void writeExternal(java.io.ObjectOutput out) throws IOException { // 將name實(shí)例變量的值反轉(zhuǎn)后寫(xiě)入二進(jìn)制流 out.writeObject(new StringBuffer(name).reverse()); out.writeInt(number); } public void readExternal(java.io.ObjectInput in) throws IOException, ClassNotFoundException { // 將讀取的字符串反轉(zhuǎn)后賦給name實(shí)例變量 this.name = ((StringBuffer)in.readObject()).reverse().toString(); this.number = in.readInt(); } }
兩種序列化機(jī)制的對(duì)比
實(shí)現(xiàn)Serializable接口 | 實(shí)現(xiàn)Externalizable接口 |
---|---|
系統(tǒng)自動(dòng)存儲(chǔ)必要信息 | 程序員決定存儲(chǔ)哪些信息 |
Java內(nèi)建支持,易于實(shí)現(xiàn),只需實(shí)現(xiàn)該接口即可,無(wú)須任何代碼支持 | 僅僅提供兩個(gè)空方法,實(shí)現(xiàn)該接口必須為兩個(gè)空方法提供實(shí)現(xiàn) |
性能略差 | 性能略好 |
對(duì)象序列化的注意事項(xiàng):
對(duì)象的類(lèi)名、實(shí)例變量(包括基本類(lèi)型、數(shù)組、對(duì)其他對(duì)象的引用)都會(huì)被序列化;方法、類(lèi)變量(即static修飾的成員變量)、transient實(shí)例變量(也被稱為瞬態(tài)實(shí)例變量)都不會(huì)被序列化
反序列化讀取的僅僅是Java對(duì)象的數(shù)據(jù),而不是Java類(lèi),因此采用反序列化恢復(fù)Java對(duì)象時(shí),必須提供Java對(duì)象所屬的class文件,否則會(huì)引發(fā)ClassNotFoundException異常
實(shí)現(xiàn)Serializable接口的類(lèi)如果需要讓某個(gè)實(shí)例變量不被序列化,則可以在該實(shí)例變量前加transient修飾符,而不是static關(guān)鍵字,雖然static關(guān)鍵字也可以達(dá)到這個(gè)效果,但static關(guān)鍵字不能這樣用
保證序列化對(duì)象的實(shí)例變量類(lèi)型也是可序列化的,否則需要使用transient修飾該變量
當(dāng)通過(guò)文件、網(wǎng)絡(luò)來(lái)讀取序列化后的對(duì)象時(shí),必須按照實(shí)際寫(xiě)入的順序讀取
版本隨著項(xiàng)目的設(shè)計(jì),系統(tǒng)的class文件也會(huì)升級(jí),Java如何保證兩個(gè)class文件的兼容性?為了在反序列化時(shí)確保序列化版本的兼容性,最好在每個(gè)要序列化的類(lèi)中加入private static final long serialVersionUID這個(gè)屬性,具體數(shù)值自定義。這樣,即使某個(gè)類(lèi)在與之對(duì)應(yīng)的對(duì)象已經(jīng)序列化出去后做了修改,該對(duì)象依然可以被正確反序列化
如不顯式定義該變量值,這個(gè)變量值將由JVM根據(jù)類(lèi)的相關(guān)信息計(jì)算,而修改后的類(lèi)的計(jì)算結(jié)果與修改前的類(lèi)的計(jì)算結(jié)果往往不同,從而造成對(duì)象的反序列化因?yàn)轭?lèi)版本不兼容而失敗
導(dǎo)致該類(lèi)實(shí)例的反序列化失敗的類(lèi)修改操作:
如果修改類(lèi)時(shí)僅僅修改了方法,則反序列化完全不受任何影響,類(lèi)定義無(wú)需修改serizlVersionUID屬性值
如果修飾類(lèi)時(shí)僅僅修改了靜態(tài)屬性或瞬態(tài)(transient)屬性,則反序列化不受任何影響,類(lèi)定義無(wú)需修改serialVersionUID屬性值
如果修改類(lèi)時(shí)修飾了非靜態(tài)、非瞬態(tài)屬性,則可能導(dǎo)致序列化版本不兼容,如果對(duì)象流中的對(duì)象和新類(lèi)中包含同名的屬性,而屬性類(lèi)型不同,則反序列化失敗 ,類(lèi)定義應(yīng)該更新serialVersionUID屬性值。如果新類(lèi)比對(duì)象流中對(duì)象包含更多的 屬性,序列化版本也可以兼容,類(lèi)定義可以不更新serialVersionUID屬性值;但反序列化得到的新對(duì)象中多出的屬性值都是null(引用類(lèi)型屬性)或0(基本類(lèi)型屬性)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66592.html
摘要:在中,對(duì)象的序列化與反序列化被廣泛應(yīng)用到遠(yuǎn)程方法調(diào)用及網(wǎng)絡(luò)傳輸中。相關(guān)接口及類(lèi)為了方便開(kāi)發(fā)人員將對(duì)象進(jìn)行序列化及反序列化提供了一套方便的來(lái)支持。未實(shí)現(xiàn)此接口的類(lèi)將無(wú)法使其任何狀態(tài)序列化或反序列化。 序列化與反序列化 序列化 (Serialization)是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^(guò)程。一般將一個(gè)對(duì)象存儲(chǔ)至一個(gè)儲(chǔ)存媒介,例如檔案或是記億體緩沖等。在網(wǎng)絡(luò)傳輸過(guò)程中,可以...
摘要:序列化對(duì)象和平臺(tái)無(wú)關(guān),序列化得到的字節(jié)流可以在任何平臺(tái)反序列化。從文件中或網(wǎng)絡(luò)上獲得序列化的字節(jié)流后,根據(jù)字節(jié)流中所保存的對(duì)象狀態(tài)及描述信息,通過(guò)反序列化重建對(duì)象。因此意味著不要序列化靜態(tài)變量不屬于對(duì)象狀態(tài)的一部分,因此它不參與序列化。 一.序列化和反序列化(1)序列化:將內(nèi)存中的對(duì)象轉(zhuǎn)化為字節(jié)序列,用于持久化到磁盤(pán)中或者通過(guò)網(wǎng)絡(luò)傳輸。對(duì)象序列化的最主要的用處就是傳遞和保存對(duì)象,保證對(duì)...
摘要:使用對(duì)象序列化,在保存對(duì)象時(shí),會(huì)把其狀態(tài)保存為一組字節(jié),在未來(lái),再將這些字節(jié)組裝成對(duì)象。由此可知,對(duì)象序列化不會(huì)關(guān)注類(lèi)中的靜態(tài)變量。對(duì)象的讀寫(xiě)類(lèi)中對(duì)象的序列化工作是通過(guò)和來(lái)完成的。這就是為什么在此序列化過(guò)程中的無(wú)參構(gòu)造器會(huì)被調(diào)用。 Java對(duì)象的序列化 Java平臺(tái)允許我們?cè)趦?nèi)存中創(chuàng)建可復(fù)用的Java對(duì)象,但一般情況下,只有當(dāng)JVM處于運(yùn)行時(shí),這些對(duì)象才可能存在,即,這些對(duì)象的生命周...
摘要:虛擬機(jī)讀取其他進(jìn)程的數(shù)據(jù)對(duì)象的方法可以運(yùn)行平臺(tái)上的其他程序該方法產(chǎn)生一個(gè)對(duì)象對(duì)象代表由該程序啟動(dòng)啟動(dòng)的子進(jìn)程類(lèi)提供如下三個(gè)方法用于和其子進(jìn)程通信獲取子進(jìn)程的錯(cuò)誤流獲取子進(jìn)程的輸入流獲取子進(jìn)程的輸出流這里的輸入流輸出流容易混淆從程序的角度思考 Java虛擬機(jī)讀取其他進(jìn)程的數(shù)據(jù) Runtime對(duì)象的exec方法可以運(yùn)行平臺(tái)上的其他程序,該方法產(chǎn)生一個(gè)Process對(duì)象,Process對(duì)象...
摘要:的序列化是將一個(gè)對(duì)象表示成字節(jié)序列,該字節(jié)序列包括了對(duì)象的數(shù)據(jù),有關(guān)對(duì)象的類(lèi)型信息和存儲(chǔ)在對(duì)象中的數(shù)據(jù)類(lèi)型。任何實(shí)現(xiàn)了接口的類(lèi)都可以被序列化。一旦對(duì)象被序列化或者重新裝配,就會(huì)分別調(diào)用那兩個(gè)方法。 Java序列化 1. 什么是序列化? 序列化是將一個(gè)對(duì)象的狀態(tài),各屬性的值序列化保存起來(lái),然后在合適的時(shí)候通過(guò)反序列化獲得。 Java的序列化是將一個(gè)對(duì)象表示成字節(jié)序列,該字節(jié)序列包括了對(duì)象...
閱讀 1212·2021-11-17 09:33
閱讀 3622·2021-09-28 09:42
閱讀 3352·2021-09-13 10:35
閱讀 2512·2021-09-06 15:00
閱讀 2455·2021-08-27 13:12
閱讀 3619·2021-07-26 23:38
閱讀 1863·2019-08-30 15:55
閱讀 549·2019-08-30 15:53