1、什么是单例模式。
确保某一个类只有一个实例,并且提供一个全局访问点。
简单来说,就是只能有一个实例。且该类需自行创建这个实例,并对其他类提供调用这一实例的方法。
它是Java中常用的设计模式。
- 特点
1.有且只有一个实例。
2.自我实例化。
3.提供全局访问方法。
- 优点
有且只有一个实例,节约了内存资源,提高了系统形同。
- 缺点
1.没有抽象层,不能扩展。
2.责任过重,违背了单一性原则。
吐槽:知识点吧,不记不背也不行,面试的时候考你要是说不上来,面上也过去。你看的烦躁,我写的也烦躁!啧啧啧!
2、单例设计的几种实现方式。
这个吧,几乎面试应该问到吧,我也好久没面试了,啧啧啧不太了解,有面试的小伙伴知道的可以评论和大家说说。
2.1、懒汉式
在第一次调用的时候才去实例化本身。
在并发条件下,可能出现多个实例化本身的对象,所以它是线程不安全
的。
吐槽:因为它本身就懒,和我一样,所以不是程序运行就实例化,而是每次调用的时候才去实例化。
- 线程不安全
package com.cp.xiaobaibai;
/**
* 描述: 单例模式——懒汉式——线程不安全
* 创建人:cp小白白
* 创建时间:2022/11/17 15:55
*/
public class SingletonLazyUnsafe {
/**
* 实例化,不懂得可以可以去网上查查
* 无参构造一般都是public,这里使用private,防止用户主动创建实例
* 你可以看看自己写的代码,无参构造是否是public(idea工具下alt+insert,选择Constructor)
*/
private SingletonLazyUnsafe() {
System.out.println("我是不安全滴懒汉式单例设计模式");
}
/**
* 这里不懂static的也去看一下基础吧。
*/
private static SingletonLazyUnsafe singletonLazyUnsafe = null;
/**
* 获取SingletonLazyUnsafe实例,也叫静态工厂方法(不知道哪期会讲,看更新速度吧,毕竟我也是小白,也要上班)
* getter方法就不多讲了。
* @return SingletonLazyUnsafe
*/
public static SingletonLazyUnsafe getSingletonLazyUnsafe() {
// singletonLazyUnsafe为空的时候创建它,反之直接返回,保证了唯一性
if (singletonLazyUnsafe == null) {
singletonLazyUnsafe = new SingletonLazyUnsafe();
}
return singletonLazyUnsafe;
}
}
测试类
package com.cp.xiaobaibai;
/**
* 描述: 单例模式测试类
* 创建人:cp小白白
* 创建时间:2022/11/17 17:17
*/
public class Singleton {
public static void main(String[] args) {
// 懒汉式不安全单例设计模式测试
SingletonLazyUnsafe singletonLazyUnsafe1 = SingletonLazyUnsafe.getSingletonLazyUnsafe();
SingletonLazyUnsafe singletonLazyUnsafe2 = SingletonLazyUnsafe.getSingletonLazyUnsafe();
System.out.println(singletonLazyUnsafe1 == singletonLazyUnsafe2);
System.out.println(singletonLazyUnsafe1);
System.out.println(singletonLazyUnsafe2);
}
}
运行结果
我是不安全滴懒汉式单例设计模式
true
com.cp.xiaobaibai.SingletonLazyUnsafe@1b6d3586
com.cp.xiaobaibai.SingletonLazyUnsafe@1b6d3586
- 线程安全
package com.cp.xiaobaibai;
/**
* 描述: 单例模式——懒汉式——线程安全
* 实际上就是在getSingletonLazySafe方法上加入synchronized的同步
* 创建人:cp小白白
* 创建时间:2022/11/17 16:49
*/
public class SingletonLazySafe {
private SingletonLazySafe() {
System.out.println("我是安全滴懒汉式单例设计模式");
}
private static SingletonLazySafe singletonLazySafe = null;
/**
* 获取SingletonLazySafe实例
* @return SingletonLazySafe
*/
public static synchronized SingletonLazySafe getSingletonLazySafe() {
if(singletonLazySafe == null) {
singletonLazySafe = new SingletonLazySafe();
}
return singletonLazySafe;
}
}
测试类
package com.cp.xiaobaibai;
/**
* 描述: 单例模式测试类
* 创建人:cp小白白
* 创建时间:2022/11/17 17:17
*/
public class Singleton {
public static void main(String[] args) {
// 懒汉式不安全安全单例设计模式测试
/* ...... */
// 懒汉式安全单例设计模式测试
SingletonLazySafe singletonLazySafe1 = SingletonLazySafe.getSingletonLazySafe();
SingletonLazySafe singletonLazySafe2 = SingletonLazySafe.getSingletonLazySafe();
System.out.println(singletonLazySafe1 == singletonLazySafe2);
System.out.println(singletonLazySafe1);
System.out.println(singletonLazySafe2);
}
}
运行结果
我是安全滴懒汉式单例设计模式
true
com.cp.xiaobaibai.SingletonLazySafe@1b6d3586
com.cp.xiaobaibai.SingletonLazySafe@1b6d3586
- 静态内部类
package com.cp.xiaobaibai;
/**
* 描述:单例模式——懒汉式——静态内部类
* 创建人:cp小白白
* 创建时间:2022/11/18 8:52
*/
public class SingletonStaticInner {
private SingletonStaticInner() {
System.out.println("我是静态内部类懒汉式单例设计模式");
}
private static class StaticInner {
// 这涉及static final变量的命名。规范,不懂得可以去搜一下。
private static final SingletonStaticInner SINGLETON_STATIC_INNER = new SingletonStaticInner();
}
public static final SingletonStaticInner getSingletonStaticInner() {
return StaticInner.SINGLETON_STATIC_INNER;
}
}
测试类
package com.cp.xiaobaibai;
/**
* 描述: 单例模式测试类
* 创建人:cp小白白
* 创建时间:2022/11/17 17:17
*/
public class Singleton {
public static void main(String[] args) {
// 懒汉式不安全安全单例设计模式测试
/* ...... */
// 懒汉式安全单例设计模式测试
/* ...... */
// 懒汉式静态内部类单例设计模式测试
SingletonStaticInner singletonStaticInner1 = SingletonStaticInner.getSingletonStaticInner();
SingletonStaticInner singletonStaticInner2 = SingletonStaticInner.getSingletonStaticInner();
System.out.println(singletonStaticInner1 == singletonStaticInner2);
System.out.println(singletonStaticInner1);
System.out.println(singletonStaticInner2);
}
}
运行结果
我是静态内部类懒汉式单例设计模式
true
com.cp.xiaobaibai.SingletonStaticInner@1b6d3586
com.cp.xiaobaibai.SingletonStaticInner@1b6d3586
- 双重检查锁定
package com.cp.xiaobaibai;
/**
* 描述:单例模式——懒汉式——双重检查锁定
* 创建人:cp小白白
* 创建时间:2022/11/18 9:00
*/
public class SingletonDoubleCheckLock {
private SingletonDoubleCheckLock() {
System.out.println("我是双重检查锁定懒汉式单例设计模式");
}
private volatile static SingletonDoubleCheckLock singletonDoubleCheckLock = null;
public static SingletonDoubleCheckLock getSingletonDoubleCheckLock() {
if (singletonDoubleCheckLock == null) {
synchronized (SingletonDoubleCheckLock.class) {
if (singletonDoubleCheckLock == null) {
singletonDoubleCheckLock = new SingletonDoubleCheckLock();
}
}
}
return singletonDoubleCheckLock;
}
}
测试类
package com.cp.xiaobaibai;
/**
* 描述: 单例模式测试类
* 创建人:cp小白白
* 创建时间:2022/11/17 17:17
*/
public class Singleton {
public static void main(String[] args) {
// 懒汉式不安全安全单例设计模式测试
/* ...... */
// 懒汉式安全单例设计模式测试
/* ...... */
// 懒汉式静态内部类单例设计模式测试
/* ...... */
// 懒汉式双重检查锁定单例设计模式测试
SingletonDoubleCheckLock singletonDoubleCheckLock1 = SingletonDoubleCheckLock.getSingletonDoubleCheckLock();
SingletonDoubleCheckLock singletonDoubleCheckLock2 = SingletonDoubleCheckLock.getSingletonDoubleCheckLock();
System.out.println(singletonDoubleCheckLock1 == singletonDoubleCheckLock2);
System.out.println(singletonDoubleCheckLock1);
System.out.println(singletonDoubleCheckLock2);
}
}
运行结果
我是双重检查锁定懒汉式单例设计模式
true
com.cp.xiaobaibai.SingletonDoubleCheckLock@1b6d3586
com.cp.xiaobaibai.SingletonDoubleCheckLock@1b6d3586
- 思考
(1)运行结果中为什么只输出类中的一次“我是…模式”,不是调用了两次吗?
(2)为什么我用的“==”而不用“.equals”?
(3)如果三次输出语句随机调换位置,输出语句会有什么变化?
(1)如果你对单例模式的定义很明白,那第一题你就很容易答出来。单例模式,
重点在单例,有且只有一个实例。
不管你调用几次,只要它实例化了,就会在堆中分配的内存空间中,只有程序终止后才会被释放。
(2)“==”和“.equals”的区别大家要是知道的话,这里应该很容易了解。还是那个重点,单例,有且只有一个实例。
我要比较地址值是否一样,来确定它实例化了几次。
(3)无论输出怎么变化,唯一不变的就是类中的那句输出“我是...模式”,
地址值和比较结果会随着输出语句变化二变化。
- 进阶
(1)既然说到线程不安全,为什么会线程不安全,来看一下例子。
package com.cp.xiaobaibai;
/**
* 描述: 单例模式进阶测试类
* 创建人:cp小白白
* 创建时间:2022/11/18 10:28
*/
public class SingletonSenior {
public static void main(String[] args) {
// 多线程下检验懒汉式不安全单例设计模式
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(SingletonLazyUnsafe.getSingletonLazyUnsafe());
}).start();
}
}
}
输出结果
我是不安全滴懒汉式单例设计模式
我是不安全滴懒汉式单例设计模式
我是不安全滴懒汉式单例设计模式
我是不安全滴懒汉式单例设计模式
我是不安全滴懒汉式单例设计模式
com.cp.xiaobaibai.SingletonLazyUnsafe@46a00ba1
com.cp.xiaobaibai.SingletonLazyUnsafe@72bb9f6e
com.cp.xiaobaibai.SingletonLazyUnsafe@39ae8a9b
com.cp.xiaobaibai.SingletonLazyUnsafe@d166de5
com.cp.xiaobaibai.SingletonLazyUnsafe@1acc1ca0
可以很明显的看出,类被实例化了好几次。违背了有且只有一个的实例的原则。所以在多线程的环境下是不安全的。也可以增大次数,看看效果。
(2)怎么解决线程不安全问题呢,加锁呗。双重检查锁定机制不就来了吗,但是,双重检查锁定机制是不是锁太重了,每次访问过来时候都需要同步,太过于影响性能了,消耗了太多的资源。有的帖子说它高并发安全,高效,个人还是不太推荐使用,锁太重了。
(3)静态内部类的衍生就来了,既能解决了多线程安全问题,还能解决同步带来的性能影响。
2.2、饿汉式
在类加载时,就立即实例化,并且创建单例对象。
因为其在线程还没出现前就已经实例化了,所以它是线程安全
滴。
吐槽:目前还没理解它为什么叫饿汉式,这个饿的定义是对谁定义的。
工作中。。。周末有时间再写。。。公司电脑被设置权限了,不能复制了,暴风哭泣
2.3、登记式
通过一个类专门的类对各单例模式的单一实例进行管理和维护。
吐槽:这个登记式吧,我没有找到特别好的定义。我也是第一次了解这个,可能以前也看到过,但是印象不深。有大佬知道的,欢迎私信或者评论,被太学术了,大白话一点好。
工作中。。。周末有时间再写。。。公司电脑被设置权限了,不能复制了,暴风哭泣
3、总结
单例模式是Java设计模式中比较简单的一种,既然存在就有存在的意义。培训机构也会对它进行一些讲解,可见它也是很重要的。
设计模式吗,设计,往往就是一种思维惯性。思维思维,人也有主观和客观之分,代码也是人写的,不是吗。
4、建议
我的文章看看就好,别深究,要有自己的理解,每个人都有自己的理解,无论对错,不要把别人的理解当成你的理解,别人的理解可以作为参考,但一定要对知识有自己的看法。
要多写代码,看理论知识点,永远是看。一看都会,一写全不会,没事多写写,熟悉熟悉它。
人非圣贤,孰能无惑!