Java设计模式之单例

2019-03-16  本文已影响0人  Sean1225

单例,顾名思义,就是整个进程运行过程中只有一个实例,单例对象的特征一般如下:

  1. 生命周期较长,通常在进程结束前都不需要释放;或者使用频率很高,反复创建不如牺少量内存来换取性能。
  2. 避免对共享资源的多重占用。

Java中实现单例一般有饿汉式和懒汉式两种主要方式。其中懒汉式又有多种拓展做法,如双重检测式、静态内部类式、枚举式等。

饿汉式

指在对象没有被使用到的时候创建。写法如下:

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    public static getInstance() {
        return INSTANCE;
    }
    
    private Singleton() {
        
    }
}

这种方式利用了类加载过程是唯一的并且安全的特性,但这种方式也有以下的限制或弊端:

  1. 构造函数不能带参数,或者参数本身不需要外界上下文信息。
  2. 可以使用反射创建多个对象。
  3. 如果对象始终没有被使用到(类使用到了),则白白浪费了内存。

另外这种实现方法还有一种变体,就是在类静态代码块中实例化对象。

懒汉式

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) {
        
    }
}

这种写法的优点是在使用时才创建对象,避免了内存浪费,但也存在以下限制或弊端:

  1. 可以使用反射创建多个对象。
  2. 每次调用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) {
        
    }
}

这种实现方式只有当对象未实例化前会加锁,提高了访问效率,但也存在以下限制或弊端:

  1. 可以使用反射创建多个对象。

静态内部类

这种方法是饿汉式的优化,采用了和饿汉式相同的原理同时又保证不会浪费内存,具体实现如下:

public class Singleton {
    
    public static getInstance() {
        return Inner.INSTANCE;
    }
    
    private Singleton() {
        
    }
    
    private static class Inner {
        static final Singleton INSTANCE = new Singleton();
    }
}

这种方式有以下的限制或弊端:

  1. 构造函数不能带参数,或者参数本身不需要外界上下文信息。
  2. 可以使用反射创建多个对象。

枚举

使用枚举的好处是不能通过反射实例化对象,原因是枚举类实际上是一个抽象类,比如

public enum Singleton {}

相当于

public abstract class Singleton extends Enum {}

抽象类是不能被实例化,即使是用反射也一样。枚举的实现方法如下:

public enum Singleton {
    INSTANCE;
    
    private Singleton() {
        
    }
}

使用这种方法的缺陷是:

  1. 如果构造函数需要传入不是常量的参数则无法使用。

抽象类匿名子类

这种方式也是用来解决反射问题的,从枚举式中得到的灵感,网络上提到这种方式的文章较少。实现方法如下:

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) {

    }
}

这种方式完美解决了各种问题,这里又使用了懒汉式,也可以替换成其他实现方式。

上一篇下一篇

猜你喜欢

热点阅读