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

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

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

美團到店的原題,手寫一個單例模式然后問如何破壞這個單例模式?

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

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

圖片

所謂單例,就是保證一個類只有一個實例對象,那想要破壞單例模式,無非就是創建多個實例對象罷了

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

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

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

反序列化破壞單例

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

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

圖片

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

反序列化是怎么創建一個新對象的?

我們可以點擊 readObject 這個方法看看

圖片

核心是 readObject0,繼續點進去:

圖片

根據傳入參數類型的不同,調用了不同的方法進行反序列化,點進針對 Object 的 readOrdinaryObject 方法看看:

圖片

真相大白了,反序列化底層其實就是使用了反射幫我們創建了一個新的對象。

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

現在我們在 Singleton 類中實現一個 readResolve 方法,該方法直接返回了這個單例對象:

圖片

重新執行下,發現結果為 true!也就是說 instance1 和 instance2 是同一個對象!

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

圖片

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

圖片

圖片

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

反射破壞單例

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

圖片

執行結果當然是 false 了

如何阻止反射破壞單例?

反射是怎么創建新對象的?是通過類的構造函數來的

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

圖片

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

圖片

不過這種構造函數判斷的方法,只能阻止餓漢式的單例模式,沒法阻止懶漢式的單例!

我們可以來寫個懶漢模式測試下:

圖片

執行下,發現結果仍然是拋異常:

圖片

什么情況?

別急,我們把 instance1 和 instance2 的構建順序調換下:

圖片

再執行,結果就是 false 了!!!

這是因為懶漢式的對象只有調用的時候才被創建,我們先調用反射通過私有構造函數來創建對象,這樣就越過了 instance != null 的判斷,不會拋異常,再通過 getInstance 創建對象,這兩個對象就不是同一個對象了,即單例模式被破壞了。

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

優雅的單例實現:Enum

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

答案是有的!可以選擇使用 Enum 枚舉來實現單例模式

圖片

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

圖片

簡單來說就是因為 singletonClass.getDeclaredConstructor() 沒有找到 Singleton 的無參構造器,這是為啥?

主要是因為,一旦一個類聲明為枚舉,實際上就是繼承了 java.lang.Enum,來看看 Enum 類源碼:

圖片

Enum 有兩個參數 name 和 ordial 兩個屬性,我們自己寫的單例類繼承了父類 Enum 的構造函數,所以在上述的 getDecalredConstructor 才會找不到無參構造器,那么是不是我們去調用父類的構造器就可以了呢?我們來測試一下:

圖片

哦吼,運行后直接拋 IllegalArgumentException 異常了

圖片

無法通過反射創建 Enum 對象!!!我們點進去報錯的 22 行即 constructor.newInstance 一探究竟:

圖片

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

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

事實上,枚舉對象的序列化、反序列化有自己的一套機制:

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

來看看 Enum.valueOf 方法:

圖片

繼續看 getEnumConstantsShared() 源碼:

圖片

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

簡單總結下:

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

2023-12-05 08:20:05

單例模式Python

2021-02-01 10:01:58

設計模式 Java單例模式

2021-03-02 08:50:31

設計單例模式

2016-03-28 10:23:11

Android設計單例

2013-11-26 16:20:26

Android設計模式

2021-09-07 10:44:35

異步單例模式

2021-02-07 23:58:10

單例模式對象

2011-03-16 10:13:31

java單例模式

2022-02-06 22:30:36

前端設計模式

2022-06-07 08:55:04

Golang單例模式語言

2015-09-06 11:07:52

C++設計模式單例模式

2024-02-04 12:04:17

2024-03-06 13:19:19

工廠模式Python函數

2023-11-21 21:39:38

單例模式音頻管理器

2011-06-28 15:18:45

Qt 單例模式

2016-10-09 09:37:49

javascript單例模式

2019-06-11 09:50:07

SparkBroadcast代碼

2021-07-27 07:31:16

單例模式關鍵字

2020-09-16 12:18:28

GoJava模式

2010-02-05 17:00:06

C++單例模式
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久久久国产精品一区二区 | 国产japanhdxxxx麻豆 | 操久久| 午夜播放器在线观看 | 91成人在线视频 | 91香蕉视频在线观看 | 欧美在线天堂 | 成人精品一区二区三区四区 | 久久精品国产一区老色匹 | 久久夜视频 | 日本精品一区二区三区视频 | 一区二区三区国产精品 | 亚洲日本一区二区 | 久久爱黑人激情av摘花 | 亚洲国产精选 | 欧美 日韩 中文 | 国产亚洲成av人片在线观看桃 | 日韩一区二区三区在线观看 | 91久久久久 | 日日干天天干 | 久久综合久久久 | 午夜在线观看视频 | 日韩欧美三区 | 中文成人无字幕乱码精品 | 国产精品视频在线播放 | 成人小视频在线免费观看 | 国产精品久久久久一区二区三区 | 亚洲色欧美另类 | 龙珠z在线观看 | 人人做人人澡人人爽欧美 | 精品影院 | 成人精品毛片 | 最新中文在线视频 | 不卡视频一区二区三区 | 一区二区三区国产好 | 国产精品久久久久久福利一牛影视 | 又爽又黄axxx片免费观看 | 午夜影院普通用户体验区 | 日韩精品一区二区三区在线观看 | 午夜精品在线观看 | 日韩一区二区视频 |