<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試長(zhǎng)知識(shí)了!Java 關(guān)鍵字 transient 竟然還能這么用

          共 4200字,需瀏覽 9分鐘

           ·

          2021-03-20 00:30

          前言

          最近在看 HashMap 源代碼的時(shí)候,發(fā)現(xiàn)鏈表 table 數(shù)組采用了transient 關(guān)鍵字,筆者當(dāng)時(shí)感覺(jué)對(duì) transient 關(guān)鍵字即陌生但又有似曾相識(shí),所以花了一些時(shí)間簡(jiǎn)要的總結(jié)了下使用transient 關(guān)鍵字的一些基本常識(shí),希望對(duì)你們也有些幫助,讓我們一起進(jìn)步,一起牛逼吧。

          58a6998c78e34a12fca0223f6b7828f3.webp

          transient 關(guān)鍵字的定義

          說(shuō)起 transient 關(guān)鍵字,不得不提對(duì)象的 序列化 的,因?yàn)槲覀兂3P枰诰W(wǎng)絡(luò)上以對(duì)象(數(shù)據(jù))的二進(jìn)制方式傳輸數(shù)據(jù),這里涉及到發(fā)送方序列化對(duì)象,接收方反序列化對(duì)象的過(guò)程。

          那什么是序列化/反序列化?

          Java 中對(duì)象的序列化指的是將對(duì)象轉(zhuǎn)換成以字節(jié)序列的形式來(lái)表示,這些字節(jié)序列包含了對(duì)象的數(shù)據(jù)和信息,一個(gè)序列化后的對(duì)象可以被寫到數(shù)據(jù)庫(kù)或文件中,也可用于網(wǎng)絡(luò)傳輸。一般地,當(dāng)我們使用緩存 cache(內(nèi)存空間不夠有可能會(huì)本地存儲(chǔ)到硬盤)或遠(yuǎn)程調(diào)用 rpc(網(wǎng)絡(luò)傳輸)的時(shí)候,經(jīng)常需要讓實(shí)體類實(shí)現(xiàn) Serializable 接口,目的就是為了讓其可序列化。當(dāng)然,序列化后的最終目的是為了反序列化,恢復(fù)成原先的Java對(duì)象實(shí)例。所以序列化后的字節(jié)序列都是可以恢復(fù)成Java對(duì)象的,這個(gè)過(guò)程就是反序列化。

          在對(duì)象的序列化/反序列化過(guò)程中,我們經(jīng)常有這種需求,就是非必要字段不必進(jìn)行序列化。

          例如有一個(gè)對(duì)象有三個(gè)字段 field1field2、field3,發(fā)送方不想讓字段 field3 被序列化,因?yàn)檫@里面可能涉及到一些敏感信息不想被接收方知道,那有沒(méi)有辦法解決這個(gè)問(wèn)題呢?

          其實(shí)聰明的 Java 作者早就為我們量身定做了 transient 關(guān)鍵字!

          簡(jiǎn)單來(lái)說(shuō),被 transient 關(guān)鍵字修飾過(guò)的成員屬性不能被序列化,transient 關(guān)鍵字只能修飾變量,而不能修飾方法和類。

          transient 關(guān)鍵字的約定

          • 約定一、只能修飾變量而不能修飾方法和類。注意本地變量是不能被 transient 關(guān)鍵字修飾的。

          • 約定二、被 transient 關(guān)鍵字修飾過(guò)的屬性不能被序列化,也就是說(shuō)被 transient 修飾過(guò)的屬性,在對(duì)對(duì)象序列化后,是無(wú)法訪問(wèn)到該屬性的。

          • 約定三、靜態(tài)變量不管有無(wú)被 transient 修飾過(guò),不能被序列化。

          transient 關(guān)鍵字的使用場(chǎng)景

          首先,我們看個(gè)例子,有個(gè)產(chǎn)品對(duì)象 Product,包括價(jià)格數(shù)量總價(jià)三個(gè)字段,那么總價(jià)可以通過(guò) 價(jià)格 乘以 數(shù)量 推導(dǎo)出來(lái)。

          我們以查詢某個(gè)產(chǎn)品 API 接口為例,通過(guò)產(chǎn)品 ID,查詢返回一個(gè)產(chǎn)品對(duì)象。

          public?class?Product?{
          ?private?int?amounts;
          ?private?int?price;
          ?private?int?sum;
          }

          通過(guò) Gson 序列化后把 json 數(shù)據(jù)返回給前端,這時(shí)的 sum 字段是沒(méi)有經(jīng)過(guò) transient 修飾過(guò)的,所以能夠正常序列化。

          {"amounts":3,"price":2,"sum":6}

          假設(shè)我們的產(chǎn)品對(duì)象 Productsum 屬性加上 transient 關(guān)鍵字修飾:

          public?class??產(chǎn)品對(duì)象?Product?的?sum?{
          ?private?int?amounts;
          ?private?int?price;
          ?private?transient?int?sum;
          }

          然后我們?cè)囍跏蓟?Product ,并用 GsontoJson() 方法序列化輸出 json 格式的結(jié)果。

          public?static?void?main(String[]?args)?{
          ?Product?p?=?new?Product();
          ?p.setAmounts(3);
          ?p.setPrice(2);
          ?p.setSum(p.getAmounts()?*?p.getPrice());
          ?String?json?=?new?Gson().toJson(p);
          ?System.out.println(json);
          }

          這時(shí)控制臺(tái)是沒(méi)有打印出 sum 字段的。

          {"amounts":3,"price":2}

          我們看到,sum 屬性被 transient 修飾后,是不會(huì)被 Gson 序列化輸出的,這里就引出了使用 transient 關(guān)鍵字一個(gè)很重要的概念:對(duì)象屬性推導(dǎo)。

          對(duì)象屬性推導(dǎo)

          如果一個(gè)對(duì)象的屬性值可以通過(guò)其他屬性或者方法推理出來(lái)的,那么該屬性就沒(méi)必要被序列化了。

          借此我們以 Gson 來(lái)分析被 transient 修飾過(guò)的屬性不能被序列化過(guò)程。

          首先,調(diào)用 GsontoJson() 方法,傳入 Product 對(duì)象。

          new?Gson().toJson(product)

          根據(jù)傳入的產(chǎn)品對(duì)象,獲取 Product 對(duì)象的 class 類型:typeOfSrc,最后 找到對(duì)應(yīng)的對(duì)象解析適配器工廠。

          toJson(Object?src,?Type?typeOfSrc,?JsonWriter?writer)
          TypeAdapter<?>?adapter?=?getAdapter(TypeToken.get(typeOfSrc));
          for?(TypeAdapterFactory?factory?:?factories)?{
          ?//?得到ReflectiveTypeAdapterFactory
          ?TypeAdapter<T>?candidate?=?factory.create(this,?type);
          }

          通過(guò)適配器 ReflectiveTypeAdapterFactory 工廠的 create() 方法,我們找到 getBoundFields 方法。

          new?Adapter<T>(constructor,?getBoundFields(gson,?type,?raw));
          for?(Field?field?:?fields)?{
          ?boolean?serialize?=?excludeField(field,?true);
          ?boolean?deserialize?=?excludeField(field,?false);
          ?...
          }

          這個(gè)方法做了兩件事情:

          1. 剔除被 transient 關(guān)鍵字修飾過(guò)的屬性
          2. 篩選出可序列化的屬性

          通過(guò) excludeField() 方法,剔除被 transient 修飾過(guò)的屬性,其規(guī)則是通過(guò)位運(yùn)算 "&" 判斷 modifiers 屬性與對(duì)象屬性的 field.getModifiers() 的值是否一致,來(lái)證明該屬性是否被 transient 修飾過(guò),如果是為真,表示剔除該屬性,不進(jìn)行序列化。

          public?boolean?excludeField(Field?field,?boolean?serialize)?{
          ?//?通過(guò)?if?判斷?modifiers?屬性
          ?//?private?int?modifiers?=?Modifier.TRANSIENT?|?Modifier.STATIC;
          ?if?((modifiers?&?field.getModifiers())?!=?0)?{
          ???return?true;
          ?}?
          }

          另外根據(jù) modifiers 屬性定義 Modifier.TRANSIENT | Modifier.STATIC 兩種類型,一種是 tranient,另一種是 static 靜態(tài)類型。

          Modifier.STATIC:靜態(tài)類型

          由約定三、我們知道,靜態(tài)變量不會(huì)被序列化。

          代碼 debug 到此,我們已經(jīng)知道 Gson 是如何證明對(duì)象是否存在被 transient 修飾過(guò)屬性以及如何過(guò)濾掉的完整過(guò)程。

          被 transient 關(guān)鍵字修飾過(guò)得變量真的不能被序列化嘛?

          想要解決這個(gè)問(wèn)題,首先還要再重提一下對(duì)象的序列化方式,Java 序列化提供兩種方式。

          一種是實(shí)現(xiàn) Serializable 接口,另一種是實(shí)現(xiàn) Exteranlizable 接口。

          實(shí)現(xiàn) Exteranlizable 接口需要重寫 writeExternalreadExternal 方法,它的效率比 Serializable 高一些,并且可以決定哪些屬性需要序列化(即使是 transient 修飾的),但是對(duì)大量對(duì)象,或者重復(fù)對(duì)象,則效率低。

          從上面的這兩種序列化方式,我想你已經(jīng)看到了,使用 Exteranlizable 接口實(shí)現(xiàn)序列化時(shí),我們自己指定那些屬性是需要序列化的,即使是 transient 修飾的。下面就驗(yàn)證一下

          首先我們定義 User1 類:這個(gè)類是被 Externalizable 接口修飾的

          2de7c2008cf812290718d5e46cb47ed4.webp

          然后我們就可以測(cè)試了

          fcf0a93cfa4e55677e19379420497a17.webp

          上面,代碼分了兩個(gè)方法,一個(gè)是序列化,一個(gè)是反序列化。里面的代碼和一開(kāi)始給出的差不多,只不過(guò),User1 里面少了 age 這個(gè)屬性。

          然后看一下結(jié)果:

          fe168cca1f5236a7869f35bed99cf9f5.webp

          結(jié)果基本上驗(yàn)證了我們的猜想,也就是說(shuō),實(shí)現(xiàn)了 Externalizable 接口,哪一個(gè)屬性被序列化是我們手動(dòng)去指定的,即使是 transient 關(guān)鍵字修飾也不起作用。

          transient 關(guān)鍵字總結(jié)

          • 通過(guò)常用的 Gson 方式來(lái)驗(yàn)證 tranient 關(guān)鍵字不能序列化的使用場(chǎng)景。

          • 通過(guò)實(shí)現(xiàn)了 Externalizable 接口,如果手動(dòng)去指定屬性序列化的,即使是 transient 關(guān)鍵字修飾也不起作用。

          • 另外,還可以通過(guò) javaio 包下的 ObjectInputStreamObjectOutputStream 兩個(gè)對(duì)象輸入輸出流也可以驗(yàn)證,這里就不再做贅述,感興趣的朋友可以在網(wǎng)上找找例子。

          參考

          • https://www.cnblogs.com/chenpt/p/9415249.html
          • https://blog.csdn.net/u012723673/article/details/80699029
          • https://baijiahao.baidu.com/s?id=1636557218432721275&wfr=spider&for=pc

          Java編譯和反編譯那些事

          2021-03-15

          9645e3cc4c4ccb17cb461d1a279e16e7.webp

          怎么在Java中自定義注解?

          2021-03-15

          4e2d4a5e2a5db88381d92b7ef3a2c152.webp

          再來(lái)工具篇,10個(gè)解放雙手實(shí)用在線工具

          2021-03-14

          86540a206333f8778e3f22ae11b4d266.webp

          Java中的枚舉,一些不為人知的干貨都在這了

          2021-03-14

          9b96a232f665bbbbd4ece343f2203e67.webp


          瀏覽 75
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美少妇多毛 | 日韩黄色免费在线 | 色色色999 | 三级片在线观看视频网站 | 免费看一区二区三区四区 |