14.单例模,枚举
单例模式
饿汉式
/*
* 饿汉式单例
* 如果这个实例从未被使用,会造成内存浪费
* */
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
* volatile 避免指令重排
* 双重保证多线程下对象只有一个
* */
public class LazyMan {
private volatile static LazyMan lazyMan;
private LazyMan() {
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();
}
}
}
静态内部类
//静态内部类 懒汉式
public class Holder {
private Holder() {
}
public static Holder getInstance() {
return InnerClass.HOLDER;
}
public static class InnerClass {
private static final Holder HOLDER = new Holder();
}
}
升级版
/*
* 双重检测锁 懒汉式 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);
}
}
枚举 自带单例
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance() {
return INSTANCE;
}
}
class Test3 {
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
//1.
//查看EnumSingle的class文件,发现有一个无参构造
//用反射去破解,但是异常了,根本没有无参构造,
// 用javap命令发现也是有无参构造的,
// 再用jad.exe把源码变成java文件,发现根本不是无参构造,而是有参 EnumSingle(String s,int i){}
/* Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
EnumSingle instance2 = constructor.newInstance();
System.out.println(instance2);//NoSuchMethodException*/
//2.再次测试,成功了
Constructor<EnumSingle> constructor1 = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
constructor1.setAccessible(true);
EnumSingle instance3 = constructor1.newInstance();
System.out.println(instance1);
System.out.println(instance3);//IllegalArgumentException: Cannot reflectively create enum objects
}
}
【推荐】国内首个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