單例模式是一種常見的設計模式,在《Java與模式》一書中,閻宏博士對單例模式做了全面的總結。
單例模式分三種:懶漢式單例、餓漢式單例、登記式單例三種。
單例模式有一下特點:
1、單例類只能有一個執行個體。
2、單例類必須自己建立自己的唯一執行個體。
3、單例類必須給所有其他對象提供這一執行個體。
說明:一下的代碼來自閻宏博士的《Java與模式》一書,其中對一些類的寫法做調整(符合Java1.5的習慣),另外還加了測試方法。
一、懶漢式單例
在類被載入的時候,唯一執行個體已經被建立。這個設計模式在Java中容易實現,在別的語言中難以實現。
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-9-11
* Time: 14:57:08
* <> 單例模式-懶漢式單例
*/
public class LazySingleton {
/**
* 私人靜態對象,載入時候不做初始化
*/
private static LazySingleton m_intance=null;
/**
* 私人構造方法,避免外部建立執行個體
*/
private LazySingleton(){
}
/**
* 靜態Factory 方法,返回此類的唯一執行個體.
* 當發現執行個體沒有初始化的時候,才初始化.
* @return LazySingleton
*/
synchronized public static LazySingleton getInstance(){
if(m_intance==null){
m_intance=new LazySingleton();
}
return m_intance;
}
}
二、餓漢式單例
在類載入的時候不建立單例執行個體。只有在第一次請求執行個體的時候的時候建立,並且只在第一次建立後,以後不再建立該類的執行個體。
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-9-11
* Time: 14:45:25
* <> 單例模式-餓漢式單例
*/
public class EagerSingleton {
/**
* 私人的(private)唯一(static final)執行個體成員,在類載入的時候就建立好了單例對象
*/
private static final EagerSingleton m_instance = new EagerSingleton();
/**
* 私人構造方法,避免外部建立執行個體
*/
private EagerSingleton() {
}
/**
* 靜態Factory 方法,返回此類的唯一執行個體.
* @return EagerSingleton
*/
public static EagerSingleton getInstance() {
return m_instance;
}
}
三、登記式單例
這個單例實際上維護的是一組單例類的執行個體,將這些執行個體存放在一個Map(登記薄)中,對於已經登記過的執行個體,則從工廠直接返回,對於沒有登記的,則先登記,而後返回。
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2005-9-11
* Time: 15:20:16
* <> 單例模式- 登記式單例
*/
public class RegSingleton {
/**
* 登記薄,用來存放所有登記的執行個體
*/
private static Map m_registry = new HashMap();
//在類載入的時候添加一個執行個體到登記薄
static {
RegSingleton x = new RegSingleton();
m_registry.put(x.getClass().getName(), x);
}
/**
* 受保護的預設構造方法
*/
protected RegSingleton() {
}
/**
* 靜態Factory 方法,返回指定登錄物件的唯一執行個體;
* 對於已登記的直接取出返回,對於還未登記的,先登記,然後取出返回
* @param name
* @return RegSingleton
*/
public static RegSingleton getInstance(String name) {
if (name == null) {
name = "RegSingleton";
}
if (m_registry.get(name) == null) {
try {
m_registry.put(name, (RegSingleton) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return m_registry.get(name);
}
/**
* 一個示意性的商業方法
* @return String
*/
public String about() {
return "Hello,I am RegSingleton!";
}
}
四、單例模式的一個應用
該應用是設定檔管理類。為了本例能正確運行,我在C盤下先建立了一個xxxx.properties檔案,內容如下:
-------------------
user=root
password=leizhimin
這個設定檔管理類的代碼如下:
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2005-9-11
* Time: 15:55:01
* 單例模式應用-單例類應用-設定檔管理
*/
public class ConfigManager {
/**
* 屬性檔案全名
*/
private static final String PFILE = "C:\xxx.properties";
/**
* 對應於屬性檔案的檔案物件變數
*/
private File m_file = null;
/**
* 屬性檔案的最後修改日期
*/
private long m_lastModifiedTime = 0;
/**
* 屬性檔案所對應的屬性物件變數
*/
private Properties m_props = null;
/**
* 本類可能存在的唯一的一個執行個體
*/
private static ConfigManager m_instance = new ConfigManager();
/**
* 私人構造子,用以保證外界無法直接執行個體化
*/
private ConfigManager() {
m_file = new File(PFILE);
m_lastModifiedTime = m_file.lastModified();
if (m_lastModifiedTime == 0) {
System.err.println(PFILE + " file does not exist!");
}
m_props = new Properties();
try {
m_props.load(new FileInputStream(PFILE));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 靜態Factory 方法
*
* @return ConfigManager
*/
synchronized public static ConfigManager getInstance() {
return m_instance;
}
/**
* 擷取屬性配置項的值
*
* @param name
* @param defaultVal
* @return Object
*/
public final Object getConfigItem(String name, Object defaultVal) {
long newTime = m_file.lastModified();
if (newTime == 0) {
//屬性檔案不存在
if (m_lastModifiedTime == 0) {
System.err.println(PFILE + " file does not exist!");
} else {
System.err.println(PFILE + " file was deleted!");
}
return defaultVal;
} else if (newTime > m_lastModifiedTime) {
m_props.clear();
try {
m_props.load(new FileInputStream(PFILE));
} catch (IOException e) {
e.printStackTrace();
}
}
m_lastModifiedTime = newTime;
Object val = m_props.getProperty(name);
if (val == null) {
return defaultVal;
} else {
return val;
}
}
}
測試組態檔案類:
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-9-11
* Time: 16:42:45
* 設定檔管理類測試
*/
public class Test_ConfigManager {
public static void main(String[] args) {
ConfigManager cfgm = ConfigManager.getInstance();
Object val1 = cfgm.getConfigItem("sdf", "leizhimin");
Object val2 = cfgm.getConfigItem("user", "leizhimin");
System.out.println(val1.toString());
System.out.println(val2.toString());
}
}
運行結果:
leizhimin
root
Process finished with exit code 0
五、筆者寫的一個JDBC資料庫工具類的單例實現
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2005-9-11
* Time: 18:04:46
* 單例模式在JDBC編程中的應用,用於設計資料庫工具類
*/
public class DBUtil {
//單一執行個體
private static final DBUtil _instance = new DBUtil();
//資料來源的JNDI
private static final String datasource = "java:comp/env/jdbc/zvfims";
/**
* 私人構造方法,防止外部執行個體化
*/
private DBUtil() {
}
/**
* 資料庫工具類執行個體工廠
*
* @return DBUtil
*/
public DBUtil getInstance() {
return _instance;
}
/**
* 業務方法:用於擷取資料庫連接
*
* @return Connection
*/
public Connection makeConnection() {
Connection conn = null;
try {
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(datasource);
conn = ds.getConnection();
} catch (NamingException e) {
System.out.println("擷取資料來源異常,請AppServer的JNDI資料來源配置!");
e.printStackTrace();
} catch (SQLException e) {
System.err.println("擷取資料庫連接發生異常!");
e.printStackTrace();
}
return conn;
}
}
通過這個單例類和開放的業務方法,可以為整個系統應用提供資料庫連接。