成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

漫談序列化之使用、原理、問題

存儲(chǔ) 存儲(chǔ)軟件
天天跟我說給我介紹對(duì)象對(duì)象,對(duì)象在哪里?哪里有對(duì)象?你倒是把對(duì)象拿給我看看啊!

[[377100]]

前言

天天跟我說給我介紹對(duì)象對(duì)象,對(duì)象在哪里?哪里有對(duì)象?

你倒是把對(duì)象拿給我看看啊!

拿去拿去 :

  1.  "name""小麗"
  2.  "age""22"
  3.  "sex""女" 

我去~

序列化概念

說到對(duì)象,是一個(gè)比較寬泛的概念,簡(jiǎn)單的說,他就是類的一個(gè)實(shí)例,有狀態(tài)和行為,存活在內(nèi)存中,一旦JVM停止運(yùn)行,對(duì)象的狀態(tài)也會(huì)丟失。

那么如何將這個(gè)對(duì)象當(dāng)前狀態(tài)進(jìn)行一個(gè)記錄,使其可以進(jìn)行存儲(chǔ)和傳輸呢?這就要用到序列化了:

序列化 (Serialization)是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^程

比如一個(gè)User對(duì)象,名字為小麗,年齡22,性別為女。現(xiàn)在要把這個(gè)User對(duì)象保存下來,不然要是這個(gè)對(duì)象被別人改成了男可咋辦。

所以我們就可以把它當(dāng)前的狀態(tài)信息轉(zhuǎn)化成一種固定的格式,比如json格式:

  1.  "name""小麗"
  2.  "age""22"
  3.  "sex""女" 

所以上述的例子就是一個(gè)序列化過程,本身這個(gè)User對(duì)象存活在內(nèi)存中,是無法直接進(jìn)行數(shù)據(jù)持久化的,所以我們需要一些序列化的方式讓它可以進(jìn)行保存?zhèn)鬏敚?/p>

比如xml、JSON、Protobuf、Serializable、Parcelable,這些都是可以進(jìn)行序列化的方式。

所以關(guān)于序列化我們就有很多問題了:

  • 在java有Serializable的前提下,Android為什么設(shè)計(jì)出了Parcelable?
  • Parcelable一定比Serializable快嗎?
  • 為什么Java提供了Serializable的序列化方式,而不是直接使用json或者xml?
  • Serializable、Parcelable、Json等序列化方式我們?cè)撛趺催x擇?

帶著這些問題,我們?nèi)タ纯葱蛄谢氖澜纭?/p>

Serializable

先說說Java中自帶的序列化方式——Serializable。

Serializable是java.io包中定義的、用于實(shí)現(xiàn)Java類的序列化操作而提供的一個(gè)語義級(jí)別的接口

只要我們實(shí)現(xiàn)Serializable接口,那么這個(gè)類就可以被ObjectOutputStream轉(zhuǎn)換為字節(jié)流,也就是進(jìn)行了序列化。

使用

