RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

static屬性為什么不會被序列化

Android編程精選 ? 來源:椰子Tyshawn ? 作者:椰子Tyshawn ? 2022-07-15 11:03 ? 次閱讀

做服務(wù)化,需要把所有model包里的類都實(shí)現(xiàn)Serializable接口, 同時(shí)還要顯示指定serialVersionUID的值。聽到這個(gè)需求,我腦海里就突然出現(xiàn)了好幾個(gè)問題,比如說:

序列化和反序列化是什么?

實(shí)現(xiàn)序列化和反序列化為什么要實(shí)現(xiàn)Serializable接口?

實(shí)現(xiàn)Serializable接口就算了,為什么還要顯示指定serialVersionUID的值?

我要為serialVersionUID指定個(gè)什么值?

下面我們來一一解答這幾個(gè)問題。

序列化和反序列化

序列化:把對象轉(zhuǎn)換為字節(jié)序列的過程稱為對象的序列化。

反序列化:把字節(jié)序列恢復(fù)為對象的過程稱為對象的反序列化。

什么時(shí)候需要用到序列化和反序列化呢?

當(dāng)我們只在本地JVM里運(yùn)行下Java實(shí)例,這個(gè)時(shí)候是不需要什么序列化和反序列化的, 但當(dāng)我們需要將內(nèi)存中的對象持久化到磁盤, 數(shù)據(jù)庫中時(shí),當(dāng)我們需要與瀏覽器進(jìn)行交互時(shí), 當(dāng)我們需要實(shí)現(xiàn)RPC時(shí),這個(gè)時(shí)候就需要序列化和反序列化了。

前兩個(gè)需要用到序列化和反序列化的場景,是不是讓我們有一個(gè)很大的疑問? 我們在與瀏覽器交互時(shí),還有將內(nèi)存中的對象持久化到數(shù)據(jù)庫中時(shí),好像都沒有去進(jìn)行序列化和反序列化,因?yàn)槲覀兌紱]有實(shí)現(xiàn)Serializable接口, 但一直正常運(yùn)行。

下面先給出結(jié)論:

只要我們對內(nèi)存中的對象進(jìn)行持久化或網(wǎng)絡(luò)傳輸, 這個(gè)時(shí)候都需要序列化和反序列化.

理由:

服務(wù)器與瀏覽器交互時(shí)真的沒有用到Serializable接口嗎? JSON格式實(shí)際上就是將一個(gè)對象轉(zhuǎn)化為字符串, 所以服務(wù)器與瀏覽器交互時(shí)的數(shù)據(jù)格式其實(shí)是字符串, 我們來看來String類型的源碼:

publicfinalclassString
implementsjava.io.Serializable,Comparable,CharSequence{
/**Thevalueisusedforcharacterstorage.*/
privatefinalcharvalue[];

/**Cachethehashcodeforthestring*/
privateinthash;//Defaultto0

/**useserialVersionUIDfromJDK1.0.2forinteroperability*/
privatestaticfinallongserialVersionUID=-6849794470754667710L;

......
}

String類型實(shí)現(xiàn)了Serializable接口,并顯示指定serialVersionUID的值。

然后我們再來看對象持久化到數(shù)據(jù)庫中時(shí)的情況,Mybatis數(shù)據(jù)庫映射文件里的insert代碼:


