java设计模式中的单例模式是最为常见的一种之一,并且网上也有许多资料可以查阅,说说我对这个模式的认识与理解,以学习笔记的形式展示给大家,也方便自己以后对知识的回顾。
单例模式用一句话来说明就是 可以 让一个类的实例有且只有一个,控制了类的实例化的个数,使用这种设计模式,有效的避免了实例化对象时出现重复性,节省了内存空间又提高实例化对象的时间效率,由于整个程序使用的都是同一个实例对象,就可以更好的统一管理控制这个对象在程序中的状态。
下面就从单例模式的5种表现形式来说明:分别是 懒汉式 ,饿汉式,双重锁形式,静态内部类,枚举。
1.饿汉式实现代码如下:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton newInstance (){
return instance;
}
}
现在来说明一下为什么这么写以及这样实现单例的优缺点
为了实现一个类只能实例化一个对象,肯定需要将构造函数设置为私有的,来保证其他类不能实例化这个类,实例化变量instance设置为私有的,也是为了防止外部其他类对其进行访问,只能有本类提供的公有的静态方法返回给调用者,静态方法可以通过Singleton来调用,不能调用多少次都是同一个instance,由于Instances变量设置成了静态的,是属于类的,同时也体现出来一个‘饿’,类加载时就已经将这个实例创建出来了。
使用这个的好处:不会在多线程的情况下,出现创建多个实例的情况,因为这个类在加载后,这个实例就被创建出来了,也正是这个原因,、
不好之处:就是 我们在不使用这个实例的时候也会创建出来,造成了内存空间上的浪费。
再说一下使用这个懒汉式的场景:一般是在是实例时,内存开销比较小的,以及在初始化后需要用到该实例,这样的情况下,如果是实例化对象的内存开销大,在特殊场合需要用到就不适合用这个懒汉式,这时就需要用到饿汉式来解决这个问题。
2 懒汉式单例
public class Singleton1 {
private static Singleton1 instance = null;
private Singleton1(){}
public static synchronized Singleton1 getInstance(){
if(null== instance){
instance = new Singleton1();
}
return instance;
}
分析说明:由上可知,很多写法和饿汉式类似,就不一一解释了,现在我们主要理解好这个“懒”字,主要体现在,外部调用getInStances这个时,先判断有没有这个实例,没有的话,就创建出来,有了就直接返回,它不会在类加载后就创建出来,这就弥补了饿汉式的缺点,减小了内存的开销,由于它会出现多线程可能会实例化多个对象问题的出现就对获取实例的方法加了同步锁。在对某个单例使用的次数少,并且创建单例消耗的资源较多的情况下,可以考虑使用这种单例模式。
3.双重锁的形式
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
对于双重锁定的理解:
上面的加了锁的懒汉式的虽然解决了线程并发,延迟加载的问题,但是存在性能上的问题,多次调用getInstance(),消耗的性能会增加。 使用双重锁定,我们知道静态代码块中的内容大部分情况下是不会被执行到的,但是也有情况下,我们有2条线程同时判断了"instance==null" 的时候成立,这时如果我们进行第二次判断,就有可能出现实例化2个对象出来,而进行双重验证就避免了这个问题,这样写的第一个做“instance==null”判断就是进行了性能的优化,因为不用每次都去执行同步代码块里面的内容,提到了程序的性能,这是对懒汉式的缺点的弥补,同时我们要注意volatile对实例变量的修饰,valatile的一个语义是禁止指令重排序优化,也就保证了instance变量被赋值的时候对象已经是初始化过的现象。
4.静态内部类
public class Singleton{
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton newInstance(){
return SingletonHolder.instance;
}
}
分析说明:这种静态内部同样使用了类加载机制,和我们第一个说到的饿汉式一样,不会出现多线程的情况下出现不同的实例,保证了线程的安全,不同的是,通过静态的内部类,也实现了延迟加载,就是如果不用静态类去调用instance 去实例化该对象。因此这种单例模式用的比较对多。
5 枚举实现
public enum Singleton{
instance;
public void whateverMethod(){}
}
这种方式在实际的开发中很少用到,但是它能解决以上四种在使用单例模式的各种问题,主要包括
1)需要额外的工作来实现序列化,否则每次反序列化一个序列化的对象时都会创建一个新的实例。
2)可以使用反射强行调用私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。
而枚举类很好的解决了这两个问题,使用枚举除了线程安全和防止反射调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。
总结一下:在使用单例模式时,优先选用第三,四种,能过解决大部分的问题,懒汉式和饿汉式存在许多的问题,而最后一种枚举来实现,用的人很少,对于很多人来说很生疏。