单例模式是类的创建模式。 满足三个要点:
(1) 单例类只能有一个实例。
(2) 必须由单例类自己创建这个实例。
(3) 单例类必须向外界提供这个实例。
为了保证单例类的实例不被其它类创建,必须使单例类的构造子为私有,因此,单例类不能被继承,不能有子类。
public class Singleton {
private static Singleton s1 = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return s1;
}
}
这一种实现方式也叫作“饿汉式”单例类,因为它本身持有一个对自身实例的静态引用,所以当此类被首次加载时便会将自己实例化。还有一种方式“懒汉式”--lazy:
public class Singleton {
private static Singleton s1 = null;
private Singleton() {}
public static Singleton getInstance() {
if (null == s1) {
s1 = new Singleton();
}
return s1;
}
}
当有外部首次获取它的实例的时候,即 Singleton.getInstance() 时才会去创建它的实例。
以上两种实现方法严格实现了单例模式的定义,但由于其构造子都是私有的,因此不能被继承。为了使其能够被继承,唯一的办法是使其构造子对其子类可见,但这样就使得可以从外部使用 new 操作符创建其实力,失去了单例类的意义,只有严格遵守使用 getInstance()方法而不是new 来获得其实例,这个单例类才有意义。
public class Singleton {
private static Map<String, Object> map = new HashMap<String, Object>();
static {
Singleton s = new Singleton();
map.put(s.getClass().getName(), s);
}
protected Singleton() { }
public static Singleton getInstance(String className)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
if (null == className) {
className = "com.mycompany.singleton.Singleton";
}
if (null == map.get(className)) {
map.put(className, Class.forName(className).newInstance());
}
return (Singleton) map.get(className);
}
}
这种方法是通过在类加载期间就创建其实例,并将其实例保存在 Map 中,以类名作为键值,调用 getInstance()方法时就从这个Map中取得这个加载期间产生的实例。
其子类的实例也有父类的 getInstance() 维护,保存在map中,我们需要将子类的类名传给getInstance()方法,父类使用这个类名创建其子类对象并登记在Map中。子类也是通过到这个Map中获得其实例。
public class SubSingleton extends Singleton {
protected SubSingleton() { }
public static SubSingleton getInstance() throws InstantiationException,
IllegalAccessException, ClassNotFoundException {
return ((SubSingleton) Singleton
.getInstance("com.mycompany.singleton.SubSingleton"));
}
}
由于父类的 getInstance(String className) 负责创建子类的实例,因此子类的构造方法对父类也必须是可见的,这样子类和父类都不能有私有构造子。所以除非必要,尽量不要试图继承单例类。
单例类可能维护了状态属性,比如维护一个 int 型的计数器,统计网站访问人数。这样的单例类是有状态的单例类。在一些情况下,如多个 ClassLoader 可能同时加载同一单例类的情况下,单例类可能有不同的实例,因为不同的类加载器加载的同一个类会被认为是不同的类型。这时候应避免使用有状态的单例类,因为有不同的实例同时存在,所以不同的用户看到的状态可能是不一致的。
多例模式 Multiton:
单例模式可以和容易推广到多例模式。既然单例模式可以维护一个自己创建的实例,那他也可以维护多个自己创建的实例并向外界提供他的实例。这种实现就是多例。
public class Multiton {
private static Multiton en = new Multiton();
private static Multiton jp = new Multiton();
private static Multiton cn = new Multiton();
private static Multiton tw = new Multiton();
private Multiton() { }
public static Multiton getInstance(String locale) {
if ("en".equals(locale)) {
return en;
} else if ("jp".equals(locale)) {
return jp;
} else if ("cn".equals(locale)) {
return cn;
} else if ("tw".equals(locale)) {
return tw;
} else {
return en;
}
}
public void showLocale() {
System.out.println(this);
}
}
这种实现可以应用到多语言版本的实现中。
Tips: 什么是i18n? -- i18n 是英文单词 Internationalization 的一种简写方式,因为在字母 i - n 之间一共有18个字母,所以又叫i18n.