java:

  1. public class User implements Serializable { 
  2.     private static final long serialVersionUID=519067123721561165l; 
  3.      
  4.     private int id; 
  5.  
  6.     public int getId() { 
  7.         return id; 
  8.     } 
  9.  
  10.     public void setId(int id) { 
  11.         this.id = id; 
  12.     } 

kotlin:

  1. data class User(  
  2.     val id: Int 
  3. ) : Serializable 

這個(gè)變量如果不寫,系統(tǒng)也會(huì)自動(dòng)生成。它的作用在于標(biāo)示這個(gè)數(shù)據(jù)對(duì)象的一致性。

當(dāng)序列化的時(shí)候,系統(tǒng)會(huì)把當(dāng)前類的serialVersionUID寫入序列化的文件中,當(dāng)反序列化的時(shí)候會(huì)去檢測(cè)這個(gè)serialVersionUID,看他是否和當(dāng)前類的serialVersionUID一致,一樣則可以正常反序列化,如果不一樣就會(huì)報(bào)錯(cuò)了。

如果我們不寫的話,在我們修改類的某些屬性之后,serialVersionUID就會(huì)改變。

所以我們手動(dòng)指定serialVersionUID后,就能在修改類之后,讓系統(tǒng)認(rèn)識(shí)序列化的過程中標(biāo)示這是同一個(gè)類,從而保證最大限度來恢復(fù)數(shù)據(jù)。

原理

在Serializable的注釋中有提到,如果要想在序列化過程中做一些特殊的操作,可以實(shí)現(xiàn)這幾個(gè)特殊方法:

  • writeObject(),負(fù)責(zé)寫入對(duì)象的特定類,以便相應(yīng)的readObject方法可以恢復(fù)它
  • readObject(),負(fù)責(zé)從流中讀取并恢復(fù)類字段

所以這兩個(gè)方法其實(shí)就是Serializable實(shí)現(xiàn)的關(guān)鍵。首先看看寫入方法writeObject(偽代碼):

  1. private void writeObject(){ 
  2.     //獲取類的描述信息ObjectStreamClass(里面包含了類名稱、類字段、serialVersionUID等,用到大量反射) 
  3.      desc = ObjectStreamClass.lookup(cl, true); 
  4.      //寫入元數(shù)據(jù)TC_OBJECT,代表是一個(gè)新對(duì)象 
  5.      bout.writeByte(TC_OBJECT); 
  6.      //寫入描述信息(從父類寫到子類) 
  7.      writeClassDesc(descfalse); 
  8.      //寫入serialVersionUID,serialVersionUID為空的情況下,序列化機(jī)制就會(huì)調(diào)用一個(gè)函數(shù)根據(jù)類內(nèi)部的屬性等計(jì)算出一個(gè)hash值 
  9.      getSerialVersionUID(); 
  10.      //執(zhí)行JVM的序列化操作 
  11.      defaultWriteFields(); 
  12.  
  13.  
  14. private void defaultWriteFields(Object obj, ObjectStreamClass desc){ 
  15.     //寫入基本數(shù)據(jù)類型 
  16.     bout.write(primVals, 0, primDataSize, false); 
  17.  
  18.     //寫入引用數(shù)據(jù)類型(又重新調(diào)用了writeObject方法) 
  19.     Object[] objVals = new Object[desc.getNumObjFields()]; 
  20.     for (int i = 0; i < objVals.length; i++) { 
  21.         writeObject(objVals[i],fields[numPrimFields + i].isUnshared()); 
  22.     } 

寫入數(shù)據(jù)的流程基本就這些,可以看到Serializable序列化的過程,其實(shí)就是一個(gè)寫入流的過程。然后就可以根據(jù)情況將二進(jìn)制流保持為文件,或者包裝成ByteArrayOutStream寫入到內(nèi)存中進(jìn)行傳輸。

所以Serializable使用的范圍比較廣,可以作為文件保存下來,也可以作為二進(jìn)制流對(duì)象用于內(nèi)存中的傳輸。但是由于用到反射、IO,而且大量的臨時(shí)變量會(huì)引起頻繁的GC,所以效率不算高。

所以,為了提高在Android中對(duì)象傳輸?shù)男誓兀珹ndroid就采用了新的序列化方式——Parcelable。

Parcelable

Parcelable是Android為我們提供的序列化的接口,是為了解決Serializable在序列化的過程中消耗資源嚴(yán)重,而Android本身的內(nèi)存比較緊缺的問題,但是用法較為繁瑣,主要用于內(nèi)存中數(shù)據(jù)的傳輸。

使用

java:

  1. public class User implements Parcelable { 
  2.     private int id; 
  3.  
  4.     protected User(Parcel in) { 
  5.         id = in.readInt(); 
  6.     } 
  7.  
  8.     @Override 
  9.     public void writeToParcel(Parcel dest, int flags) { 
  10.         dest.writeInt(id); 
  11.     } 
  12.  
  13.     @Override 
  14.     public int describeContents() { 
  15.         return 0; 
  16.     } 
  17.  
  18.     public static final Creator<User> CREATOR = new Creator<User>() { 
  19.         @Override 
  20.         public User createFromParcel(Parcel in) { 
  21.             return new User(in); 
  22.         } 
  23.  
  24.         @Override 
  25.         public User[] newArray(int size) { 
  26.             return new User[size]; 
  27.         } 
  28.     }; 
  29.  
  30.     public int getId() { 
  31.         return id; 
  32.     } 
  33.  
  34.     public void setId(int id) { 
  35.         this.id = id; 
  36.     } 
  1. androidExtensions { 
  2.     experimental = true 
  3.  
  4. @Parcelize 
  5. data class User(val name: String) : Parcelable 

原理

先說說Parcelable寫法中這幾個(gè)方法參數(shù)的意思:

  • createFromParcel,User(Parcel in) ,代表從序列化的對(duì)象中創(chuàng)建原始對(duì)象
  • newArray,代表創(chuàng)建指定長(zhǎng)度的原始對(duì)象數(shù)組
  • writeToParcel,代表將當(dāng)前對(duì)象寫入到序列化結(jié)構(gòu)中。
  • describeContents,代表返回當(dāng)前對(duì)象的內(nèi)容描述。如果還有文件描述符,返回1,否則返回0。

好了,在了解Parcelable原理之前,我們先要了解下Parcel。

Parcel是一個(gè)容器,它主要用于存儲(chǔ)序列化數(shù)據(jù),然后可以通過Binder在進(jìn)程間傳遞這些數(shù)據(jù)

所以Parcel就是可以進(jìn)行IPC通信的容器,同樣底層也是用到了Binder。(Binder在Android中真是無處不在啊)

  1. //寫入數(shù)據(jù) 
  2. Parcel parcle = Parcel.Obtain(); 
  3. parcel.writeString(String val); 
  4.  
  5. //讀取數(shù)據(jù) 
  6. parcel.setDataPosition(i); 
  7. parcel.readString(); 

再往底層就是Binder的原理了,也就是將數(shù)據(jù)寫到內(nèi)核的共享內(nèi)存中,然后其他進(jìn)程可以從共享內(nèi)存中進(jìn)行讀取。

而Parcelable的實(shí)現(xiàn)就是基于這個(gè)Parcel容器,還記得剛才的幾個(gè)方法嗎:

  • writeToParcel,寫入數(shù)據(jù)到Parcel容器。
  • new User(in),從Parcel容器讀取數(shù)據(jù)。

Parcelable的原理就是如此啦。

思考問題

介紹完了兩種序列化方式,我們?cè)賮砜纯次恼麻_頭的這些問題。

在java有Serializable的前提下,Android為什么設(shè)計(jì)出了Parcelable?

java中的序列化方式Serializable效率比較低,主要有以下原因:

  • Serializable在序列化過程中會(huì)創(chuàng)建大量的臨時(shí)變量,這樣就會(huì)造成大量的GC。
  • Serializable使用了大量反射,而反射操作耗時(shí)。
  • Serializable使用了大量的IO操作,也影響了耗時(shí)。

所以Android就像重新設(shè)計(jì)了IPC方式Binder一樣,重新設(shè)計(jì)了一種序列化方式,結(jié)合Binder的方式,對(duì)上述三點(diǎn)進(jìn)行了優(yōu)化,一定程度上提高了序列化和反序列化的效率。

Serializable、Parcelable、Json等序列化方式我們?cè)撛趺催x擇?

先說說序列化的用處,主要用在三個(gè)方面:

1、內(nèi)存數(shù)據(jù)傳輸

內(nèi)存?zhèn)鬏敺矫妫饕肞arcelable。一是因?yàn)镻arcelable在內(nèi)存?zhèn)鬏數(shù)男时萐erializable高。二是因?yàn)樵贏ndroid中很多傳輸數(shù)據(jù)的方法中,自帶了對(duì)于Serializable、Parcelable類型的傳輸方法。比如:

  • Bundle.putParcelable,
  • Intent putExtra(String name, Parcelable value)

