单例模式是一种常见的设计模式,在《Java与模式》一书中,阎宏博士对单例模式做了全面的总结。
单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。
单例模式有一下特点:
1、单例类只能有一个实例。
2、单例类必须自己自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
说明:一下的代码来自阎宏博士的《Java与模式》一书,其中对一些类的写法做调整(符合Java1.5的习惯),另外还加了测试方法。
一、懒汉式单例
在类加载的时候不创建单例实例。只有在第一次请求实例的时候的时候创建,并且只在第一次创建后,以后不再创建该类的实例。
public class LazySingleton {
private static LazySingleton m_intance=null;
private LazySingleton(){
}
synchronized public static LazySingleton getInstance(){
if(m_intance==null){
m_intance=new LazySingleton();
}
return m_intance;
}
}
此种要保证为单例,首先要保证构造方法是私有的,然后获取实例的方法一定要是线程安全的,不然不行
问题:如果方法前不加
synchronized
,在多线程情况下,两个线程同时调用这个方法,那么此时是报错呢,还是创建了两个实例,个人认为应该是报错吧,因为
m_intance 申明为静态的
,它能被实例化多次吗
二、饿汉式单例
在类被加载的时候,唯一实例已经被创建。这个设计模式在Java中容易实现,在别的语言中难以实现。
public class EagerSingleton {
private static final EagerSingleton m_instance = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return m_instance;
}
}
说明:这里用static final 保证了这个
m_instance
是类变量,而不是属于某个实例的,而且这个引用值是不可变的。这个类在加载时己经创建好了这个实例,所以
不用在方法前在加
synchronized 关键字了
三、登记式单例
这个单例实际上维护的是一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从工厂直接返回,对于没有登记的,则先登记,而后返回。
public class RegSingleton {
private static Map<String, RegSingleton> m_registry = new HashMap();
//在类加载的时候添加一个实例到登记薄
static {
RegSingleton x = new RegSingleton();
m_registry.put(x.getClass().getName(), x);
}
protected 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);
}
public String about() {
return "Hello,I am RegSingleton!";
}
}
四、单例模式的一个应用
该应用是配置文件管理类。为了本例能正确运行,我在C盘下先建立了一个xxxx.properties文件,内容如下:
-------------------
user=root
password=leizhimin
这个配置文件管理类的代码如下:
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();
}
}
synchronized public static ConfigManager getInstance() {
return m_instance;
}
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;
}
}
}
测试配置文件类:
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数据库工具类的单例实现
public class DBUtil {
//单一实例
private static final DBUtil _instance = new DBUtil();
//数据源的JNDI
private static final String datasource = "java:comp/env/jdbc/zvfims";
private DBUtil() {
}
public DBUtil getInstance() {
return _instance;
}
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;
}
}
通过这个单例类和开放的业务方法,可以为整个系统应用提供数据库连接。