第158天学习打卡(单例模式)
Posted doudoutj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第158天学习打卡(单例模式)相关的知识,希望对你有一定的参考价值。
什么是设计模式
设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
学习设计模式的意义
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
正确使用设计模式具有以下优点:
- 可以提高程序员的思维能力、编程能力和设计能力
- 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
- 使设计的代码可重用性高,可读性强,可靠性高,灵活性好,可维护性强
设计模式的基本要素
- 模式名称
- 问题
- 解决方案
- 效果
GoF 23
- 一种思维,一种态度,一种进步
创建型模式:
- 单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式
结构型模式:
- 适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
行为型模式:
- 模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式,访问者模式。
OOP(面向对象)七大原则
- 开闭原则:对扩展开放,对修改关闭
- 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立
- 依赖倒置原则:要面向接口编程,不要面向实现编程
- 单一职责原则:控制类的粒度大小,将对象解耦,提高其内聚性
- 接口隔离原则: 要为各个类建立它们需要的专用接口
- 迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话
- 合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
单例模式(构造器一定要私有)
饿汉式
package com.doudou.note.single;
//饿汉式单例
public class Hungry {
//可能会浪费空间
private byte[] date1 = new byte[1024*1024];
private byte[] date2 = new byte[1024*1024];
private byte[] date3 = new byte[1024*1024];
private byte[] date4 = new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式
package com.doudou.note.single;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
System.out.println(Thread.currentThread().getName() + "ok");
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan == null){
lazyMan = new LazyMan();
}
return lazyMan;
}
//单例模式下是ok的
//多线程并发 会出现问题
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
输出结果:
Thread-0ok
Thread-3ok
Thread-1ok
Thread-2ok
Process finished with exit code 0
DCL懒汉式
package com.doudou.note.single;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
System.out.println(Thread.currentThread().getName() + "ok");
}
private static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
//单例模式下是ok的
//多线程并发 会出现问题
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
输出结果:
Thread-0ok
Process finished with exit code 0
加入volatile实现双重指令重排和原子性操作
package com.doudou.note.single;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
System.out.println(Thread.currentThread().getName() + "ok");
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
}
静态内部类
package com.doudou.note.single;
//静态内部类
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
单例不安全,存在反射
反射可以破坏单例
package com.doudou.note.single;
import java.lang.reflect.Constructor;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
System.out.println(Thread.currentThread().getName() + "ok");
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
declaredConstructor.setAccessible(true);
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
输出结果:
mainok
mainok
com.doudou.note.single.LazyMan@14ae5a5
com.doudou.note.single.LazyMan@7f31245a
Process finished with exit code 0
破坏反射
package com.doudou.note.single;
import java.lang.reflect.Constructor;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
synchronized (LazyMan.class){
if (lazyMan!=null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
declaredConstructor.setAccessible(true);
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
输出结果
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.doudou.note.single.LazyMan.main(LazyMan.java:46)
Caused by: java.lang.RuntimeException: 不要试图使用反射破坏异常
at com.doudou.note.single.LazyMan.<init>(LazyMan.java:11)
... 5 more
Process finished with exit code 1
破坏单例模式
package com.doudou.note.single;
import java.lang.reflect.Constructor;
//懒汉式单例
public class LazyMan {
private LazyMan(){
//构造器私有
synchronized (LazyMan.class){
if (lazyMan!=null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
输出结果
com.doudou.note.single.LazyMan@14ae5a5
com.doudou.note.single.LazyMan@7f31245a
Process finished with exit code 0
破坏反射2
package com.doudou.note.single;
import java.lang.reflect.Constructor;
//懒汉式单例
public class LazyMan {
private static boolean qinjiang = false;
private LazyMan(){
//构造器私有
synchronized (LazyMan.class){
if (qinjiang == false){
qinjiang = true;
}else{
throw new RuntimeException("不要试图使用反射破话异常");
}
}
}
//volatile可以实现双重指令重排和实现原子性操作
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){//锁class只有一个
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
*
* 比如出现指令重排现象
* 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
*/
}
}
}
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
输出结果
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorI以上是关于第158天学习打卡(单例模式)的主要内容,如果未能解决你的问题,请参考以下文章
第128天学习打卡(Redis 哨兵模式 Redis缓存穿透和缓存击穿 雪崩)