单例模式的核心竞争力在于:保证了一个类只有一个实例,节省系统资源。
单例模式的应用场景在于:当我们创建一个类的实例的时候要消耗很多的系统资源,或者我们要通过读取配置文件来获得一个类的实例的时候,我们就可以使用单例模式,这里可以减少每次重新创建一个新对象的资源消耗。比如我们javaee中的servlet编程中,Application和每一个servlet类都是单例的,我们在数据库连接池的设计也是一般采用单例模式,spring中每个bean默认是单例的,springMVC框架中的控制器对象等等。
单例模式的实现方式:
1.饿汉式:一上来就创建静态属性指向一个实例对象的引用,私有化构造函数,提供一个供外界获得对象实例引用的接口。代码如下:
public class SingletonDemo1 implements Serializable{
private static final SingletonDemo1 INSTANCE = new SingletonDemo1();//类一初始化,就获得一个对象引用
private SingletonDemo1() { };//核心点:私有化构造函数
public static SingletonDemo1 getInstance() { //外界获得对象引用的接口
return INSTANCE;
}
}
其优点在于:线程安全(并发问题是在于程序员访问时候发生,类加载静态方法和静态属性时,没有程序员可以访问,天然的优势),调用效率高;
其缺点在于:类一初始化就创建一个对象,但是程序员可能只是通过类来调用其他静态方法,根本不需要改对象,造成内存浪费。也就是说:不能延时加载对象。
2.懒汉式:针对饿汉式的不能延时加载,进行修改,提供了延时加载对象的功能。代码如下:
public class SingletonDemo2 {
private static SingletonDemo2 instance ;
private SingletonDemo2(){};
public static synchronized SingletonDemo2 getInstance(){
if(instance==null){
instance = new SingletonDemo2();
}
return instance;
}
}
使用了synchronized来锁住这个方法的访问对象,可避免同步并发的问题。
其优点在于:提供延时加载,可以避免内存浪费,线程也是安全的;
其缺点在于:引入锁,导致效率会降低。
3.使用静态内部类:通过jvm的在初始化类时不会自动加载静态内部类的特性,也可以实现天然的线程安全。代码如下:
public class SingletonDemo3 {
private static class Single{
private static SingletonDemo3 instance = new SingletonDemo3();
} ;
private SingletonDemo3() {
};
public static SingletonDemo3 getInstance() {
return Single.instance;
}
}
其优势在于:提供延时加载,能避免内存的浪费,在实际工作中一般采用这种方式。
以上3种实现单例的方式都有一个共同的特点:私有化构造函数。这样也暴露一个问题:通过反射和反序列化强行创建新的对象实例。所以我们介绍下第四种
4.枚举实现单例:利用枚举本身的天然特性--枚举就是单例模式,而枚举由于jvm根本上提供保障,可以避免反射和反序列化的漏洞。代码如下:
public enum SingletonDemo4 {
INSTANCE;
}
单例模式的使用小结:
如果类创建实例的资源消耗多,那么就需要延时加载,静态内部类方式由于懒汉式;如果不需要延时加载,枚举优于饿汉式。