单例模式类似计划生育,其保证类仅有一个实例,且对外提供访问该实例的接口。
所有类都有构造器,若不显示定义,则系统提供默认的构造器;若显示定义,则默认的构造器失效。
使用常规代码实现单例会导致代码臃肿,后期debug和修改时工作量加大。
public class Singleton {
private static Singleton obj; //obj设为static
private Singleton() { //构造器设为private
}
public static Singleton getInstance() { //给外部提供一个单例接口
if (obj == null)
obj = new Singleton();
return obj;
}
}
实用类:仅仅只是静态方法和静态属性的集合,没有实例对象,如Math类。
为保证单例模式在多线程下的安全性,有两种解决方案,饿汉式和懒汉式(形象的比喻)。
饿汉式(线程安全,代码简洁,空间效率低):
public class Singleton {
private static Singleton obj = new Singleton(); //obj设为static
private Singleton() { //构造器设为private
}
public static Singleton getInstance() { //给外部提供一个单例接口
return obj;
}
}
懒汉式(延迟实例对象创建时间,只有在需要时才创建):线程等待时有一定的时间开销。
直接对接口加锁:效率低,保证线程安全。
public class Singleton {
private static Singleton obj; //obj设为static
private Singleton() { //构造器设为private
}
public synchronized static Singleton getInstance() { //给外部提供一个单例接口
if (obj == null)
obj = new Singleton();
return obj;
}
}
双重检测:效率高,保证线程安全
public class Singleton {
private static Singleton obj; //obj设为static
private static Object lock = new Object(); //创建一个对象,该对象作为锁使用
private Singleton() { //构造器设为private
}
public static Singleton getInstance() { //给外部提供一个单例接口
if (obj == null) { //只有对象还未创建时,才进行加锁,否则直接返回对象,无需加锁
synchronized (lock) {
if (obj == null)
obj = new Singleton();
}
}
return obj;
}
}
JDK5之后实现单例的最佳方法是枚举单个元素。
枚举类似类,一个枚举可以拥有成员变量,成员方法,构造方法。
enum Type {
A,B,C;
//field and method...
}
上面的枚举类型等价:
class Type extends Enum {
public static final Type A;
public static final Type B;
public static final Type C;
//field and method...
}
注意这个过程是由javac编译器自动转换的,手动书写上述代码不能通过编译。
枚举类中的构造方法默认是私有的,因此可以利用这个特性实现单例。
class User {
//...
}
enum SingletonUser{
INSTANCE;
private User user;
SingletonUser() {
user = new User();
}
public User getInstance() {
return user;
}
}
我们可以通过SingletonUser.INSTANCE.getInstance()方法获取User类的唯一实例user。