等等吧,基本上對(duì)象傳輸?shù)姆椒ǘ贾С至耍赃@也是Parcelable的優(yōu)勢(shì)。

2、 數(shù)據(jù)持久化(本地存儲(chǔ))

如果只針對(duì)Serializable和Parcelable兩種序列化方式,需要選擇Serializable。

首先,Serializable本身就是存儲(chǔ)到二進(jìn)制文件,所以用于持久化比較方便。而Parcelable序列化是在內(nèi)存中操作,如果進(jìn)程關(guān)閉或者重啟的時(shí)候,內(nèi)存中的數(shù)據(jù)就會(huì)消失,那么Parcelable序列化用來持久化就有可能會(huì)失敗,也就是數(shù)據(jù)不會(huì)連續(xù)完整。而且Parcelable還有一個(gè)問題是兼容性,每個(gè)Android版本可能內(nèi)部實(shí)現(xiàn)都不一樣,知識(shí)用于內(nèi)存中也就是傳遞數(shù)據(jù)的話是不影響的,但是如果持久化可能就會(huì)有問題了,低版本的數(shù)據(jù)拿到高版本可能會(huì)出現(xiàn)兼容性問題。

但是實(shí)際情況,對(duì)于Android中的對(duì)象本地化存儲(chǔ),一般是以數(shù)據(jù)庫(kù)、SP的方式進(jìn)行保存。

