单例模式就是一个类只有一个。
单例模式必须确保只有自己能创建实例,提供一种访问其唯一对象的方法,就是众所周知的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;
}
上面用到的枚举类可以防止反射。
单例模式的优缺点:
优点:
- 因为单例模式在内存中只有一个实例,减少了内存的开支,而且避免了一个类在频繁调用的时候带来的创建、销毁带来的性能消耗
- 避免对资源的重复利用
- 可以在系统设置全局的访问点,优化和共享资源访问。
缺点:
- 扩展困难,因为自我实例化带来的封闭,导致其他的类无法继承和实现
- 和单一职责原则有冲突。
单例模式的使用场景:
- 要求生成唯一序列号的环境
- 整个项目中需要一个共享访问点或共享数据,例如一个web页面上的计数器。
- 创建一个对象需要消耗很多的资源
- 需要定义大量的静态常量和静态方法的环境。
总结
注意
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须是写给别的对象提供这一实例的方法