单例,顾名思义,就是整个进程运行过程中只有一个实例,单例对象的特征一般如下:
- 生命周期较长,通常在进程结束前都不需要释放;或者使用频率很高,反复创建不如牺少量内存来换取性能。
- 避免对共享资源的多重占用。
Java中实现单例一般有饿汉式和懒汉式两种主要方式。其中懒汉式又有多种拓展做法,如双重检测式、静态内部类式、枚举式等。
饿汉式
指在对象没有被使用到的时候创建。写法如下:
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
public static getInstance() {
return INSTANCE;
}
private Singleton() {
}
}
这种方式利用了类加载过程是唯一的并且安全的特性,但这种方式也有以下的限制或弊端:
- 构造函数不能带参数,或者参数本身不需要外界上下文信息。
- 可以使用反射创建多个对象。
- 如果对象始终没有被使用到(类使用到了),则白白浪费了内存。
另外这种实现方法还有一种变体,就是在类静态代码块中实例化对象。
懒汉式
public class Singleton {
private static Singleton sInstance;
public synchronized static getInstance(Context context) {
if(sInstance == null) {
sInstance = new Singleton(context);
}
return sInstance;
}
private Singleton(Context context) {
}
}
这种写法的优点是在使用时才创建对象,避免了内存浪费,但也存在以下限制或弊端:
- 可以使用反射创建多个对象。
- 每次调用getInstance()方法都要加锁,在高并发访问中会降低效率,为了改进这个问题,便有了双重检测式的出现。
双重检测
public class Singleton {
private static Singleton sInstance;
public static getInstance(Context context) {
if(sInstance == null) {
synchronized(Singleton.class) {
if(sInstance == null) {
sInstance = new Singleton(context);
}
}
}
return sInstance;
}
private Singleton(Context context) {
}
}
这种实现方式只有当对象未实例化前会加锁,提高了访问效率,但也存在以下限制或弊端:
- 可以使用反射创建多个对象。
静态内部类
这种方法是饿汉式的优化,采用了和饿汉式相同的原理同时又保证不会浪费内存,具体实现如下:
public class Singleton {
public static getInstance() {
return Inner.INSTANCE;
}
private Singleton() {
}
private static class Inner {
static final Singleton INSTANCE = new Singleton();
}
}
这种方式有以下的限制或弊端:
- 构造函数不能带参数,或者参数本身不需要外界上下文信息。
- 可以使用反射创建多个对象。
枚举
使用枚举的好处是不能通过反射实例化对象,原因是枚举类实际上是一个抽象类,比如
public enum Singleton {}
相当于
public abstract class Singleton extends Enum {}
抽象类是不能被实例化,即使是用反射也一样。枚举的实现方法如下:
public enum Singleton {
INSTANCE;
private Singleton() {
}
}
使用这种方法的缺陷是:
- 如果构造函数需要传入不是常量的参数则无法使用。
抽象类匿名子类
这种方式也是用来解决反射问题的,从枚举式中得到的灵感,网络上提到这种方式的文章较少。实现方法如下:
public abstract class Singleton {
private static Singleton sInstance;
public synchronized static Singleton getInstance(Context context) {
if(sInstance == null) {
sInstance = new Singleton(context) {};
}
return sInstance;
}
private Singleton(Context context) {
}
}
这种方式完美解决了各种问题,这里又使用了懒汉式,也可以替换成其他实现方式。