1.单例模式介绍
单例模式( Singleton Pattern )是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。单例模式在现实生活中应用也非常广泛,例如,总统,班主任等。J2EE标准中的ServletContext 、ServletContextConfig 等、Spring框架应用中的。
特点:构造方法私有,提供一个全局访问点。
实现方式:有很多,1.饿汉式 2.懒汉式 3.注册式 4.ThreadLocal
优点:内存中只有一个实例,减少内存开销;避免对资源多重占用;设置全局访问点,严格控制访问。
缺点:没有接口,扩展困难;如果要扩展单例对象,只有修改代码,没有其他途径,不符合程序的开闭原则。
2.单例模式代码实现具体思路
1、确保对象实例只有一个
只对类进行一次实例化,所有人都只能使用这个第一次实例化的对象。2、构造方法私有化
保证外界不能通过new来创建一个新对象,而是只能使用上面第一次实例化的对象。3、提供静态方法供外界来使用这个唯一的对象实例
因为外界不能new新对象了,所以我们必须提供一个类方法才能让外界访问到这个唯一的对象实例。
3.尝试代码实现
我们现在来自己写一些试一下。
1.饿汉式
如图所示,我们按照三个步骤做出了一个饿汉式的单例模式,那么我们现在测试一下。
首先,新建一个类写一个main方法。
我们来想想,以前实例化一个对象是怎么做的呢?
类型 变量名 = new 类的构造器
但是我们尝试发现,会直接报错,这是因为我们的private访问权限。
所以我们想到刚才的步骤三,提供了一个“接口”。
我们用刚才的接口来实例化两个对象,然后调用了一下方法,看看,会发生什么呢。
说明两个对象都实例化成功了。这时候我们思考,真的是实例化了两个吗?我们是不是应该看一下这俩玩意是不是同一个呢?(毕竟这次学的是单例模式嘛)
我们这里发现,他们2者是相等的!
饿汉式单例模式在类加载的时候就立即初始化,并且创建单例对象。它绝对线程安全,在线程还没出现以前就实例化了,不可能存在访问安全问题。
总结:
final:防止反射破坏单例。
饿汉式缺点:可能会造成内存空间的浪费。
饿汉式单例模式适用于单例对象较少的情况。这样写可以保证绝对线程安全、执行效率比较高。但是它的缺点也很明显,就是所有对象类加载的时候就实例化。这样一来,如果系统中有大批量的单例对象存在,那系统初始化是就会导致大量的内存浪费。
在类装载时就创建了实例,可能导致资源浪费,因为过早地创建了实例,这个实例可能压根就用不到,最好时是什么时候需要什么时候创建,也就是懒加载。
2.懒汉式
懒汉式之所以叫懒汉式,是因为只有用到他的时候才会去new对象来实例化。
我们还是直接用上次的main方法
可以发现,创建成功啦,并且两个所谓的对象。
从上述代码可以看到,懒汉式是在需要实例的时候才进行的实例化,这样虽然实现了懒加载,但是这样会出现线程安全的问题,试想如果在多线程场景下,一个线程进入了if(s == null)语句,但未完成实例化,同时另一个线程也到了if (s == null)语句,因为之前未完成实例化,所以第二个线程也会进入到if语句,这样就创建了多个实例。
优点:实现了懒加载,需要时才创建实例。
缺点:多线程场景下存在线程安全问题,只能在单线程场景中使用。
四、单例模式优缺点
优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
注意:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。