23种设计模式学习记录之单例设计模式
本文所有案例代碼
碼云:https://gitee.com/helloworld6379/designPattern
Github:Github地址
設計模式概述
1 設計模式是程序員在面對同類軟件工程設計問題所總結出來的有用的經驗,模式不是代碼,而是某類問題的通
用解決方案,設計模式(Design pattern)代表了最佳的實踐。這些解決方案是眾多軟件開發人員經過相當長的
一段時間的試驗和錯誤總結出來的。
2 設計模式的本質提高 軟件的維護性,通用性和擴展性,并降低軟件的復雜度。
3 設計模式并不局限于某種語言,java,php,c++ 都有設計模式.
設計模式類型
設計模式分為三種類型,共 23 種
1 創建型模式:單例模式、抽象工廠模式、原型模式、建造者模式、工廠模式。
2 結構型模式:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
3 行為型模式:模版方法模式、命令模式、訪問者模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式(Interpreter 模式)、狀態模式、策略模式、職責鏈模式(責任鏈模式)。
不同的資料上對分類和名稱略有差別
單例設計模式
所謂類的單例設計模式,就是采取一定的方法保證在整個的軟件系統中,對某個類只能存在一個對象實例,并且該類只提供一個取得其對象實例的方法(靜態方法)。
比如 Hibernate 的 SessionFactory,它充當數據存儲源的代理,并負責創建 Session 對象。SessionFactory 并不是輕量級的,一般情況下,一個項目通常只需要一個 SessionFactory 就夠,這是就會使用到單例模式。
Windows的任務管理器就是一個單例模式的應用,只能打開一個。
單例模式有八種方式:
1. 餓漢式(靜態常量)
public class Person {//1. 構造器私有化, 外部不能newprivate Person() {}//2.本類內部創建對象實例private final static Person person = new Person();//3. 提供一個公有的靜態方法,返回實例對象public static Person getPerson() {return person;} } public class SingletonTest {public static void main(String[] args) {//測試Person person1 = Person.getPerson();Person person2 = Person.getPerson();System.out.println(person1 == person2); // trueSystem.out.println("person1.hashCode=" + person1.hashCode());System.out.println("person2.hashCode=" + person2.hashCode());} }優缺點說明:
1 優點:這種寫法比較簡單,就是在類裝載的時候就完成實例化。避免了線程同步問題。
2 缺點:在類裝載的時候就完成實例化,沒有達到 Lazy Loading 的效果。如果從始至終從未使用過這個實例,則會造成內存的浪費。
3 這種方式基于 classloder 機制避免了多線程的同步問題,不過,person 在類裝載時就實例化,在單例模式中大多數都是調用 getPerson 方法,但是導致類裝載的原因有很多種,因此不能確定有其他的方式(或者其他的靜態方法)導致類裝載,這時候初始化 person 就沒有達到 lazy loading 的效果。
4 結論:這種單例模式可用,可能造成內存浪費。
2 餓漢式(靜態代碼塊)
/*** @Description 餓漢式(靜態代碼塊)* @Author LiuXing* @Date 2020/05/21 23:04*/ public class Person {//1.本類內部創建對象實例private static Person person;//2. 構造器私有化, 外部不能newprivate Person() {}static { // 在靜態代碼塊中,創建單例對象person = new Person();}//3. 提供一個公有的靜態方法,返回實例對象public static Person getPerson() {return person;} }優缺點說明:
1 這種方式和上面的方式其實類似,只不過將類實例化的過程放在了靜態代碼塊中,也是在類裝載的時候,就執行靜態代碼塊中的代碼,初始化類的實例。優缺點和上面是一樣的。
2 結論:這種單例模式可用,但是可能造成內存浪費
3 懶漢式(線程不安全)
/*** @Description 懶漢式(線程不安全)* @Author LiuXing* @Date 2020/05/21 23:04*/ public class Person {//1.本類內部創建對象實例private static Person person;//2. 構造器私有化, 外部不能newprivate Person() {}//3. 提供一個公有的靜態方法,當使用到該方法時,才去創建public static Person getPerson() {if(person == null ){person = new Person();}return person;} }優缺點說明:
1 起到了 Lazy Loading 的效果,但是只能在單線程下使用。
2 如果在多線程下,一個線程進入了 if (person == null)判斷語句塊,還未來得及往下執行,另一個線程也通過了這個判斷語句,這時便會產生多個實例。所以在多線程環境下不可使用這種方式
3) 結論:在實際開發中,不要使用這種方式.
4 懶漢式(線程安全,同步方法)
/*** @Description 懶漢式(線程安全,同步方法)* @Author LiuXing* @Date 2020/05/21 23:04*/ public class Person {//1.本類內部創建對象實例private static Person person;//2. 構造器私有化, 外部不能newprivate Person() {}// 3. 提供一個公有的靜態方法,當使用到該方法時,才去創建// 加入同步處理的代碼,解決線程安全問題public static synchronized Person getPerson() {if(person == null ){person = new Person();}return person;} }優缺點說明:
1 解決了線程安全問題
2 效率太低了,每個線程在想獲得類的實例時候,執行 getPerson()方法都要進行同步。而其實這個方法只執行一次實例化代碼就夠了,后面的想獲得該類實例,直接 return 就行了。方法進行同步效率太低
3 結論:在實際開發中,不推薦使用這種方式
5 懶漢式(線程安全,同步代碼塊)
/*** @Description 懶漢式(線程安全,同步代碼塊)* @Author LiuXing* @Date 2020/05/21 23:04*/ public class Person {//1.本類內部創建對象實例private static Person person;//2. 構造器私有化, 外部不能newprivate Person() {}// 3. 提供一個公有的靜態方法,當使用到該方法時,才去創建// 加入同步處理的代碼,解決線程安全問題public static Person getPerson() {if(person == null ){synchronized (Person.class){person = new Person();}}return person;} }不推薦使用
6 雙重檢查
package com.fighting.pattern.singleton.type6;/*** @Description 雙重檢查* @Author LiuXing* @Date 2020/05/21 23:04*/ public class Person {//1.本類內部創建對象實例private volatile static Person person;//2. 構造器私有化, 外部不能newprivate Person() {}// 3. 寫一個靜態的共有方法,加入雙重檢查代碼,解決線程安全問題,同時解決懶加載問題public static Person getPerson() {if (person == null){synchronized (Person.class){if (person ==null){person = new Person();}}}return person;} }優缺點說明:
1 Double-Check 概念是多線程開發中常使用到的,如代碼中所示,我們進行了兩次 if (person == null)檢查,這樣就可以保證線程安全了。
2 這樣,實例化代碼只用執行一次,后面再次訪問時,判斷 if (person == null),直接 return 實例化對象,也避免的反復進行方法同步.
3 線程安全;延遲加載;效率較高
4 結論:在實際開發中,推薦使用這種單例設計模式
7 靜態內部類
package com.fighting.pattern.singleton.type7;/*** @Description 靜態內部類* @Author LiuXing* @Date 2020/05/21 23:04*/ public class Person {//1.本類內部創建對象實例private static Person person;//2. 構造器私有化, 外部不能newprivate Person() {}// 3. 寫一個靜態內部類,該類中有一個靜態屬性 PERSONprivate static class SingletonInstance{private static final Person PERSON = new Person();}//4 提供一個靜態的公有方法,直接返回 SingletonInstance.INSTANCEpublic static synchronized Person getPerson() {return SingletonInstance.PERSON;} }優缺點說明:
1 這種方式采用了類裝載的機制來保證初始化實例時只有一個線程。
2 靜態內部類方式在 Person 類被裝載時并不會立即實例化,而是在需要實例化時,調用 getPerson 方法,才會裝載 Person 類,從而完成 Person 的實例化。
3 類的靜態屬性只會在第一次加載類的時候初始化,所以在這里,JVM 幫助我們保證了線程的安全性,在類進行
初始化時,別的線程是無法進入的。
4 優點:避免了線程不安全,利用靜態內部類特點實現延遲加載,效率高。
5 結論:推薦使用。
8 枚舉
package com.fighting.pattern.singleton.type8;public enum Person {INSTANCE;//屬性public void sayOK(){System.out.println("OK~");} } /*** @Description 枚舉* @Author LiuXing* @Date 2020/05/21 23:03*/ public class SingletonTest {public static void main(String[] args) {System.out.println("枚舉~");Person person1 = Person.INSTANCE;Person person2 = Person.INSTANCE;System.out.println(person1 == person2); // trueSystem.out.println("person1.hashCode=" + person1.hashCode());System.out.println("person2.hashCode=" + person2.hashCode());} }優缺點說明:
1 這借助 JDK1.5 中添加的枚舉來實現單例模式。不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象。
2 這種方式是 Effective Java 作者 Josh Bloch 提倡的方式
3 結論:推薦使用
前七種方式可通過反射和反序列化的方式破解:
/*** @Description 靜態內部類* @Author LiuXing* @Date 2020/05/21 23:03*/ public class SingletonTest {public static void main(String[] args) throws Exception {System.out.println("靜態內部類~");//測試Person person1 = Person.getPerson();Person person2 = Person.getPerson();System.out.println(person1 == person2); // trueSystem.out.println("person1.hashCode=" + person1.hashCode());System.out.println("person2.hashCode=" + person2.hashCode());Class<Person> clazz = (Class<Person>)Class.forName("com.fighting.pattern.singleton.type7.Person");Constructor<Person> con = clazz.getDeclaredConstructor(null);con.setAccessible(true);Person person11 = con.newInstance();Person person22 = con.newInstance();System.out.println("通過反射破解:");System.out.println(person11 == person22); // falseSystem.out.println("person11.hashCode=" + person11.hashCode());System.out.println("person22.hashCode=" + person22.hashCode());System.out.println("person11=" + person11);System.out.println("person22=" + person22);//此種方法測試時 實體類Person實現Serializable接口FileOutputStream fos = new FileOutputStream( "D:/person.txt" );ObjectOutputStream oos = new ObjectOutputStream( fos );oos.writeObject( person1 );oos.close();fos.close();System.out.println("通過序列化反序列化破解:");ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/person.txt"));Person person3 = (Person)ois.readObject();System.out.println(person1 == person3); // trueSystem.out.println("person1.hashCode=" + person1.hashCode());System.out.println("person3.hashCode=" + person3.hashCode());System.out.println("person1=" + person1);System.out.println("person3=" + person3);} } 靜態內部類~ true person1.hashCode=460141958 person2.hashCode=460141958 通過反射破解: false person1.hashCode=1163157884 person2.hashCode=1956725890 通過序列化反序列化破解: false person1.hashCode=460141958 person2.hashCode=931919113單例模式在 JDK 應用的源碼分析
在JDK 中,java.lang.Runtime 就是經典的單例模式(餓漢式)
總結
以上是生活随笔為你收集整理的23种设计模式学习记录之单例设计模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 初识云计算————虚拟化背景
- 下一篇: 【设计模式】适配器模式:如何巧妙地过滤游