INSERTINTOt_user(name,age)VALUES(#{name},#{age})

實(shí)際上我們并不是將整個(gè)對象持久化到數(shù)據(jù)庫中, 而是將對象中的屬性持久化到數(shù)據(jù)庫中, 而這些屬性都是實(shí)現(xiàn)了Serializable接口的基本屬性。

實(shí)現(xiàn)序列化和反序列化為什么要實(shí)現(xiàn)Serializable接口?

在Java中實(shí)現(xiàn)了Serializable接口后,JVM會在底層幫我們實(shí)現(xiàn)序列化和反序列化, 如果我們不實(shí)現(xiàn)Serializable接口, 那自己去寫一套序列化和反序列化代碼也行, 至于具體怎么寫, Google一下你就知道了。

實(shí)現(xiàn)Serializable接口就算了, 為什么還要顯示指定serialVersionUID的值?

如果不顯示指定serialVersionUID,JVM在序列化時(shí)會根據(jù)屬性自動生成一個(gè)serialVersionUID, 然后與屬性一起序列化,再進(jìn)行持久化或網(wǎng)絡(luò)傳輸. 在反序列化時(shí),JVM會再根據(jù)屬性自動生成一個(gè)新版serialVersionUID,然后將這個(gè)新版serialVersionUID與序列化時(shí)生成的舊版serialVersionUID進(jìn)行比較, 如果相同則反序列化成功, 否則報(bào)錯(cuò).

如果顯示指定了serialVersionUID, JVM在序列化和反序列化時(shí)仍然都會生成一個(gè)serialVersionUID, 但值為我們顯示指定的值, 這樣在反序列化時(shí)新舊版本的serialVersionUID就一致了。

在實(shí)際開發(fā)中, 不顯示指定serialVersionUID的情況會導(dǎo)致什么問題? 如果我們的類寫完后不再修改, 那當(dāng)然不會有問題, 但這在實(shí)際開發(fā)中是不可能的,我們的類會不斷迭代, 一旦類被修改了,那舊對象反序列化就會報(bào)錯(cuò). 所以在實(shí)際開發(fā)中, 我們都會顯示指定一個(gè)serialVersionUID, 值是多少無所謂, 只要不變就行。

寫個(gè)實(shí)例測試下:

User類

不顯示指定serialVersionUID.

publicclassUserimplementsSerializable{
privateStringname;
privateIntegerage;
publicStringgetName(){
returnname;
}

publicvoidsetName(Stringname){
this.name=name;
}

publicIntegergetAge(){
returnage;
}

publicvoidsetAge(Integerage){
this.age=age;
}

@Override
publicStringtoString(){
return"User{"+
"name='"+name+'''+
",age="+age+
'}';
}
}

測試類

先進(jìn)行序列化, 再進(jìn)行反序列化.

publicclassSerializableTest{
privatestaticvoidserialize(Useruser)throwsException{
ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream(newFile("D:\111.txt")));
oos.writeObject(user);
oos.close();
}

privatestaticUserdeserialize()throwsException{
ObjectInputStreamois=newObjectInputStream(newFileInputStream(newFile("D:\111.txt")));
return(User)ois.readObject();
}

publicstaticvoidmain(String[]args)throwsException{
Useruser=newUser();
user.setName("tyshawn");
user.setAge(18);
System.out.println("序列化前的結(jié)果:"+user);

serialize(user);
UserdUser=deserialize();
System.out.println("反序列化后的結(jié)果:"+dUser);
}
}

結(jié)果

先注釋掉反序列化代碼,執(zhí)行序列化代碼,然后User類新增一個(gè)屬性sex。

publicclassUserimplementsSerializable{
privateStringname;
privateIntegerage;
privateStringsex;
publicStringgetName(){
returnname;
}

publicvoidsetName(Stringname){
this.name=name;
}

publicIntegergetAge(){
returnage;
}

publicvoidsetAge(Integerage){
this.age=age;
}

publicStringgetSex(){
returnsex;
}

publicvoidsetSex(Stringsex){
this.sex=sex;
}

@Override
publicStringtoString(){
return"User{"+
"name='"+name+'''+
",age="+age+
",sex='"+sex+'''+
'}';
}
}

再注釋掉序列化代碼執(zhí)行反序列化代碼,最后結(jié)果如下:

序列化前的結(jié)果: User{name='tyshawn', age=18}Exception in thread "main" java.io.InvalidClassException: org.tyshawn.SerializeAndDeserialize.User; local class incompatible: stream classdesc serialVersionUID = 1035612825366363028, local class serialVersionUID = -1830850955895931978報(bào)錯(cuò)結(jié)果為序列化與反序列化產(chǎn)生的serialVersionUID不一致。

接下來我們在上面User類的基礎(chǔ)上顯示指定一個(gè)serialVersionUID。

privatestaticfinallongserialVersionUID=1L;

再執(zhí)行上述步驟, 測試結(jié)果如下:

序列化前的結(jié)果: User{name='tyshawn', age=18}反序列化后的結(jié)果: User{name='tyshawn', age=18, sex='null'}

顯示指定serialVersionUID后就解決了序列化與反序列化產(chǎn)生的serialVersionUID不一致的問題。

Java序列化的其他特性

先說結(jié)論, 被transient關(guān)鍵字修飾的屬性不會被序列化, static屬性也不會被序列化。

我們來測試下這個(gè)結(jié)論:

User類

publicclassUserimplementsSerializable{
privatestaticfinallongserialVersionUID=1L;
privateStringname;
privateIntegerage;
privatetransientStringsex;
privatestaticStringsignature="你眼中的世界就是你自己的樣子";
publicStringgetName(){
returnname;
}

publicvoidsetName(Stringname){
this.name=name;
}

publicIntegergetAge(){
returnage;
}

publicvoidsetAge(Integerage){
this.age=age;
}

publicStringgetSex(){
returnsex;
}

publicvoidsetSex(Stringsex){
this.sex=sex;
}

publicstaticStringgetSignature(){
returnsignature;
}

publicstaticvoidsetSignature(Stringsignature){
User.signature=signature;
}

@Override
publicStringtoString(){
return"User{"+
"name='"+name+'''+
",age="+age+
",sex='"+sex+'''+
",signature='"+signature+'''+
'}';
}
}

測試類

publicclassSerializableTest{
privatestaticvoidserialize(Useruser)throwsException{
ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream(newFile("D:\111.txt")));
oos.writeObject(user);
oos.close();
}

privatestaticUserdeserialize()throwsException{
ObjectInputStreamois=newObjectInputStream(newFileInputStream(newFile("D:\111.txt")));
return(User)ois.readObject();
}
publicstaticvoidmain(String[]args)throwsException{
Useruser=newUser();
user.setName("tyshawn");
user.setAge(18);
user.setSex("man");
System.out.println("序列化前的結(jié)果:"+user);
serialize(user);
UserdUser=deserialize();
System.out.println("反序列化后的結(jié)果:"+dUser);
}
}

