设计模式概述,单例模式
概述
OOP七大原则
- 开闭原则: 对拓展开放,对修改关闭
- 里氏替换原则: 继承必须保证超类所拥有的性质在子类中仍然成立
- 依赖倒置原则: 要面向接口编程,不要面向现实
- 单一职责原则: 控制类的粒度大小,将对象解耦,提高内聚
- 接口隔离原则: 要用各个类建立他们需要的专用接口
- 迪米特法则: 只与直接朋友交谈,不与“陌生人”通信
- 合成复用原则: 尽量先使用组合或者内聚等关联关系来实现,其次才考虑使用继承来实现
分类
创建型:单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式
结构型:适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
行为型:其他
单例模式
场景: 数据库连接池,多线程,Runtime类,Spring中的Bean
饿汉式
/*
* 饿汉式单例
* 如果这个实例从未被使用,会造成内存浪费
* */
public class HungryMan {
private final static HungryMan hungryMan = new HungryMan();
private HungryMan() {
//System.out.println(Thread.currentThread().getName()); 只会执行一次
}
public static HungryMan getInstance() {
return hungryMan;
}
}
class Test1 {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
new Thread(()->{
HungryMan instance = HungryMan.getInstance();
//System.out.println(instance);//每个对象都相等
}).start();
}
/*HungryMan instance1 = HungryMan.getInstance();
HungryMan instance2 = HungryMan.getInstance();
System.out.println(instance1 == instance2);*/
}
}
懒汉式
双重检测锁DCL,反射不安全
/*
* 双重检测锁 懒汉式 DCL
* */
public class LazyMan {
private volatile static LazyMan lazyMan;
private static boolean flag = false;
//构造方法中也加锁
private LazyMan() {
synchronized (LazyMan.class) {
if (!flag) {
flag = true;
} else {
throw new RuntimeException("不要试图使用反射破坏,异常");
}
/*if (lazyMan != null) {
throw new RuntimeException("不要试图使用反射破坏,异常");
}*/
}
System.out.println(Thread.currentThread().getName());
}
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();//不是原子操作
/*
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
* 期待的是123
* A线程由于指令重排变成了 132
* B线程 进来,发现lazyMan!=null 就直接返回了,就有问题了,此时lazyMan还没有完成构造
* 结局:volatile避免指令重排
* */
}
}
}
return lazyMan;
}
}
class Test2 {
public static void main(String[] args) throws Exception {
/*for (int i = 1; i <= 10; i++) {
new Thread(() -> {
LazyMan instance = LazyMan.getInstance();
//System.out.println(instance);
}).start();
}*/
//反射破解DCL
//解决:构造方法中也加锁
/* LazyMan instance1 = LazyMan.getInstance();
Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
LazyMan instance2 = constructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);//instance1和instance2不是一个对象*/
//再次破解,只通过反射创建对象,结果又成功了
//解决:信号灯
/*Constructor<LazyMan> constructor1 = LazyMan.class.getDeclaredConstructor(null);
constructor1.setAccessible(true);
LazyMan instance3 = constructor1.newInstance();
LazyMan instance4 = constructor1.newInstance();
System.out.println(instance3);
System.out.println(instance4);*/
//再次破击,前提知道了你的信号灯是谁
Field flag = LazyMan.class.getDeclaredField("flag");
flag.setAccessible(true);
Constructor<LazyMan> constructor5 = LazyMan.class.getDeclaredConstructor(null);
constructor5.setAccessible(true);
LazyMan instance5 = constructor5.newInstance();
flag.set(instance5, false);//重新设置信号灯,
LazyMan instance6 = constructor5.newInstance();
System.out.println(instance5);
System.out.println(instance6);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY