package com.jan.single; //饿汉式单例 public class Hungry { //一上来就会加载好,可能会浪费空间 private byte[] data11=new byte[1024*1024]; private byte[] data12=new byte[1024*1024]; private byte[] data13=new byte[1024*1024]; private byte[] data14=new byte[1024*1024]; private Hungry(){ } private final static Hungry HUNGRY = new Hungry();//保证是唯一的了 public static Hungry getInstance(){ //一上来就加载了 return HUNGRY; } }
package com.jan.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; } //多线程并发 public static void main(String[] args){ for (int i = 0; i < 10; i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } } }
此时运行结果:单例有问题,偶尔会有多线程
package com.jan.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.jan.single; //懒汉式单例 public class LazyMan { private LazyMan(){ ///构造器私有 System.out.println(Thread.currentThread().getName()+"ok"); } //为了安全,避免 lazyMan 指令重排,必须加volatile private volatile static LazyMan lazyMan;//对象 public static LazyMan getInstance(){ //这代码安全吗? //双重检测锁模式的 懒汉式单例 DCL模式 if (lazyMan==null){ synchronized (LazyMan.class){ //保证这个类 LazyMan.class 只有一个 if(lazyMan==null){ lazyMan = new LazyMan(); //不是原子性操作 /* * 1. 分配内存空间 * 2. 执行构造方法,初始化对象 * 3. 把这个对象指向这个空间 * * 期望执行 123 * 132 A(先占内存空间) * 若此时有 B线程 基于A,系统会认为 lazyMan 不为Null 直接return * //此时lazyMan没有完成构造 * */ } } } return lazyMan; } //多线程并发 public static void main(String[] args){ for (int i = 0; i < 10; i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } } }
单例不安全,反射
枚举
package com.jan.single; import java.lang.reflect.Constructor; //懒汉式单例 public class LazyMan { private LazyMan(){ ///构造器私有 System.out.println(Thread.currentThread().getName()+"ok"); } //为了安全,避免 lazyMan 指令重排,必须加volatile private volatile static LazyMan lazyMan;//对象 public static LazyMan getInstance(){ //这代码安全吗? //双重检测锁模式的 懒汉式单例 DCL模式 if (lazyMan==null){ synchronized (LazyMan.class){ //保证这个类 LazyMan.class 只有一个 if(lazyMan==null){ lazyMan = new LazyMan(); //不是原子性操作 } } } return lazyMan; } //反射 public static void main(String[] args) throws Exception { LazyMan instance1 = LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器 declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器 通过反射来创建对象 LazyMan instance2= declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }
结果:
package com.jan.single; import java.lang.reflect.Constructor; //懒汉式单例 public class LazyMan { private LazyMan(){ ///构造器私有 synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错 if (lazyMan!=null){ throw new RuntimeException("不要试图使用反射破坏异常"); } } } //为了安全,避免 lazyMan 指令重排,必须加volatile private volatile static LazyMan lazyMan;//对象 public static LazyMan getInstance(){ //这代码安全吗? //双重检测锁模式的 懒汉式单例 DCL模式 if (lazyMan==null){ synchronized (LazyMan.class){ //保证这个类 LazyMan.class 只有一个 if(lazyMan==null){ lazyMan = new LazyMan(); //不是原子性操作 /* * 1. 分配内存空间 * 2. 执行构造方法,初始化对象 * 3. 把这个对象指向这个空间 * * 期望执行 123 * 132 A(先占内存空间) * 若此时有 B线程 基于A,系统会认为 lazyMan 不为Null 直接return * //此时lazyMan没有完成构造 * */ } } } return lazyMan; } //反射 public static void main(String[] args) throws Exception { LazyMan instance1 = LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器 declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器 通过反射来创建对象 LazyMan instance2= declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }
结果:
如果两个对象都是LazyMan instance2= declaredConstructor.newInstance();
这样获得,单例又被破坏
package com.jan.single; import java.lang.reflect.Constructor; //懒汉式单例 public class LazyMan { private static boolean jan = false;//定义一个变量 private LazyMan(){ ///构造器私有 synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错 if (jan==false){ jan = true; //这个肯定会变 }else { throw new RuntimeException("不要试图使用反射破坏异常"); } } } //为了安全,避免 lazyMan 指令重排,必须加volatile private volatile static LazyMan lazyMan;//对象 public static LazyMan getInstance(){ //这代码安全吗? //双重检测锁模式的 懒汉式单例 DCL模式 if (lazyMan==null){ synchronized (LazyMan.class){ //保证这个类 LazyMan.class 只有一个 if(lazyMan==null){ lazyMan = new LazyMan(); //不是原子性操作 } } } return lazyMan; } //反射 public static void main(String[] args) throws Exception { //LazyMan instance1 = LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器 declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器 通过反射来创建对象 LazyMan instance2= declaredConstructor.newInstance(); LazyMan instance1= declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }
package com.jan.single; import java.lang.reflect.Constructor; import java.lang.reflect.Field; //懒汉式单例 //道高一尺魔高一丈 public class LazyMan { private static boolean jan = false;//定义一个变量 private LazyMan(){ ///构造器私有 synchronized (LazyMan.class){ //这里加一把锁,第二次用就会报错 if (jan==false){ jan = true; //这个肯定会变 }else { throw new RuntimeException("不要试图使用反射破坏异常"); } } } //为了安全,避免 lazyMan 指令重排,必须加volatile private volatile static LazyMan lazyMan;//对象 public static LazyMan getInstance(){ //这代码安全吗? //双重检测锁模式的 懒汉式单例 DCL模式 if (lazyMan==null){ synchronized (LazyMan.class){ //保证这个类 LazyMan.class 只有一个 if(lazyMan==null){ lazyMan = new LazyMan(); //不是原子性操作 } } } return lazyMan; } //反射 public static void main(String[] args) throws Exception { //LazyMan instance1 = Field jan = LazyMan.class.getDeclaredField("jan");//获取字段 jan.setAccessible(true);//破坏私有权限 Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器 declaredConstructor.setAccessible(true);//破坏私有权限,无视私有的构造器 通过反射来创建对象 LazyMan instance2= declaredConstructor.newInstance(); jan.set(instance2,false); LazyMan instance1= declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }
结果:
反编译jad:https://varaneckas.com/jad/
package com.jan.single; //枚举 是一个什么? 本身也是class 类 public enum EnumSingle { INSTANCE;//不可能被拿走 public EnumSingle getInstance(){ return INSTANCE; } } class Test{ //先保证对象是唯一的,测试一下 public static void main(String[] args) { EnumSingle instance1= EnumSingle.INSTANCE; EnumSingle instance2= EnumSingle.INSTANCE; System.out.println(instance1); System.out.println(instance2); } }
结果:
package com.jan.single; import java.lang.reflect.Constructor; //枚举 是一个什么? 本身也是class 类 public enum EnumSingle { INSTANCE;//不可能被拿走 public EnumSingle getInstance(){ return INSTANCE; } } class Test{ //试一试能不能破坏,看源代码 有一个无参构造 public static void main(String[] args) throws Exception { EnumSingle instance1= EnumSingle.INSTANCE; //有一个无参构造,试一试,看看是不是真的 Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); EnumSingle instance2 = declaredConstructor.newInstance(); //com.jan.single.EnumSingle.<init>() //(newInstance 源码)正常报错是:throw new IllegalArgumentException("Cannot reflectively create enum objects"); System.out.println(instance1); System.out.println(instance2); } }
结果:被欺骗,这源码不是真的
因为newinstance的源码:
两个输出的结果不同!!!
在IDEA的targer中找到 EnumSingle.class,打开文件的文件的位置,再从文件的卫视输入 cmd
也有空参,也是骗我们的
jad
这里是有参构造
修改代码,这里只展示不同的
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
结果:这个反射的确不能破坏枚举的单例
枚举最终反编译源码
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: EnumSingle.java package com.jan.single; public final class EnumSingle extends Enum { public static EnumSingle[] values() { return (EnumSingle[])$VALUES.clone(); } public static EnumSingle valueOf(String name) { return (EnumSingle)Enum.valueOf(com/jan/single/EnumSingle, name); } private EnumSingle(String s, int i) { super(s, i); } public EnumSingle getInstance() { return INSTANCE; } public static final EnumSingle INSTANCE; private static final EnumSingle $VALUES[]; static { INSTANCE = new EnumSingle("INSTANCE", 0); $VALUES = (new EnumSingle[] { INSTANCE }); } }