設計模式1:建立型-單例模式
如果你沒有一顆最求完善的心,得過且過,請遠離設計模式。
如果你不知道設計原則,請遠離設計模式。
設計模式的目的在於:讓使用者更容易使用,讓設計者容易拓展與修正
單例意味著什麼?----崇高與孤獨
?--什麼樣的物件適合用單例? |--一個物件足以完成任務時,沒有必要去建立多個物件 |--一個物件非常消耗資源,需要限制物件的建立
比如:世界,你不會讓別人隨便去new,如何不讓外部無法建立本類物件,
如何給外部提供唯一的物件,是單例模式的核心,要達到這兩點很簡單。
一、單例的四種形式-- 形式上的一切都僅是開始而已
1.終極孤獨--餓漢
即便無人問津,我也永遠存在

餓漢式.png
public class World_1 { private static World sWorld = new World(); //[1]私有化構造 private World() { initWorld();//初始化世界 System.out.println("世界已建立"); } private void initWorld() { } //[2]返回內部靜態例項 public static World getInstance() { return sWorld; } }
2.有你相伴--懶漢雙檢鎖
雖然我們不是同類,但感謝有你相伴

懶漢雙檢鎖.png
public class World { private static volatile World sWorld; //[1]私有化構造 private World() { initWorld();//初始化世界 System.out.println("世界已建立"); } private void initWorld() { } //[2]返回內部靜態例項 public static World getInstance() { if (sWorld == null) {//判斷非空後--執行 synchronized (World.class) {//加鎖,保證多執行緒下的單例 if (sWorld == null) {//非空,建立例項 sWorld = new World(); } } } return sWorld; } }
3.有你相伴--靜態內部類
和上面的功能基本一致,所以我喜歡這個

靜態內部類.png
public class World { //[1]私有化構造 private World() { initWorld();//初始化世界 System.out.println("世界已建立"); } private void initWorld() { } //[3]返回內部靜態例項 public static World getInstance() { return WorldHolder.sWorld; } //[2]建立內部類建立例項 private static class WorldHolder { private static final World sWorld = new World(); } }
4.至簡--列舉

列舉.png
public enum World { INSTANCE; World() { initWorld();//初始化世界 System.out.println("世界已建立"); } private void initWorld() { } }
二、單例下的序列化與反射
單例的價值在於一個程式中只用一個該物件例項
如果有惡意份子通過反射建立了另一個世界會怎麼樣?
1.單例的測試(以靜態內部類版為例)
通過debug看出兩次獲取的都是同一個世界,這就是單一例項

單例測試.png
public class God { public static void main(String[] args) { World world1 = World.getInstance(); World world2 = World.getInstance(); } }
2.通過反射建立例項
可見 world3
的記憶體地址已經不一樣了,說明出現了第二個世界,也就是單例的失效
不過應該沒有人吃飽了沒事幹用反射建立單例物件吧,天堂有路你不走...

反射測試.png
public class God { public static void main(String[] args) { World world1 = World.getInstance(); World world2 = World.getInstance(); //通過反射建立 Class<World> worldClass = World.class; try { Constructor<World> constructor = worldClass.getDeclaredConstructor(null); constructor.setAccessible(true); World world3 = constructor.newInstance(); System.out.println(world3==world2);//false System.out.println(world1==world2);//true } catch (Exception e) { e.printStackTrace(); } } }
3.通過反序列化建立物件
如果你的單例類有序列化的需求(如,單例物件本地儲存,單例物件網路傳輸)
反序列化形成的例項也並非原來的例項

反序列化.png
---->[World]------------- public class World implements Serializable { ---->[God]------------- public class God { public static void main(String[] args) { World world1 = World.getInstance(); World world2 = World.getInstance(); //通過反射建立 Class<World> worldClass = World.class; try { Constructor<World> constructor = worldClass.getDeclaredConstructor(null); constructor.setAccessible(true); World world3 = constructor.newInstance(); System.out.println(world3 == world2);//false System.out.println(world1 == world2);//true } catch (Exception e) { e.printStackTrace(); } //通過反序列化建立物件 try { //序列化輸出 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("world.obj")); oos.writeObject(world1); //反序列化建立物件 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("world.obj")); World world4 = (World) ois.readObject(); ois.close(); System.out.println(world1 == world4);//false } catch (Exception e) { e.printStackTrace(); } } }
4.發序列化的解決方案
通過反序列化時的鉤子函式: readResolve
來控制序列化物件例項

反序列化的防治.png
---->[World]------------- //解決反序列化建立例項的問題,readResolve建立的物件會直接替換io流讀取的物件 private Object readResolve() throws ObjectStreamException { return getInstance(); }
三、結尾小述
1.優缺點
優點: 設計者嚴格管控這個類,全域性作用。簡化使用者使用 只建立一個例項,避免頻繁建立銷燬物件,節省記憶體資源,減少系統開銷 缺點: 沒有抽象層,不利於擴充套件 職責過重,違背單一職責原則
2.常見的單例
java.util.Calendar 標準單例,通過Calendar.getInstance方法獲取物件 java.lang.System 完全單例,不提供外部構造方法,全部以靜態方法提供服務 android.view.LayoutInflater 標準單例 ,通過LayoutInflater.from(Context)方法獲取物件
後記:捷文規範
1.本文成長記錄及勘誤表
專案原始碼 | 日期 | 附錄 |
---|---|---|
V0.1--github | 2018-2-10 | 無 |
釋出名: 設計模式1:建立型-單例模式
捷文連結: https://www.jianshu.com/p/6c1f3e355ba9
2.更多關於我
筆名 | 微信 | |
---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 |
我的github: https://github.com/toly1994328
我的簡書: https://www.jianshu.com/u/e4e52c116681
我的掘金: https://juejin.im/user/5b42c0656fb9a04fe727eb37
個人網站: http://www.toly1994.com
3.宣告
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援

icon_wx_200.png