怎么深入理解Java原生的序列化機(jī)制-創(chuàng)新互聯(lián)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)怎么深入理解Java原生的序列化機(jī)制,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

從策劃到設(shè)計(jì)制作,每一步都追求做到細(xì)膩,制作可持續(xù)發(fā)展的企業(yè)網(wǎng)站。為客戶提供成都網(wǎng)站建設(shè)、做網(wǎng)站、網(wǎng)站策劃、網(wǎng)頁(yè)設(shè)計(jì)、申請(qǐng)域名、虛擬主機(jī)、網(wǎng)絡(luò)營(yíng)銷(xiāo)、VI設(shè)計(jì)、 網(wǎng)站改版、漏洞修補(bǔ)等服務(wù)。為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,以客戶的口碑塑造優(yōu)易品牌,攜手廣大客戶,共同發(fā)展進(jìn)步。

概念

一個(gè)對(duì)象如果想在硬盤(pán)上存儲(chǔ),一定就需要借助于一定的數(shù)據(jù)格式。這種把對(duì)象轉(zhuǎn)換為硬盤(pán)存儲(chǔ)的格式的過(guò)程就叫做對(duì)象的序列化,同樣地,將這些文件再反向轉(zhuǎn)換為程序中對(duì)象的操作就叫做反序列化一些復(fù)雜的解決方案可能是將對(duì)象轉(zhuǎn)換為json字符串的方式,這種方式的優(yōu)點(diǎn)是易讀,但是效率還是太低,所以Java的序列化的解決方案是將對(duì)象轉(zhuǎn)換為一個(gè)二進(jìn)制流的形式,來(lái)實(shí)現(xiàn)數(shù)據(jù)的持久化,本篇文章將會(huì)來(lái)詳細(xì)講解序列化的實(shí)現(xiàn)和原理

實(shí)現(xiàn)

準(zhǔn)備

我們這里有一個(gè)普通的對(duì)象,要注意的是這個(gè)類和其中用到的所有對(duì)象都需要實(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);@Overridepublic String toString() {return "[hashcode=" + hashCode() + " val=" + val + ", time=" + time + ", A.val=" + a.val +"]";}}

這個(gè)A是一個(gè)普通的對(duì)象,如下:

class A implements Serializable {int val = 20;public A(int val) {this.val = val;}}

現(xiàn)在我們有一個(gè)Demo對(duì)象,來(lái)輸出一下這個(gè)對(duì)象的標(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è)對(duì)象序列化為二進(jìn)制流,則需要以下的操作:

FileOutputStream fileOutputStream = new FileOutputStream("target");ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);objectOutputStream.writeObject(demo);objectOutputStream.flush();objectOutputStream.close();

這樣,demo對(duì)象就被我們持久化到硬盤(pán)的target文件中了

反序列化

反之,如果我們想將這個(gè)對(duì)象從target文件中取出,就需要如下的操作:

FileInputStream fileInputStream = new FileInputStream("target");ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);Demo newDemo = (Demo)objectInputStream.readObject();

檢驗(yàn)

現(xiàn)在,我們用以下的語(yǔ)句來(lái)檢驗(yàn)這兩個(gè)對(duì)象是否是一個(gè)對(duì)象:

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

我們會(huì)發(fā)現(xiàn),反序列化得到的對(duì)象雖然值和原有對(duì)象一致,但是其不是同一個(gè)對(duì)象,這一點(diǎn)很重要

原理

我們打開(kāi)序列化生成的target文件,這里需要用二進(jìn)制流的方式打開(kāi):

這里可以將文件分為5個(gè)部分:

文件頭:聲明文件是一個(gè)對(duì)象序列化文件,同時(shí)聲明了序列化版本  類描述:聲明類信息,包括類名、序列化id,以及域的個(gè)數(shù)等屬性  屬性描述  父類信息描述  對(duì)象屬性的實(shí)際值

也就是說(shuō),在這個(gè)二進(jìn)制文件中,通過(guò)這幾部分就能表明一個(gè)類的全部信息,在反序列化的過(guò)程中,Java將會(huì)按照指定的文件格式來(lái)從文件中恢復(fù)數(shù)據(jù)

注意事項(xiàng)

序列化的類一定要實(shí)現(xiàn)Serializable接口序列化類中包含的自定義對(duì)象都需要實(shí)現(xiàn)Serializable接口

這兩點(diǎn)是為什么呢,我們來(lái)看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不僅僅是被序列化的對(duì)象,還會(huì)是這個(gè)對(duì)象中的所有字段,也就是說(shuō)其中的域?qū)ο?,必須是字符串、?shù)組、枚舉和序列化接口中的一種,否則就會(huì)拋出異常

序列化ID

其實(shí),還有一點(diǎn)注意事項(xiàng),我留在了這里來(lái)講:

在序列化和反序列化之間,對(duì)象的字段名稱、類型和數(shù)量均不能改變

這是為什么呢,我們來(lái)看反序列化中的一塊代碼:

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è)方法也就是讀取我們序列化文件的核心方法,用于初始化類描述符

不過(guò)我們重點(diǎn)不在這里,重點(diǎn)是一個(gè)suid和osc.getSerialVersionUID()的比較,這時(shí)候就要涉及到一個(gè)序列化id的概念了,序列化id的聲明類似下面這種形式:

class Demo implements Serializable {// 這個(gè)序列化id一般的ide都會(huì)提供有自動(dòng)生成的插件,感興趣的可以自行下載private static final long serialVersionUID = -5809782578272943999L;// ...}

Java的反序列化成功與否的關(guān)鍵,就是比較文件的序列化id和類的序列化id是否一致,如果一致,則認(rèn)為文件中的對(duì)象和類對(duì)象是同一個(gè)對(duì)象,否則,就說(shuō)明兩個(gè)類壓根就不是一個(gè)類,如果強(qiáng)行轉(zhuǎn)換則很有可能發(fā)生異常

但是我們之前沒(méi)有手動(dòng)設(shè)置序列化id也一樣能反序列化成功不是嗎?其實(shí),之前能反序列化成功僅僅是因?yàn)槲覀儧](méi)有改動(dòng)原來(lái)的類,如果我們沒(méi)有設(shè)置序列化id,則以下任何的操作,均會(huì)導(dǎo)致反序列化失?。?/p>

修改了字段/方法的名稱/類型  添加或刪除字段/方法

看到了嗎,即使我們僅僅修改了字段的名稱,也會(huì)導(dǎo)致反序列化的失敗,如果不注意這一點(diǎn),將會(huì)導(dǎo)致所有反序列化操作的崩潰,但是只要我們?cè)O(shè)置一個(gè)序列化id,即使我們把類中元素刪的一干二凈,也一樣會(huì)反序列化成功,只不過(guò)是丟失屬性而已

上述就是小編為大家分享的怎么深入理解Java原生的序列化機(jī)制了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

當(dāng)前文章:怎么深入理解Java原生的序列化機(jī)制-創(chuàng)新互聯(lián)
地址分享:http://bm7419.com/article48/ihpep.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)頁(yè)設(shè)計(jì)公司、用戶體驗(yàn)、面包屑導(dǎo)航、關(guān)鍵詞優(yōu)化、云服務(wù)器、響應(yīng)式網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

營(yíng)銷(xiāo)型網(wǎng)站建設(shè)