单例模式

54 阅读3分钟

单例模式就是一个类只有一个。

单例模式必须确保只有自己能创建实例,提供一种访问其唯一对象的方法,就是众所周知的get方法。

其他类引用这个类的时候,可以直接访问,不需要实例化该类的对象

单例模式的最普遍的实现有两种:饿汉和懒汉

饿汉代码实现:

public class hungry {
    //利用private确保这个属性别人访问不了
    //利用static确保这个属性只有一个
    //final确保这个属性不能改变
    //直接new,自己实例化自己
    private static final hungry h = new hungry();
    //private修饰hungry构造器,保证别人无法访问
    private hungry(){}
    //一个public的get让别的类可以引用这个类,但不用实例化。
    public hungry getHungry(){
        return h;
    }
}

懒汉代码实现:

public class lazy {
    //利用private确保这个属性别人访问不了
    //利用static确保这个属性只有一个
    //先不实例化,这样确保了效率,就是说,如果这个类不用的话,那么就不需要实例化带来的开销。
    //volatile确保内存可见性
    private volatile static lazy lazy;
    //锁
    private static Object lock = new Object();
    //private的构造器,确保别的类无法实例化这个对象
    private lazy(){}
    //public的get方法
    public lazy getLazy(){
        //这个判断,是防止创建锁带来的开销
        if(lazy == null){
            //用这个锁来保证线程安全
            synchronized (lock){
                //这个是判断属性有没有被创建
                if(lazy == null){
                    lazy = new lazy();
                }
            }
        }
        return lazy;
    }
}

但是上面的懒加载是还能继续优化的,其中有开销的就是这个锁的问题。

懒汉优化代码:

public class lazyBetter {
	//利用内部类的特性,外部类加载的时候,内部类是不加载的。
    //private保证内部类无法被外部访问
    //static 保证这个类只有一个
    private static class lazyIn{
        //private保证只有内部类可以访问,其他类访问不了
        //static保证只有一个
        //final保证不被改变
        //当你需要用到这个类的时候直接用内部类实例化。这边的直接new并不是饿汉的意思
        private static final lazyBetter l = new lazyBetter();
    }
    //private的构造器,确保别的类无法实例化这个对象
    private lazyBetter(){}
    public lazyBetter getLazyButter(){
        return lazyIn.l;
    }
}

但是上面的还是有可以优化的地方

懒汉模式优化的优化:

public enum lazyBetterBetter {
    INSTANCE;
}

上面用到的枚举类可以防止反射。

单例模式的优缺点:

优点:

  1. 因为单例模式在内存中只有一个实例,减少了内存的开支,而且避免了一个类在频繁调用的时候带来的创建、销毁带来的性能消耗
  2. 避免对资源的重复利用
  3. 可以在系统设置全局的访问点,优化和共享资源访问。

缺点:

  1. 扩展困难,因为自我实例化带来的封闭,导致其他的类无法继承和实现
  2. 和单一职责原则有冲突。

单例模式的使用场景:

  • 要求生成唯一序列号的环境
  • 整个项目中需要一个共享访问点或共享数据,例如一个web页面上的计数器。
  • 创建一个对象需要消耗很多的资源
  • 需要定义大量的静态常量和静态方法的环境。

总结

注意

  • 单例类只能有一个实例
  • 单例类必须自己创建自己的唯一实例
  • 单例类必须是写给别的对象提供这一实例的方法