3、 網(wǎng)絡(luò)傳輸

而對(duì)于網(wǎng)絡(luò)傳輸?shù)那闆r,一般就是使用JSON了。主要有以下幾點(diǎn)原因:

  • 輕量級(jí),沒有多余的數(shù)據(jù)。
  • 與語言無關(guān),所以能兼容所有平臺(tái)語言。
  • 易讀性,易解析。

Parcelable一定比Serializable快嗎?

正常情況下,對(duì)象在內(nèi)存中進(jìn)行傳輸確實(shí)是Parcelable比較快,但是Serializable是有緩存的概念的,有人做了一個(gè)比較有趣的實(shí)驗(yàn):

當(dāng)序列化一個(gè)超級(jí)大的對(duì)象圖表(表示通過一個(gè)對(duì)象,擁有通過某路徑能訪問到其他很多的對(duì)象),并且每個(gè)對(duì)象有10個(gè)以上屬性時(shí),并且Serializable實(shí)現(xiàn)了writeObject()以及readObject(),在平均每臺(tái)安卓設(shè)備上,Serializable序列化速度大于Parcelable 3.6倍,反序列化速度大于1.6倍.

具體原因就是因?yàn)镾erilazable的實(shí)現(xiàn)方式中,是有緩存的概念的,當(dāng)一個(gè)對(duì)象被解析過后,將會(huì)緩存在HandleTable中,當(dāng)下一次解析到同一種類型的對(duì)象后,便可以向二進(jìn)制流中,寫入對(duì)應(yīng)的緩存索引即可。但是對(duì)于Parcel來說,沒有這種概念,每一次的序列化都是獨(dú)立的,每一個(gè)對(duì)象,都當(dāng)作一種新的對(duì)象以及新的類型的方式來處理。

具體過程可以看看這篇:https://juejin.cn/post/6854573218334769166

為什么Java提供了Serializable的序列化方式,而不是直接使用json或者xml?

我覺得是歷史遺留問題。

有的人可能會(huì)想到各種理由,比如可以標(biāo)記哪些類可以被序列化。又或者可以通過UID來標(biāo)示反序列化為同一個(gè)對(duì)象。等等。

但是我覺得最大的問題還是歷史遺留問題,在以前,json還沒有成為大家認(rèn)同的數(shù)據(jù)結(jié)構(gòu),所以Java就設(shè)計(jì)出了Serializable的序列化方式來解決對(duì)象持久化和對(duì)象傳輸?shù)膯栴}。然后Java中各種API就會(huì)依賴于這種序列化方式,這么些年過去了,Java體系的龐大也造成難以改變這個(gè)問題,牽一發(fā)而動(dòng)全身。

為什么我這么說呢?

