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

單例模式誰都會,破壞單例模式聽說過嗎?

開發(fā) 前端
序列化是破壞單例模式的一大利器。相比于克隆,實(shí)現(xiàn)序列化在實(shí)際操作中更加不可避免,有些類,它就是一定要序列化。

美團(tuán)到店的原題,手寫一個(gè)單例模式然后問如何破壞這個(gè)單例模式?

單例模式誰都會,懶漢、餓漢、雙重校驗(yàn)鎖、匿名內(nèi)部類、Enum,倒背如流了都,那如何破壞單例呢?

以最簡單的餓漢式寫法為例:

圖片

所謂單例,就是保證一個(gè)類只有一個(gè)實(shí)例對象,那想要破壞單例模式,無非就是創(chuàng)建多個(gè)實(shí)例對象罷了

那單例模式的構(gòu)造函數(shù)都是 private 的,我們沒法直接通過 new 來構(gòu)造對象,也就是說通過 new 這種方式去破壞單例的可能性是不存在的,得另尋他路。

除了 new,創(chuàng)建對象的方式還有 clone,反序列化,以及反射。

要調(diào)用 clone 方法,那么必須實(shí)現(xiàn) Cloneable 接口,但是單例模式是不能實(shí)現(xiàn)這個(gè)接口的,因此排除這種可能性。所以我們要討論的其實(shí)就是如何通過反序列化和反射對單例模式進(jìn)行破壞

反序列化破壞單例

序列化是破壞單例模式的一大利器。相比于克隆,實(shí)現(xiàn)序列化在實(shí)際操作中更加不可避免,有些類,它就是一定要序列化。

下面我們來做個(gè)測試,在上面的單例模式中實(shí)現(xiàn)序列化接口,然后先通過 getInstance 拿到一個(gè)對象,對這個(gè)對象進(jìn)行序列化再反序列化拿到一個(gè)對象,比較兩個(gè)對象是否是同一個(gè)對象:

圖片

結(jié)果為 false,說明通過對 Singleton 的序列化再反序列化得到的對象是一個(gè)新的對象,這就破壞了 Singleton 的單例性。

反序列化是怎么創(chuàng)建一個(gè)新對象的?

我們可以點(diǎn)擊 readObject 這個(gè)方法看看

圖片

核心是 readObject0,繼續(xù)點(diǎn)進(jìn)去:

圖片

根據(jù)傳入?yún)?shù)類型的不同,調(diào)用了不同的方法進(jìn)行反序列化,點(diǎn)進(jìn)針對 Object 的 readOrdinaryObject 方法看看:

圖片

真相大白了,反序列化底層其實(shí)就是使用了反射幫我們創(chuàng)建了一個(gè)新的對象。

如何阻止反序列化破壞單例?

現(xiàn)在我們在 Singleton 類中實(shí)現(xiàn)一個(gè) readResolve 方法,該方法直接返回了這個(gè)單例對象:

圖片

重新執(zhí)行下,發(fā)現(xiàn)結(jié)果為 true!也就是說 instance1 和 instance2 是同一個(gè)對象!

具體是什么原理,我們來看看剛才的 readOrdinaryObject 方法:

圖片

可以看到,在條件判斷中 desc.hasReadResolveMethod() 會判斷類是否有 readResolve() 方法,如果有的話會通過desc.invokeReadResolve(obj) 去反射調(diào)用該方法,由于我們的 readResolve 方法直接返回了 instance,不會創(chuàng)建一個(gè)新對象,這樣最終就保證了類實(shí)例對象的唯一性

圖片

圖片

所以,如果想要防止單例被反序列化破壞,就讓單例類實(shí)現(xiàn) readResolve() 方法

反射破壞單例

上面說到,反序列化底層其實(shí)就是通過反射來創(chuàng)建一個(gè)新對象的,我們直接來看反射是怎么破壞單例的:

圖片

執(zhí)行結(jié)果當(dāng)然是 false 了

如何阻止反射破壞單例?

反射是怎么創(chuàng)建新對象的?是通過類的構(gòu)造函數(shù)來的

所以如果我們想要阻止反射破壞單例,我們就需要修改類的構(gòu)造函數(shù):

圖片

重新執(zhí)行一遍我們的代碼,不出所料拋異常了,這樣便防止了單例被反射破壞:

圖片

不過這種構(gòu)造函數(shù)判斷的方法,只能阻止餓漢式的單例模式,沒法阻止懶漢式的單例!

我們可以來寫個(gè)懶漢模式測試下:

圖片

執(zhí)行下,發(fā)現(xiàn)結(jié)果仍然是拋異常:

圖片

什么情況?

別急,我們把 instance1 和 instance2 的構(gòu)建順序調(diào)換下:

圖片

再執(zhí)行,結(jié)果就是 false 了!!!

這是因?yàn)閼袧h式的對象只有調(diào)用的時(shí)候才被創(chuàng)建,我們先調(diào)用反射通過私有構(gòu)造函數(shù)來創(chuàng)建對象,這樣就越過了 instance != null 的判斷,不會拋異常,再通過 getInstance 創(chuàng)建對象,這兩個(gè)對象就不是同一個(gè)對象了,即單例模式被破壞了。

總結(jié)下,如果今后需要自己手動實(shí)現(xiàn)一個(gè)單例的話,可以選擇【構(gòu)造函數(shù)判斷】+【實(shí)現(xiàn) readResolve() 方法】的方式 來防止單例被破壞。

優(yōu)雅的單例實(shí)現(xiàn):Enum

那如果我不想在構(gòu)造函數(shù)里面做判斷,也不想寫 readResolve() 方法,我就想安安靜靜寫個(gè)單例,有沒有更簡單更優(yōu)雅的方法?

