設計模式總結之單例模式
單例模式(Singleton):“保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。”
通過單例模式可以保證系統中一個類只有一個例項而且該例項易於外界訪問,從而方便對例項個數的控制並節約系統資源。如果希望在系統中某個類的物件只能存在一個,單例模式是最好的解決方案。
單例模式結構圖
從具體實現角度來說,注意以下三點:
- 單例模式的類只提供私有的建構函式
- 類定義中含有一個該類的靜態私有物件
- 該類提供了一個靜態的共有的函式用於建立或獲取它本身的靜態私有物件
一般單例模式通常有三種形式(考慮了多執行緒之後的情況):
- 懶漢式
public class Singleton{ private static Singleton instance=null; private Singleton(){ //do something } public static synchronized Singleton GetInstance(){ if(instance==null){ instance=newSingleton(); } return instance; } }
- 餓漢式
public class Singleton{
//在自己內部定義自己的一個例項,只供內部呼叫
private static Singleton instance=new Singleton();
private Singleton(){
//do something
}
//這裡提供了一個供外部訪問本class的靜態方法,可以直接訪問
public static Singleton GetInstance(){
return instance;
}
}
- 雙重鎖的形式
單例模式使用場景:public class Singleton{ private static Singleton instance=null; private Singleton(){ //do something } public static Singleton GetInstance(){ if(instance==null){ synchronized(Singleton.class){ if(null==instance){ instance=new Singleton(); } } } return instance; } }//這個模式將同步內容下方到if內部,提高了執行的效率,不必每次獲取物件時都進行同步,只有第一次才同步,建立了以後就沒必要了。
1. Windows的Task Manager(工作管理員)就是很典型的單例模式(這個很熟悉吧),想想看,是不是呢,你能開啟兩個windows task manager嗎?
2. windows的Recycle Bin(回收站)也是典型的單例應用。在整個系統執行過程中,回收站一直維護著僅有的一個例項。
3. 網站的計數器,一般也是採用單例模式實現,否則難以同步。
4. 應用程式的日誌應用,一般都何用單例模式實現,這一般是由於共享的日誌檔案一直處於開啟狀態,因為只能有一個例項去操作,否則內容不好追加。
5. Web應用的配置物件的讀取,一般也應用單例模式,這個是由於配置檔案是共享的資源。
6. 資料庫連線池的設計一般也是採用單例模式,因為資料庫連線是一種資料庫資源。資料庫軟體系統中使用資料庫連線池,主要是節省開啟或者關閉資料庫連線所引起的效率損耗,這種效率上的損耗還是非常昂貴的,因為何用單例模式來維護,就可以大大降低這種損耗。
7. 多執行緒的執行緒池的設計一般也是採用單例模式,這是由於執行緒池要方便對池中的執行緒進行控制。
8. 作業系統的檔案系統,也是大的單例模式實現的具體例子,一個作業系統只能有一個檔案系統。
9. HttpApplication 也是單位例的典型應用。熟悉ASP.Net(IIS)的整個請求生命週期的人應該知道HttpApplication也是單例模式,所有的HttpModule都共享一個HttpApplication例項.總結以上,不難看出:
單例模式應用的場景一般發現在以下條件下:
(1)資源共享的情況下,避免由於資源操作時導致的效能或損耗等。如上述中的日誌檔案,應用配置。
(2)控制資源的情況下,方便資源之間的互相通訊。如執行緒池等。JDBC連線資料庫(單例)簡單例子:
package com.test.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * * @author AD JDBC輔助類 用於構建資料庫連線(採用單例模式) */ public final class JDBCUtilSingle { // 該url為預設方式(省略主機跟埠) // 完整為:jdbc:mysql//localhost:3306/test static String url = "jdbc:mysql:///test"; static String name = "root"; static String password = "sli"; static Connection conn = null; private static JDBCUtilSingle jdbcUtilSingle = null; public static JDBCUtilSingle getInitJDBCUtil() { if (jdbcUtilSingle == null) { // 給類加鎖 防止執行緒併發 synchronized (JDBCUtilSingle.class) { if (jdbcUtilSingle == null) { jdbcUtilSingle = new JDBCUtilSingle(); } } } return jdbcUtilSingle; } private JDBCUtilSingle() { } // 通過靜態程式碼塊註冊資料庫驅動,保證註冊只執行一次 static { try { // 註冊驅動有如下方式: // 1.通過驅動管理器註冊驅動,但會註冊兩次,並且會對類產生依賴。如果該類不存在那就報錯了。 Class.forName("com.mysql.jdbc.Driver");// 推薦使用方式 } catch (ClassNotFoundException e) { e.printStackTrace(); } } // 獲得連線 public Connection getConnection() { try { conn = DriverManager.getConnection(url,name,password); } catch (SQLException e) { e.printStackTrace(); } return conn; } // 關閉連線 public void closeConnection(ResultSet rs, Statement statement, Connection con) { try { if (rs != null) { rs.close(); } } catch (SQLException e) { e.printStackTrace(); } finally { try { if (statement != null) { statement.close(); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (con != null) { con.close(); } } catch (SQLException e) { e.printStackTrace(); } } } } }