本文系作者原创,转载请注明:https://www.cnblogs.com/yanfei1819/p/10280671.html
一、要点:
1)某个类只能有一个实例:构造器私有化;
2)必须自行创建这个实例:含有一个该类的静态变量来保存这个唯一的实例;
3)必须自行向整个系统提供这个实例:对外提供获取该实例对象的方式:a.直接暴露;b.用静态变量的get方法获取。
二、有以下几种写法:
-
饿汉式(静态常量)
public class Singleton { private final static Singleton INSTANCE = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return INSTANCE; } }
-
饿汉式(静态代码块)
public class Singleton { private static Singleton instance; static { instance = new Singleton(); } private Singleton() {} public static Singleton getInstance() { return instance; } }
-
懒汉式(线程不安全)
public class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
-
懒汉式(线程安全,同步方法)
public class Singleton { private static Singleton singleton; private Singleton() {} public static synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
-
懒汉式(线程安全,同步代码块)
public class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { singleton = new Singleton(); } } return singleton; } }
-
双重检查
public class Singleton { private static volatile Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
-
静态内部类
这种方式既不用加锁,也实现了懒加载。
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
-
枚举
public enum Singleton { INSTANCE; public void whateverMethod() {} }
三、优点:
1) 减少内存开销,尤其是频繁的创建和销毁实例,提升系统性能;
2)避免对资源对过多占用。
四、缺点:
1)没有抽象层,不利于继承扩展;
2)违背了“单一职责原则”,一个类只重视内部关系,而忽略外部关系;
3)不适用于变化的对象;
4)滥用单例会出现一些负面问题,如为节省资源将数据库连接池对象设计为单例,可能会导致共享连接池对象对程序过多而出现连接池溢出。如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这样将导致对象状态丢失;
5)当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。
五、使用场合:
1)需要频繁的进行创建和销毁的对象;
2)创建对象时耗时过多或耗费资源过多,但又经常用到的对象;
3)工具类对象;
4)频繁访问数据库或文件的对象。