結(jié)果

先注釋掉反序列化代碼, 執(zhí)行序列化代碼, 然后修改User類signature = “我的眼里只有你”, 再注釋掉序列化代碼執(zhí)行反序列化代碼, 最后結(jié)果如下:

序列化前的結(jié)果: User{name='tyshawn', age=18, sex='man', signature='你眼中的世界就是你自己的樣子'}反序列化后的結(jié)果: User{name='tyshawn', age=18, sex='null', signature='我的眼里只有你'}

static屬性為什么不會被序列化?

因?yàn)樾蛄谢轻槍ο蠖缘?,而static屬性優(yōu)先于對象存在,隨著類的加載而加載, 所以不會被序列化。

看到這個(gè)結(jié)論,是不是有人會問,serialVersionUID也被static修飾,為什么serialVersionUID會被序列化? 其實(shí)serialVersionUID屬性并沒有被序列化,JVM在序列化對象時(shí)會自動生成一個(gè)serialVersionUID,然后將我們顯示指定的serialVersionUID屬性值賦給自動生成的serialVersionUID。

原文標(biāo)題:Java 序列化和反序列化,為什么要實(shí)現(xiàn) Serializable 接口?

文章出處:【微信公眾號:Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

審核編輯:彭靜

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8575

    瀏覽量

    151015
  • 網(wǎng)絡(luò)傳輸
    +關(guān)注

    關(guān)注

    0

    文章

    138

    瀏覽量

    17394
  • static
    +關(guān)注

    關(guān)注

    0

    文章

    33

    瀏覽量

    10366

