概念
創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),信宜企業(yè)網(wǎng)站建設(shè),信宜品牌網(wǎng)站建設(shè),網(wǎng)站定制,信宜網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,信宜網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
一個(gè)對象如果想在硬盤上存儲,一定就需要借助于一定的數(shù)據(jù)格式。這種把對象轉(zhuǎn)換為硬盤存儲的格式的過程就叫做對象的序列化,同樣地,將這些文件再反向轉(zhuǎn)換為程序中對象的操作就叫做反序列化
一些復(fù)雜的解決方案可能是將對象轉(zhuǎn)換為json字符串的方式,這種方式的優(yōu)點(diǎn)是易讀,但是效率還是太低,所以Java的序列化的解決方案是將對象轉(zhuǎn)換為一個(gè)二進(jìn)制流的形式,來實(shí)現(xiàn)數(shù)據(jù)的持久化,本篇文章將會來詳細(xì)講解序列化的實(shí)現(xiàn)和原理
實(shí)現(xiàn)
準(zhǔn)備
我們這里有一個(gè)普通的對象,要注意的是這個(gè)類和其中用到的所有對象都需要實(shí)現(xiàn)序列化接口Serializable:
class Demo implements Serializable { int val = 10; String time = new SimpleDateFormat("HH:mm:ss").format(new Date()); A a = new A(20); @Override public String toString() { return "[hashcode=" + hashCode() + " val=" + val + ", time=" + time + ", A.val=" + a.val +"]"; } }
這個(gè)A是一個(gè)普通的對象,如下:
class A implements Serializable { int val = 20; public A(int val) { this.val = val; } }
現(xiàn)在我們有一個(gè)Demo對象,來輸出一下這個(gè)對象的標(biāo)志字符串:
Demo demo = new Demo(); System.out.println(demo.toString());
輸出結(jié)果:
[hashcode=1625635731 val=10, time=20:28:56, A.val=20]
序列化
現(xiàn)在,我們需要將這個(gè)對象序列化為二進(jìn)制流,則需要以下的操作:
FileOutputStream fileOutputStream = new FileOutputStream("target"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(demo); objectOutputStream.flush(); objectOutputStream.close();
這樣,demo對象就被我們持久化到硬盤的target文件中了
反序列化
反之,如果我們想將這個(gè)對象從target文件中取出,就需要如下的操作:
FileInputStream fileInputStream = new FileInputStream("target"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); Demo newDemo = (Demo)objectInputStream.readObject();
檢驗(yàn)
現(xiàn)在,我們用以下的語句來檢驗(yàn)這兩個(gè)對象是否是一個(gè)對象:
System.out.println(newDemo.toString()); System.out.println("demo == newDemo : " + (demo == newDemo));
輸出
[hashcode=885284298 val=10, time=20:28:56, A.val=20] demo == newDemo : false
我們會發(fā)現(xiàn),反序列化得到的對象雖然值和原有對象一致,但是其不是同一個(gè)對象,這一點(diǎn)很重要
原理
我們打開序列化生成的target文件,這里需要用二進(jìn)制流的方式打開:
這里可以將文件分為5個(gè)部分:
也就是說,在這個(gè)二進(jìn)制文件中,通過這幾部分就能表明一個(gè)類的全部信息,在反序列化的過程中,Java將會按照指定的文件格式來從文件中恢復(fù)數(shù)據(jù)
注意事項(xiàng)
序列化的類一定要實(shí)現(xiàn)Serializable接口
序列化類中包含的自定義對象都需要實(shí)現(xiàn)Serializable接口
這兩點(diǎn)是為什么呢,我們來看ObjectOutputStream中的writeObject0方法,這里截取了一小段:
if (obj instanceof String) { writeString((String) obj, unshared); } else if (cl.isArray()) { writeArray(obj, desc, unshared); } else if (obj instanceof Enum) { writeEnum((Enum<?>) obj, desc, unshared); } else if (obj instanceof Serializable) { writeOrdinaryObject(obj, desc, unshared); } else { if (extendedDebugInfo) { throw new NotSerializableException( cl.getName() + "\n" + debugInfoStack.toString()); } else { throw new NotSerializableException(cl.getName()); } }
這段代碼中的obj不僅僅是被序列化的對象,還會是這個(gè)對象中的所有字段,也就是說其中的域?qū)ο?,必須是字符串、?shù)組、枚舉和序列化接口中的一種,否則就會拋出異常
序列化ID
其實(shí),還有一點(diǎn)注意事項(xiàng),我留在了這里來講:
在序列化和反序列化之間,對象的字段名稱、類型和數(shù)量均不能改變
這是為什么呢,我們來看反序列化中的一塊代碼:
if (model.serializable == osc.serializable && !cl.isArray() && suid != osc.getSerialVersionUID()) { throw new InvalidClassException(osc.name, "local class incompatible: " + "stream classdesc serialVersionUID = " + suid + ", local class serialVersionUID = " + osc.getSerialVersionUID()); }
這是ObjectStreamClass中的initNonProxy方法中的一段,這個(gè)方法也就是讀取我們序列化文件的核心方法,用于初始化類描述符
不過我們重點(diǎn)不在這里,重點(diǎn)是一個(gè)suid和osc.getSerialVersionUID()的比較,這時(shí)候就要涉及到一個(gè)序列化id的概念了,序列化id的聲明類似下面這種形式:
class Demo implements Serializable { // 這個(gè)序列化id一般的ide都會提供有自動生成的插件,感興趣的可以自行下載 private static final long serialVersionUID = -5809782578272943999L; // ... }
Java的反序列化成功與否的關(guān)鍵,就是比較文件的序列化id和類的序列化id是否一致,如果一致,則認(rèn)為文件中的對象和類對象是同一個(gè)對象,否則,就說明兩個(gè)類壓根就不是一個(gè)類,如果強(qiáng)行轉(zhuǎn)換則很有可能發(fā)生異常
但是我們之前沒有手動設(shè)置序列化id也一樣能反序列化成功不是嗎?其實(shí),之前能反序列化成功僅僅是因?yàn)槲覀儧]有改動原來的類,如果我們沒有設(shè)置序列化id,則以下任何的操作,均會導(dǎo)致反序列化失?。?/p>
看到了嗎,即使我們僅僅修改了字段的名稱,也會導(dǎo)致反序列化的失敗,如果不注意這一點(diǎn),將會導(dǎo)致所有反序列化操作的崩潰,但是只要我們設(shè)置一個(gè)序列化id,即使我們把類中元素刪的一干二凈,也一樣會反序列化成功,只不過是丟失屬性而已
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
網(wǎng)頁名稱:深入理解Java原生的序列化機(jī)制
本文URL:http://jinyejixie.com/article48/ijjshp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、網(wǎng)站設(shè)計(jì)、域名注冊、標(biāo)簽優(yōu)化、商城網(wǎng)站、企業(yè)建站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)