主要有兩點(diǎn)依據(jù):

  • 曾經(jīng)Oracle Java平臺(tái)組的架構(gòu)師說過,刪除Java的序列化機(jī)制并且提供給用戶可以選擇的序列化方式(比如json)是他們計(jì)劃中的一部分,因?yàn)镴ava序列化也造成了很多Java漏洞。具體可以參見文章:https://www.infoworld.com/article/3275924/oracle-plans-to-dump-risky-java-serialization.html
  • 因?yàn)樵赟erializable類的介紹注釋中,明確說到推薦大家選擇JSON 和 GSON庫(kù),因?yàn)樗?jiǎn)潔、易讀、高效。
  1. <h3>Recommended Alternatives</h3> 
  2.  <strong>JSON</strong> is concise, human-readable and efficient. Android 
  3.  includes both a {@link android.util.JsonReader streaming API} and a {@link 
  4.  org.json.JSONObject tree API} to read and write JSON. Use a binding library 
  5.  like <a href="http://code.google.com/p/google-gson/">GSON</a> to read and 
  6.  write Java objects directly. 

Android體系架構(gòu)

連載文章、腦圖、面試專題:

https://github.com/JiMuzz/Android-Architecture

參考

https://developer.android.google.cn/reference/android/os/Parcel?hl=en https://blog.csdn.net/lwj_zeal/article/details/90743500 https://juejin.cn/post/6854573218334769166#heading http://blog.sina.com.cn/s/blog_6e07f1eb0100rsax.html https://www.zhihu.com/question/283510695 https://www.infoworld.com/article/3275924/oracle-plans-to-dump-risky-java-serialization.html

本文轉(zhuǎn)載自微信公眾號(hào)「碼上積木」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系碼上積木公眾號(hào)。

 

責(zé)任編輯:武曉燕 來源: 碼上積木
相關(guān)推薦

2024-10-24 11:08:00

C#AOT泛型

2019-11-20 10:07:23

web安全PHP序列化反序列化

2022-08-06 08:41:18

序列化反序列化Hessian

2009-08-28 10:18:48

Java序列化

2009-08-24 17:14:08

C#序列化

2011-06-01 15:05:02

序列化反序列化

2011-04-02 13:47:01

2011-06-01 15:18:43

Serializabl

2010-01-08 13:25:07

ibmdwXML

2009-03-10 13:38:01

Java序列化字節(jié)流

2023-06-29 08:41:02

2018-03-19 10:20:23

Java序列化反序列化

2009-08-06 11:16:25

C#序列化和反序列化

2011-05-18 15:20:13

XML

2023-12-13 13:49:52

Python序列化模塊

2023-11-13 23:06:52

Android序列化

2011-06-01 14:50:48

2009-06-14 22:01:27

Java對(duì)象序列化反序列化

2009-08-25 14:24:36

C#序列化和反序列化

2013-03-11 13:55:03

JavaJSON
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 91久久电影 | 玖玖国产精品视频 | 久草网站| 亚洲精品电影在线观看 | 亚洲永久免费观看 | 狠狠亚洲 | 国产一区不卡 | 夜夜爽99久久国产综合精品女不卡 | 97精品超碰一区二区三区 | 欧美日韩国产综合在线 | 久久香蕉精品视频 | 精品无码三级在线观看视频 | av中文字幕在线 | 国产一在线 | 日韩在线播放第一页 | 日本一区二区三区四区 | 国产精品久久久久久久久免费樱桃 | 91在线导航 | 激情网站在线 | 国产成人一区二区三区久久久 | 亚洲人成在线观看 | 天天干免费视频 | 欧美精品福利视频 | 日韩成人专区 | 日韩欧美中文字幕在线观看 | 欧美电影免费观看 | 欧美精品一区二区三区在线播放 | 九色视频网 | 亚洲综合视频 | 韩国av网站在线观看 | 成人在线影视 | 狠狠夜夜 | 亚洲国产精品一区二区久久 | 一区二区成人在线 | 午夜精品久久 | 精品欧美一区免费观看α√ | 天天干天天干 | 激情小说综合网 | 亚洲欧洲国产视频 | 99精品网| 精品视频一二区 |