一、单例模式核心思想
单例模式是一种特殊的工厂方法模式,它适合于一个类只有一个实例的情况,比如窗口管理器打印缓冲池和文件系统。典型的情况是,那些对象的实例能够被整个软件系统的不同对象访问,因此需要一个全局的访问指针,这便是众所周知的单例模式的应用。当然这只有在不再需要任何多于一个的实例的情况下出现。
通过单例模式你可以:
- 确保一个类只有一个实例被建立。
- 提供了一个对对象的全局访问指针。
- 在不影响单例类的客户端的情况下允许将来有多个实例。
单例模式的实例在全局中有且只有一个,并且该实例必须由自身创建,不能够被克隆。为了满足这些要求,一个标准的单例模式需要包含如下4个要素:
(1) 拥有一个私有的静态实例,该实例禁止外部访问。
(2) 拥有私有的默认构造函数,防止使用构造函数进行实例化。
(3) 拥有一个静态工厂方法,并且必须是同步的,防止多线程环境同时执行。
(4) 重写clone0)函数,并返回当前实例对象,默认的clone()函数会创建新的实例。
根据以上4点要求,完成的标准单例模式代码如下程序所示:
java
package creation.singleton;
public class SingletonFactory {
//(1)私有的防止外部引用
private static SingletonFactory _instance = null;
//(2)私有的默认构造函数,防止使用构造函数进行实例化
private SingletonFactory(){}
//(3)单例静态工厂方法,同步防止多线程环境同时执行
synchronized public static SingletonFactory getInstance(){
if( _instance == null){
_instance = new SingletonFactory();
}
return _instance;
}
//(4)重写该函数,默认的cloneO函数会创建新的实例
public SingletonFactory clone(){
return getInstance();
}
}
以上单例模式的代码中的各项缺一不可,读者可以作为单例模式的标准模板使用,在后面的示例中我们也将使用该模板进行开发。
二、何时使用单例模式
使用单例模式有一个前提条件:就是在一个系统中某一个类的实例必须只有一个,如果可以有多个实例存在,就不能够使用单例模式。以上的说法比较抽象,具体的可以应用在如下场景:
- 系统的全局变量、存储区域。
- 系统的全局配置文件。
- 系统的全局操作函数。
由此可见,只要希望在全局使用一个统一的对象,就可以使用单例模式。
三、属性文件加载工厂实例
根据以上的场景,我们举例说明。以属性文件为例,在一个系统中,通常需要配置全局的属性文件,例如用户名和密码文件,如下程序所示:
属性文件user.properties
java
admin=123
liuzhongbing=123
guest=123
为了在系统中使用该属性文件中定义的用户名和密码,可以根据以上的单例模式模板文件SingletonFactory.java创建一个工厂类,其中定义一个私有变量 properties,用来在系统启动时将文件读取数据存储在该变量中,并提供一个按照用户名查找密码的函数getConfigO。完整的代码如下程序所示:
属性文件工厂PropertiesFactory.java
java
import java.util.Properties;
import java.io.FilelnputStream;
public class PropertiesFactory {
//(1)私有的防止外部引用
private static PropertiesFactory instance = null;
private Properties properties = new Properties();
//(2)私有的默认构造函数,防止使用构造函数进行实例化
private PropertiesFactory(){
try {
properties.load(new
FileInputStream("sre/creation/singleton/user.properties'));
}catch (Exception e) {
e.printStackTrace();
}
}
//(3)单例静态工厂方法,同步防止多线程环境同时执行
public static synchronized PropertiesFactory getInstance(){
if( instance == null){
instance = new PropertiesFactory();
return instance;
}
}
//(4)重写该函数,默认的clone()函数会创建新的实例
public PropertiesFactory clone(){
return getInstance();
}
public String getConfg(String key){
return properties.getProperty(key);
}
}
编写测试类来读取其中的密码,如下程序所示:
测试类PropertiesFactoryTest.java
java
package creation.singleton;
publie class PropertiesFactoryTest {
public static void main(String[] args){
PropertiesFactory factory= PropertiesFactory.getInstance():
String pwdl = factory.getConfig("admin");
System.out.println(pwdl);
}
}
运行该程序即会输出密码:123。
四、Java中的应用--日历单例类 Calendar
java
package java.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io .ObjectOutputStream;
import java.io.Serializable;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import sun.util.BuddhistCalendar;
import sun.util.calendar.Zonelnfo;
import sun.util.resources.LocaleData;
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar>{
protected Calendar(){
this(TimeZone.getDefaultRef(), Locale.getDefault());
sharedZone = true;
}
protected Calendar(TimeZone zone, Locale aLocale){
fields = new int[FIELD_COUNT];
isSet = new boolean[FIELD_COUNT];
stamp = new int[FIELD_COUNT];
this.zone = zone;
setWeekCountData(aLocale);
}
public static Calendar getInstance() {
Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault());
cal.sharedZone = true;
return cal;
}
public static Calendar getInstance(TimeZone zone) {
return createCalendar(zone, Locale.getDefault());
}
public statie Calendar getInstance(Loeale aLocale) {
Calendar cal = createCalendar(TimeZone.getDefaultRef(), aLocale)
cal.sharedZone = true;
return cal;
}
public static Calendar getInstance(TimeZone zone, Locale aLocale) {
return createCalendar(zone, aLocale);
}
}
从代码可以看出,该单例类的应用并不是严格遵守单例模式的四项规则。所以,在实际的开发中,可以根据实际的需要来自由运用。