建立型模式: – 單例模式、工廠模式、抽象工廠模式、建造者模式、原型模式。java
• 結構型模式: – 適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模 式。spring
• 行爲型模式: – 模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模 式、解釋器模式、狀態模式、策略模式、職責鏈模式、訪問者模式數據庫
單例模式:編程
• 核心做用: – 保證一個類只有一個實例,而且提供一個訪問該實例的全局訪問點windows
• 常見應用場景:安全
– Windows的Task Manager(任務管理器)就是很典型的單例模式 – windows的Recycle Bin(回收站)也是典型的單例應用。在整個系統運行過程當中,回收站一直維護着僅有的一個實例。併發
– 項目中,讀取配置文件的類,通常也只有一個對象。沒有必要每次使用配置文件數據,每次new一個對象去讀取。框架
– 網站的計數器,通常也是採用單例模式實現,不然難以同步。jvm
– 應用程序的日誌應用,通常都何用單例模式實現,這通常是因爲共享的日誌文件一直處於打開狀態,由於只能有一個實例去操做 ,不然內容很差追加。ide
– 數據庫鏈接池的設計通常也是採用單例模式,由於數據庫鏈接是一種數據庫資源。
– 操做系統的文件系統,也是大的單例模式實現的具體例子,一個操做系統只能有一個文件系統。
– Application 也是單例的典型應用
– 在Spring中,每一個Bean默認就是單例的,這樣作的優勢是Spring容器能夠管理 – 在servlet編程中,每一個Servlet也是單例 – 在spring MVC框架/struts1框架中,控制器對象也是單例
單例模式的優勢:
– 因爲單例模式只生成一個實例,減小了系統性能開銷,當一個對象的產生須要 比較多的資源時,如讀取配置、產生其餘依賴對象時,則能夠經過在應用啓動 時直接產生一個單例對象,而後永久駐留內存的方式來解決 – 單例模式能夠在系統設置全局的訪問點,優化環共享資源訪問,例如能夠設計 一個單例類,負責全部數據表的映射處理
常見的五種單例模式實現方式:
– 主要: • 餓漢式(線程安全,調用效率高。 可是,不能延時加載。)
• 懶漢式(線程安全,調用效率不高。 可是,能夠延時加載。)
– 其餘: • 雙重檢測鎖式(因爲JVM底層內部模型緣由,偶爾會出問題。不建議使用)
• 靜態內部類式(線程安全,調用效率高。 可是,能夠延時加載)
• 枚舉單例(線程安全,調用效率高,不能延時加載)
餓漢式實現(單例對象當即加載)
//餓漢式單例 public class Singleton01 { //類初始化時,當即加載這個對象 //加載類是自然的線程安全的,(沒有延時加載的優點) private static Singleton01 instance=new Singleton01(); private Singleton01(){ } //方法沒有同步,調用效率高 public static Singleton01 getInstance(){ return instance; } }
懶漢式
//懶漢式 public class Singleton02 { //類初始化時,不初始化這個對象(真正用到的時候在建立) private static Singleton02 instance; private Singleton02(){ //私有構造器 } //方法同步,調用效率低 public static synchronized Singleton02 getInstance(){ if(instance==null){ instance=new Singleton02(); } return instance; } }
雙重檢索:
//雙重檢測所機制 //因爲編譯器優化緣由和jvm底層內部模型緣由,有時候回出現問題 public class Singleton03 { private static Singleton03 instance=null; public static Singleton03 getInstance(){ if(instance==null){ Singleton03 sc; synchronized (Singleton03.class){ sc=instance; if(sc==null){ synchronized(Singleton03.class){ if(sc==null){ sc=new Singleton03(); } } instance=sc; } } } return instance; } private Singleton03(){ } }
靜態內部類:
//靜態內部類 public class Singleton04 { //要點: //– 外部類沒有static屬性,則不會像餓漢式那樣當即加載對象。 //– 只有真正調用getInstance(),纔會加載靜態內部類。加載類時是線程 安全的。 //instance是static final 類型,保證了內存中只有這樣一個實例存在,並且只能被賦值一次, //從而保證了線程安全性. – 兼備了併發高效調用和延遲加載的優點! private static class Singleton04ClassInstance { private static final Singleton04 instance = new Singleton04(); } private Singleton04() { } public static Singleton04 getInstance() { return Singleton04ClassInstance.instance; } }
枚舉模式:
//枚舉單例(沒有延時加載) public enum Singleton05 { //定義一個枚舉,枚舉 元素自己就是一個單例 INSTANCE; //添加本身須要的元素 public void singletonOperation(){ } }
public static void main(String[] args) { Singleton01 s1=Singleton01.getInstance(); Singleton01 s2=Singleton01.getInstance(); System.out.println(s1); System.out.println(s2); }
常見的五種單例模式實現方式
– 主要: • 餓漢式(線程安全,調用效率高。 可是,不能延時加載。) • 懶漢式(線程安全,調用效率不高。 可是,能夠延時加載。)
– 其餘: • 雙重檢測鎖式(因爲JVM底層內部模型緣由,偶爾會出問題。不建議使用) • 靜態內部類式(線程安全,調用效率高。 可是,能夠延時加載) • 枚舉式(線程安全,調用效率高,不能延時加載。而且能夠自然的防止反射和反序列 化漏洞!) • 如何選用? – 單例對象 佔用 資源 少,不須要 延時加載: • 枚舉式 好於 餓漢式 – 單例對象 佔用 資源 大,須要 延時加載: • 靜態內部類式 好於 懶漢式
反射和序列化破壞單例:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; public class SingletonTest01 { public static void main(String[] args) throws Exception{ Singleton06 s1=Singleton06.getInstance(); Singleton06 s2=Singleton06.getInstance(); System.out.println(s1); System.out.println(s2); //經過反射的方式直接調用私有構造器 Class<Singleton06> clazz= (Class<Singleton06>)Class.forName("demo.singleton.Singleton06"); //獲取無參數構造器 Constructor<Singleton06> c=clazz.getDeclaredConstructor(null); //跳過權限的檢查 c.setAccessible(true); Singleton06 s3=c.newInstance(); Singleton06 s4=c.newInstance(); System.out.println(s3); System.out.println(s4); //經過序列化的方式構造多個對象 FileOutputStream fos=new FileOutputStream("d:/a.txt"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(s1); oos.close(); fos.close(); ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/a.txt")); Singleton06 s5=(Singleton06) ois.readObject(); System.out.println(s5); } }
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; public class SingletonTest01 { public static void main(String[] args) throws Exception{ Singleton06 s1=Singleton06.getInstance(); Singleton06 s2=Singleton06.getInstance(); System.out.println(s1); System.out.println(s2); //經過反射的方式直接調用私有構造器 Class<Singleton06> clazz= (Class<Singleton06>)Class.forName("demo.singleton.Singleton06"); //獲取無參數構造器 Constructor<Singleton06> c=clazz.getDeclaredConstructor(null); //跳過權限的檢查 c.setAccessible(true); Singleton06 s3=c.newInstance(); Singleton06 s4=c.newInstance(); System.out.println(s3); System.out.println(s4); //經過序列化的方式構造多個對象 FileOutputStream fos=new FileOutputStream("d:/a.txt"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(s1); oos.close(); fos.close(); ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/a.txt")); Singleton06 s5=(Singleton06) ois.readObject(); System.out.println(s5); } }
測試:
import java.util.concurrent.CountDownLatch; public class SingletonTest03 { public static void main(String[] args) throws Exception { long start=System.currentTimeMillis(); int threanNum=10; final CountDownLatch countDownLath=new CountDownLatch(threanNum); for(int i=0;i<10;i++){ new Thread(new Runnable(){ @Override public void run(){ for(int i=0;i<1000000;i++){ //Object o=Singleton04.getInstance(); Object o=Singleton05.INSTANCE; } countDownLath.countDown(); } }).start(); } countDownLath.await();//mian線程阻塞,知道計數器變成0,纔會繼續往下執行 long end=System.currentTimeMillis(); System.out.println("總耗時:"+(end-start)); } }