答案是有的!可以選擇使用 Enum 枚舉來實(shí)現(xiàn)單例模式

圖片

用反射來測試下,結(jié)果是直接拋異常了 java.lang.NoSuchMethodException

圖片

簡單來說就是因?yàn)?nbsp;singletonClass.getDeclaredConstructor() 沒有找到 Singleton 的無參構(gòu)造器,這是為啥?

主要是因?yàn)椋坏┮粋€(gè)類聲明為枚舉,實(shí)際上就是繼承了 java.lang.Enum,來看看 Enum 類源碼:

圖片

Enum 有兩個(gè)參數(shù) name 和 ordial 兩個(gè)屬性,我們自己寫的單例類繼承了父類 Enum 的構(gòu)造函數(shù),所以在上述的 getDecalredConstructor 才會找不到無參構(gòu)造器,那么是不是我們?nèi)フ{(diào)用父類的構(gòu)造器就可以了呢?我們來測試一下:

圖片

哦吼,運(yùn)行后直接拋 IllegalArgumentException 異常了

圖片

無法通過反射創(chuàng)建 Enum 對象!!!我們點(diǎn)進(jìn)去報(bào)錯(cuò)的 22 行即 constructor.newInstance 一探究竟:

圖片

簡單來說就是反射在通過 newInstance 創(chuàng)建對象時(shí),會檢查該類是否被 ENUM 修飾,如果是則直接拋出異常,反射失敗,所以枚舉是不怕反射暴力破解構(gòu)造器的

上面說枚舉是可以阻止反射通過暴力破解構(gòu)造函數(shù)來破壞單例的,再來看枚舉是如何阻止反序列化破壞單例的。

事實(shí)上,枚舉對象的序列化、反序列化有自己的一套機(jī)制:

  • 序列化的時(shí)候,僅僅是將枚舉對象的 name 屬性輸出到結(jié)果中
  • 反序列化的時(shí)候則是通過java.lang.Enum 的 valueOf? 方法 來根據(jù) name 查找枚舉對象

來看看 Enum.valueOf 方法:

圖片

繼續(xù)看 getEnumConstantsShared() 源碼:

圖片

水落石出啦,仍然是通過反射做的,先獲取枚舉類的 values() 方法,再得到所有枚舉對象。

簡單總結(jié)下:

  • 每個(gè)枚舉對象都有一個(gè)唯一的name 屬性。
  • 序列化只是將name 屬性序列化。
  • 在反序列化的時(shí)候,通過一個(gè)Map(key,value) 存儲 name 和與之對應(yīng)的對象之間的映射,然后通過 name 就可以直接獲得原來的 Enum 對象,而不會創(chuàng)建一個(gè)新對象!也就是說反序列化 Enum  類對象拿到的仍然是原來的對象,這樣就使得 Enum 類型實(shí)現(xiàn)了單例模式下的序列化安全。
責(zé)任編輯:武曉燕 來源: 飛天小牛肉
相關(guān)推薦

2023-12-05 08:20:05

單例模式Python

2021-03-02 08:50:31

設(shè)計(jì)單例模式

2021-02-01 10:01:58

設(shè)計(jì)模式 Java單例模式

2016-03-28 10:23:11

Android設(shè)計(jì)單例

2013-11-26 16:20:26

Android設(shè)計(jì)模式

2021-09-07 10:44:35

異步單例模式

2022-06-07 08:55:04

Golang單例模式語言

2021-02-07 23:58:10

單例模式對象

2011-03-16 10:13:31

java單例模式

2022-02-06 22:30:36

前端設(shè)計(jì)模式

2024-02-04 12:04:17

2024-03-06 13:19:19

工廠模式Python函數(shù)

2016-10-09 09:37:49

javascript單例模式

2015-09-06 11:07:52

C++設(shè)計(jì)模式單例模式

2023-11-21 21:39:38

單例模式音頻管理器

2011-06-28 15:18:45

Qt 單例模式

2010-02-05 17:00:06

C++單例模式

2022-05-23 07:35:15

單例模式懶漢模式靜態(tài)內(nèi)部類

2019-06-11 09:50:07

SparkBroadcast代碼

2021-07-27 07:31:16

單例模式關(guān)鍵字
點(diǎn)贊
收藏

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

主站蜘蛛池模板: a级毛片免费高清视频 | 人人做人人澡人人爽欧美 | 午夜一区二区三区 | 久久久久91| 国产精品视频免费 | 国产色婷婷精品综合在线手机播放 | 亚洲天堂中文字幕 | 亚洲精品日韩综合观看成人91 | 人妖av| 国产精品999| 美女视频h| 女人夜夜春 | 久久久www成人免费精品张筱雨 | 一区二区影院 | 久久99网站 | 成人国产精品久久久 | 欧美成人二区 | 成人精品国产一区二区4080 | 成人在线一区二区 | 久草欧美视频 | 国产精品成人一区 | 天天操天天拍 | 久久69精品久久久久久久电影好 | 日韩午夜在线观看 | 久久国内精品 | 久草视频观看 | 亚洲电影在线播放 | 国产精品美女久久久 | 久久久久久久久久毛片 | 一区二区在线免费观看 | 国产一区二区免费 | 久久神马 | 久久国产视频播放 | 国产精品久久久久久久久久久免费看 | 亚洲人成一区二区三区性色 | 男人阁久久| 中文字幕av亚洲精品一部二部 | 国产亚洲一区二区三区在线 | 国产av毛片 | 国产成人精品一区二区三区在线 | 欧美视频一区二区三区 |