设计模式(一)__单例设计模式
先来介绍一下什么是设计模式,设计模式起先是从建筑行业借鉴来的,然后慢慢发展到了今天这个样子。
设计模式是解决问题最行之有效的思想,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
java有23种设计模式,今天就先来介绍一种:单例设计模式
单例设计模式:
有时候当你需要保证一个类在内存中的对象唯一性的时候,比如多程序读取一个配置文件时,建议配置文件封装成对象。会方便操作其中数据,又要保证多个程序读到的是同一个配置文件对象,就需要该配置文件对象在内存中是唯一的。那么这时候你就需要用到单例设计模式了。
那么,如何保证对象的唯一性呢?
思想:
- 不让其他类创建该类对象。
- 在本类中创建一个本类对象
- 对外提供方法,让其他程序获取这个对象
步骤:
1. 因为创建对象都需要构造函数初始化,所以只需要将本类中的构造函数私有化,这样其他类就不能创建该类对象了。
2. 在本类中创建一个本类对象
3. 定义一个公有方法,返回该对象,让其他类访问。(作用:可控)
好了,上面就把单例模式的思路给大家说了下,单例模式的写法有好几种,这里主要介绍两种:懒汉式单例、饿汉式单例。
一:饿汉式
class Single { private Single() {} // 私有化构造函数。 private static final Single s = new Single(); // 创建私有并静态的本类对象。 public static Single getInstance() { // 定义公有并静态的方法,返回该对象。 return s; } }
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
二:懒汉式
class Single { private Single() {} private static Single s = null; public static Single getInstance() { if (s == null) s = new Single(); return s; } }
但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Single实例,要实现线程安全,可通过多判断加同步来解决。
class Single { private Single() {}; private static Single s = null; public static Single getInstance() { if (s == null) { synchronized (Single.class) { if (s == null) { s = new Single(); } } } return s; } }
--------------------------------------------------------------------------------------------
双重锁检查只能在jdk1.5以后才能有效,1.5以前在java对象模型中的无序写问题不能保证。
查阅了一下资料,发现还可以通过静态内部类来解决延迟加载的安全问题。
class Single { private Single() {}; private static class Holder { private static final Single INSTANCE = new Single(); } public static Single getInstance() { return Holder.INSTANCE; } }
这种方式既实现了线程安全,又避免了同步带来的性能影响
那么为什么这样可以实现单例呢?
先来介绍一下内部类:
内部类分为对象级别和类级别,类级内部类指的是,有static修饰的成员变量的内部类。如果没有static修饰的成员变量的内部类被称为对象级内部类。
类级内部类相当于其外部类的static成员,它的对象与外部类对象间不存在依赖关系,相互独立,因此可直接创建。而对象级内部类的实例,是必须绑定在外部对象实例上的。类级内部类只有在第一次被使用的时候才被会装载。
当getInstance方法第一次被调用的时候,它第一次读取Holder.instance,内部类Holder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Single的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
个人推荐此种方式。
--------------------------------------------------------------------------------------------
三:饿汉式和懒汉式区别
①
饿汉比较急,类一旦加载,就把单例初始化完成。
懒汉太懒了,只有当调用getInstance的时候,才会去初始化这个单例。
②
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题。
懒汉式本身是非线程安全的。
③
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,而且因为线程安全问题,需要加同步,效率上比较低。
总之,从速度和反应时间角度来讲,饿汉式好;从资源利用效率上说,延迟加载(又称懒汉式)好。