彻底玩转单例模式
Posted 偶像java练习生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了彻底玩转单例模式相关的知识,希望对你有一定的参考价值。
单例模式
饿汉式,DCL 懒汉式,深究!
饿汉式
package com.sigle;
//饿汉式单例: 缺点浪费内存
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;
}
}
类初始化的时候就创建对象
DCL 懒汉式
package com.sigle;
//懒汉式单例模式
//道高一尺魔高一丈
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* 并发情况下,是有问题的,会创建多个对象
*/
public class LazyMan {
private static boolean qingjiang =false;
//三重检测
private LazyMan(){
synchronized (LazyMan.class){
// if(qingjiang ==false){
// qingjiang =true;
// }else{
// throw new RuntimeException("不要试图使用反射破坏异常");
// }
if(lazyMan !=null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
//增加volatile 关键字,防止指令重排,即代码只会初始化一个lazyman 对象
private volatile static LazyMan lazyMan;
//双重检测锁模式的懒汉式单例 ,DCL 懒汉式
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
/**
* 1. 分配内存空间
* 2. 执行构造方法,初始化对象
* 3. 把这个对象指向这个空间
* 期望按照 123 走
* 可能指令重排按132走 ,先占用空间,再把内存对象放进去 A
* 此时一个线程 B 来了,他看到这个空间已经被占用了,认为lazyman 已经初始化了,他可能直接return 了
*/
}
}
}
return lazyMan;// 此时lazyMan 还没有完成构造
}
// 多线程并发
// 反射! 只要有反射,任何代码都不安全,反射可以破坏单例,如下面代码操作
public static void main(String[] args) throws Exception {
// for (int i = 0; i < 10; i++) {
// new Thread(() ->{
// LazyMan.getInstance();
// }).start();
// }
Field qingjiang = LazyMan.class.getDeclaredField("qingjiang");
qingjiang.setAccessible(true);
// LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
//无视私有构造器
declaredConstructor.setAccessible(true);
LazyMan instance2 = declaredConstructor.newInstance();
qingjiang.set(instance2,false);
LazyMan instance3 = declaredConstructor.newInstance();
// qingjiang.set(instance,false);
// LazyMan instance2 = declaredConstructor.newInstance();
//System.out.println(instance);
System.out.println(instance3);
System.out.println(instance2);
}
}
静态类不类
package com.sigle;
//静态内部类
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.sigle;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
// enum 是一个什么,本身也是一个Class 类
//反射不能破坏枚举
public enum EnumSingle {
Instance;
public EnumSingle getInstance(){
return Instance;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle enumSingle1 = EnumSingle.Instance;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
//私有权限破除
declaredConstructor.setAccessible(true);
//Exception in thread "main" java.lang.NoSuchMethodException: com.sigle.EnumSingle.<init>() ,枚举中是有参构造器
//Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects// 反射不能破解枚举的单例
EnumSingle enumSingle2 = declaredConstructor.newInstance();
System.out.println(enumSingle1);
System.out.println(enumSingle2);
// EnumSingle enumSingle2 = EnumSingle.Instance;
// System.out.println(enumSingle1);
// System.out.println(enumSingle2);
}
}
javap -EnumSingle 文件,反编译后的文件发现还真有个无参构造器
但是报错还是没有无参的构造器,然后通过专业的jda 反编译工具编译后是这样的:
以上是关于彻底玩转单例模式的主要内容,如果未能解决你的问题,请参考以下文章