最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解Java原生的序列化機(jī)制

 更新時(shí)間:2019年06月06日 10:22:40   作者:mayoi7  
Java 提供了一種對象序列化的機(jī)制,該機(jī)制中,一個(gè)對象可以被表示為一個(gè)字節(jié)序列,該字節(jié)序列包括該對象的數(shù)據(jù)、有關(guān)對象的類型的信息和存儲在對象中數(shù)據(jù)的類型。下面小編和大家來一起學(xué)習(xí)一下吧

概念

一個(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ù)的持久化,本篇文章將會(huì)來詳細(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

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

原理

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

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

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

也就是說,在這個(gè)二進(jìn)制文件中,通過這幾部分就能表明一個(gè)類的全部信息,在反序列化的過程中,Java將會(huì)按照指定的文件格式來從文件中恢復(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不僅僅是被序列化的對象,還會(huì)是這個(gè)對象中的所有字段,也就是說其中的域?qū)ο?,必須是字符串、?shù)組、枚舉和序列化接口中的一種,否則就會(huì)拋出異常

序列化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都會(huì)提供有自動(dòng)生成的插件,感興趣的可以自行下載
private static final long serialVersionUID = -5809782578272943999L;
// ...
}

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

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

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

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java享元設(shè)計(jì)模式優(yōu)化對象創(chuàng)建提高性能和效率

    Java享元設(shè)計(jì)模式優(yōu)化對象創(chuàng)建提高性能和效率

    Java享元設(shè)計(jì)模式通過共享可重用的對象,減少了系統(tǒng)中對象的數(shù)量,優(yōu)化了對象的創(chuàng)建和管理,提高了性能和效率。它是一種經(jīng)典的設(shè)計(jì)模式,適用于需要處理大量相似對象的應(yīng)用程序
    2023-04-04
  • Lombok中@Builder和@SuperBuilder注解的用法案例

    Lombok中@Builder和@SuperBuilder注解的用法案例

    @Builder?是?lombok?中的注解,可以使用builder()構(gòu)造的Person.PersonBuilder對象進(jìn)行鏈?zhǔn)秸{(diào)用,給所有屬性依次賦值,這篇文章主要介紹了Lombok中@Builder和@SuperBuilder注解的用法,需要的朋友可以參考下
    2023-01-01
  • SpringBoot中事務(wù)的只讀屬性詳解

    SpringBoot中事務(wù)的只讀屬性詳解

    這篇文章主要介紹了SpringBoot中事務(wù)的只讀屬性詳解,在開發(fā)過程中,事務(wù)是一個(gè)非常重要的概念,在?Spring?Boot中,事務(wù)是通過?AOP?機(jī)制來實(shí)現(xiàn)的,可以很方便地進(jìn)行管理,需要的朋友可以參考下
    2023-08-08
  • Java文件選擇對話框JFileChooser使用詳解

    Java文件選擇對話框JFileChooser使用詳解

    這篇文章主要介紹了Java文件選擇對話框JFileChooser使用詳解的相關(guān)資料,需要的朋友可以參考下
    2015-07-07
  • Java8內(nèi)存模型PermGen Metaspace實(shí)例解析

    Java8內(nèi)存模型PermGen Metaspace實(shí)例解析

    這篇文章主要介紹了Java8內(nèi)存模型PermGen Metaspace實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • springboot使用Validator校驗(yàn)方式

    springboot使用Validator校驗(yàn)方式

    這篇文章主要介紹了springboot使用Validator校驗(yàn)方式,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • MyBatis-Plus自動(dòng)填充功能失效導(dǎo)致的原因及解決

    MyBatis-Plus自動(dòng)填充功能失效導(dǎo)致的原因及解決

    這篇文章主要介紹了MyBatis-Plus自動(dòng)填充功能失效導(dǎo)致的原因及解決,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • spring aop execution表達(dá)式的用法

    spring aop execution表達(dá)式的用法

    這篇文章主要介紹了spring aop execution表達(dá)式的用法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • SpringBoot使用Async注解失效原因分析及解決(spring異步回調(diào))

    SpringBoot使用Async注解失效原因分析及解決(spring異步回調(diào))

    這篇文章主要介紹了SpringBoot使用Async注解失效原因分析及解決(spring異步回調(diào)),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 淺談java中的聲明常量為什么要用static修飾

    淺談java中的聲明常量為什么要用static修飾

    這篇文章主要介紹了淺談java中的聲明常量為什么要用static修飾的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09

最新評論

泰顺县| 确山县| 浏阳市| 洪泽县| 惠来县| 南江县| 邵阳县| 襄城县| 滨州市| 宜黄县| 昌图县| 奎屯市| 宜章县| 旌德县| 张家口市| 东阳市| 曲麻莱县| 澄城县| 鸡西市| 鹤庆县| 获嘉县| 丰镇市| 宁波市| 仁布县| 禹城市| 阳泉市| 威远县| 阿城市| 淅川县| 玛多县| 调兵山市| 禹城市| 南木林县| 辛集市| 安阳市| 汉源县| 敦煌市| 舒城县| 宜丰县| 腾冲县| 临城县|