单例常见的有饿汉式和懒汉式和很少使用的登记式
饿汉式:
class HungerSingleton {
private HungerSingleton hungerSingleton = new HungerSingleton();
private HungerSingleton() {
}
public HungerSingleton getInstance() {
return hungerSingleton;
}
}
懒汉式:
class LazySingleton {
private LazySingleton lazySingleton;
private LazySingleton() {
}
public synchronized LazySingleton getInstance() {
if (lazySingleton == null)
lazySingleton = new LazySingleton();
return lazySingleton;
}
}
登记式:
class RegSingleton {
private static Map<String, RegSingleton> regSingletonMap = new HashMap<String, RegSingleton>();
static {
RegSingleton regSingleton = new RegSingleton();
regSingletonMap.put(regSingleton.getClass().getName(), regSingleton);
}
protected RegSingleton() {
}
public static RegSingleton getInstance(String name) throws Exception {
if (name == null || name.equals(""))
name = "RegSingleton";
if (regSingletonMap.containsKey(name))
regSingletonMap.put(name, (RegSingleton) Class.forName(name).newInstance());
return regSingletonMap.get(name);
}
}
登记式的单例可以被继承,但是很明显可以看出,由于构造器不能使private,所以允许该类的实例不在登记的范围内。
Lazy Loading:
class LazyLoadingSingleton {
private LazyLoadingSingleton() {
}
private static class LazyLoadingSingletonHolder {
private static LazyLoadingSingleton lazyLoadingSingleton = new LazyLoadingSingleton();
}
public LazyLoadingSingleton getInstance() {
return LazyLoadingSingletonHolder.lazyLoadingSingleton;
}
}
要理解LazyLoading首先要看Classloader是如何加载类的
所有的Java虚拟机实现必须在每个类或接口被Java程序首次主动使用时才初始化他们。
有六种情况被视作为主动使用:
1. 创建类的实例
2. 访问某个类或接口的静态变量,或者对该静态变量赋值
3. 调用类的静态方法
4. 反射(如Class.forName(""))
5. 初始化一个类的子类 (初始化子类的过程中会主动使用父类的构造方法)
6. Java虚拟机启动时被标明为启动类的类(java命令指定的类)
而其他情况都被视为被动加载就是指,只有getInstance()方法被调用的时候,Classloader才会加载LazyLoadingSingleton$LazyLoadingSingletonHolder这个类并做连接和初始化
而由于lazyLoadingSingleton引用被声明为static,所以在初始化LazyLoadingSingletonHolder时,能够保证只被实例化一次
这样使得LazyLoading既可以保证线程安全,又可以做到LazyLoad