原文標(biāo)題:Java 序列化和反序列化,為什么要實(shí)現(xiàn) Serializable 接口?

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    如何使用Serde進(jìn)行序列化和反序列化

    Serde 是一個(gè)用于序列化和反序列化 Rust 數(shù)據(jù)結(jié)構(gòu)的庫。它支持 JSON、BSON、YAML 等多種格式,并且可以自定義序列化和反序列化方式。Serde 的特點(diǎn)是代碼簡潔、易于
    的頭像 發(fā)表于 09-30 17:09 ?1281次閱讀

    序列化哈希表到文件

    BinarySerialize {//把哈希表對象序列化到文件public static void Serialize(String strFile, Hashtable ht){using
    發(fā)表于 06-18 18:28

    Java序列化的機(jī)制和原理

    本文講解了Java序列化的機(jī)制和原理。從文中你可以了解如何序列化一個(gè)對象,什么時(shí)候需要序列化以及Java序列化的算法。AD:WOT2014課程推薦:實(shí)戰(zhàn)MSA:用開源軟件搭建微服務(wù)系統(tǒng)
    發(fā)表于 07-10 07:27

    c語言序列化和反序列化有何區(qū)別

    這里寫自定義目錄標(biāo)題c語言序列化和反序列化tplut.htplut.c測試代碼參考c語言序列化和反序列化網(wǎng)絡(luò)調(diào)用,數(shù)據(jù)傳輸都需要把數(shù)據(jù)序列化
    發(fā)表于 07-14 07:32

    關(guān)于c語言序列化和反序列化的知識點(diǎn)看完你就懂了

    關(guān)于c語言序列和反序列化的知識點(diǎn)你就懂了
    發(fā)表于 10-15 08:47

    SpringMVC JSON框架的自定義序列化與反序列化

    ,那么jackson的@JsonSerialize就不會有觸發(fā)入口了,我們來看看fastjson的處理方式。自定義序列化相應(yīng)的,使用fastjson會有相應(yīng)的配置類,示例如下:/** * 統(tǒng)一輸出
    發(fā)表于 10-10 16:02

    理解PHP反序列化漏洞

    理解PHP反序列化漏洞
    發(fā)表于 09-07 11:03 ?7次下載
    理解PHP反<b class='flag-5'>序列化</b>漏洞

    java序列化和反序列化范例和JDK類庫中的序列化API

    一、序列化和反序列化的概念 把對象轉(zhuǎn)換為字節(jié)序列的過程稱為對象的序列化。 把字節(jié)序列恢復(fù)為對象的過程稱為對象的反
    發(fā)表于 09-27 10:13 ?6次下載

    C#實(shí)現(xiàn)對象序列化的三種方式是什么

    很多小伙伴一提到序列化,都會想到二進(jìn)制序列化,但其實(shí)序列化并不僅僅只是二進(jìn)制序列化,我們常說的對象序列化有三種方式,分別是二進(jìn)制
    的頭像 發(fā)表于 02-22 16:11 ?1199次閱讀
    C#實(shí)現(xiàn)對象<b class='flag-5'>序列化</b>的三種方式是什么

    python序列化對象

    序列化對象:將對象轉(zhuǎn)換為可以存儲或傳輸?shù)男问健? (1) 用于存儲:將對象的字節(jié)序列存儲到文件中,程序退出后不會消失,便于后續(xù)使用。
    的頭像 發(fā)表于 03-10 09:57 ?2338次閱讀

    什么是序列化 為什么要序列化

    什么是序列化? “序列化”(Serialization )的意思是將一個(gè)對象轉(zhuǎn)化為字節(jié)流。 這里說的對象可以理解為“面向?qū)ο蟆崩锏哪莻€(gè)對象,具體的就是存儲在內(nèi)存中的對象數(shù)據(jù)。 與之相反的過程是“反序列化
    的頭像 發(fā)表于 09-14 17:22 ?2540次閱讀
    什么是<b class='flag-5'>序列化</b> 為什么要<b class='flag-5'>序列化</b>

    ROS中的序列化實(shí)現(xiàn)

    理解了序列化,再回到ROS。我們發(fā)現(xiàn),ROS沒有采用第三方的序列化工具,而是選擇自己實(shí)現(xiàn),代碼在roscpp_core項(xiàng)目下的roscpp_serialization中,見下圖。這個(gè)功能涉及的代碼量
    的頭像 發(fā)表于 09-14 17:26 ?906次閱讀

    如何用C語言進(jìn)行json的序列化和反序列化

    json是目前最為流行的文本數(shù)據(jù)傳輸格式,特別是在網(wǎng)絡(luò)通信上廣泛應(yīng)用,隨著物聯(lián)網(wǎng)的興起,在嵌入式設(shè)備上,也需要開始使用json進(jìn)行數(shù)據(jù)傳輸,那么,如何快速簡潔地用C語言進(jìn)行json的序列化和反序列化
    的頭像 發(fā)表于 10-07 11:05 ?1465次閱讀

    Java序列化怎么使用

    轉(zhuǎn)換方式就叫做序列化。將文件或者網(wǎng)絡(luò)傳輸中得到的 byte[] 數(shù)組轉(zhuǎn)換為 java 對象就叫做反序列化。 怎么使用 如果一個(gè) Java 對象要能被序列化,必須實(shí)現(xiàn)一個(gè)特殊
    的頭像 發(fā)表于 10-10 14:19 ?443次閱讀

    什么時(shí)候需要Boost序列化

    程序開發(fā)中,序列化是經(jīng)常需要用到的。像一些相對高級語言,比如JAVA, C#都已經(jīng)很好的支持了序列化,那么C++呢?當(dāng)然一個(gè)比較好的選擇就是用Boost,這個(gè)號稱C++準(zhǔn)標(biāo)準(zhǔn)庫的東西。 什么時(shí)候需要
    的頭像 發(fā)表于 11-10 10:14 ?419次閱讀
    RM新时代网站-首页