DCL懒汉式单例反破解攻坚战 (๑•̀ㅂ•́)و✧ DCL懒汉式单例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class LazyMan { private volatile static LazyMan lazyMan; public static LazyMan getInstance () { if (lazyMan==null ){ synchronized (LazyMan.class){ if (lazyMan==null ){ lazyMan = new LazyMan (); } } } return lazyMan; } private LazyMan () { } }
通过反射破解DCL懒汉式单例 1 2 3 4 5 6 7 8 9 10 11 12 13 public class Crack1 { public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { LazyManV2 lazyManV2_1 = LazyManV2.getInstance(); Class c1 = LazyManV2.class; Constructor constructor = c1.getDeclaredConstructor(); constructor.setAccessible(true ); LazyManV2 lazyManV2_2 = (LazyManV2) constructor.newInstance(); System.out.println(lazyManV2_1); System.out.println(lazyManV2_2); } }
1 2 3 4 5 6 main线程创建了一个DCL LazyMan main线程创建了一个DCL LazyMan com.yn.Demo31_懒汉式单例破解攻坚战.LazyManV2@1b6d3586 com.yn.Demo31_懒汉式单例破解攻坚战.LazyManV2@4554617c Process finished with exit code 0
成功创建两个不同的单例。
DCL懒汉式单例2.0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class LazyManV3 { private static LazyManV3 lazyMan; public static LazyManV3 getInstance () { if (lazyMan==null ){ synchronized (LazyManV3.class){ if (lazyMan==null ){ lazyMan = new LazyManV3 (); } } } return lazyMan; } private LazyManV3 () { synchronized (LazyManV3.class){ if (lazyMan!=null ){ throw new RuntimeException ("不要试图使用反射破坏异常" ); } } System.out.println(Thread.currentThread().getName()+ "线程创建了一个DCL LazyMan" ); } }
这里在构造方法中添加了语句,在getInstance()方法创建对象后,禁止通过反射方式再次创建对象。
使用getInstance()创建对象后,再次使用反射方式创建对象会抛出异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 main线程创建了一个DCL LazyMan 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.yn.Demo31_懒汉式单例破解攻坚战.Crack2.main(Crack2.java:13) Caused by: java.lang.RuntimeException: 不要试图使用反射破坏异常 at com.yn.Demo31_懒汉式单例破解攻坚战.LazyManV3.<init>(LazyManV3.java:24) ... 5 more Process finished with exit code 1
通过反射破解DCL懒汉式单例2.0 道高一尺,魔高一丈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Crack2 { public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class c1 = LazyManV3.class; Constructor constructor = c1.getDeclaredConstructor(); constructor.setAccessible(true ); LazyManV3 lazyMan_2 = (LazyManV3) constructor.newInstance(); LazyManV3 lazyMan_1 = (LazyManV3) constructor.newInstance(); System.out.println(lazyMan_1); System.out.println(lazyMan_2); } }
两个对象都通过反射方式创建,这样LazyMan的类变量不会得到赋值,可以成功创建两个不同的对象。
DCL懒汉式单例3.0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class LazyManV4 { private static LazyManV4 lazyMan; private static boolean awsl = false ; public static LazyManV4 getInstance () { if (lazyMan==null ){ synchronized (LazyManV4.class){ if (lazyMan==null ){ lazyMan = new LazyManV4 (); } } } return lazyMan; } private LazyManV4 () { synchronized (LazyManV4.class){ if (!awsl){ awsl=true ; }else { throw new RuntimeException ("不要试图使用反射破坏异常" ); } } System.out.println(Thread.currentThread().getName()+ "线程创建了一个DCL LazyMan" ); } }
添加红绿灯,用于判断对象是否被反复创建。
构造方法被重复调用,除第一次外,后面都会抛出异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 main线程创建了一个DCL LazyMan 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.yn.Demo31_懒汉式单例破解攻坚战.Crack3.main(Crack3.java:14 ) Caused by: java.lang.RuntimeException: 不要试图使用反射破坏异常 at com.yn.Demo31_懒汉式单例破解攻坚战.LazyManV4.<init>(LazyManV4.java:27 ) ... 5 more Process finished with exit code 1
通过反射破解DCL懒汉式单例3.0 解密并想办法获得红绿灯的变量,然后修改红绿灯的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Crack3 { public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class c1 = LazyManV4.class; Constructor constructor = c1.getDeclaredConstructor(); constructor.setAccessible(true ); LazyManV4 lazyMan_2 = (LazyManV4) constructor.newInstance(); Field awsl = c1.getDeclaredField("awsl" ); awsl.setAccessible(true ); awsl.set(LazyManV4.class,false ); LazyManV4 lazyMan_1 = (LazyManV4) constructor.newInstance(); System.out.println(lazyMan_1); System.out.println(lazyMan_2); } }
枚举是一个单例对象
newInstance()的源代码:不能通过反射破坏枚举类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, null, modifiers); } } if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects"); ConstructorAccessor ca = constructorAccessor; // read volatile if (ca == null) { ca = acquireConstructorAccessor(); } @SuppressWarnings("unchecked") T inst = (T) ca.newInstance(initargs); return inst; }
通过newInstance()的源代码发现,newInstance()不能通过反射破坏枚举类型。
枚举自带单例模式。
枚举
1 2 3 4 5 6 public enum Enum1 { INSTANCE; public static Enum1 getInstance () { return INSTANCE; } }
通过反射破解枚举的单例对象
Idea反编译Enmu1的class,发现代码里面有一个无参构造方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public enum Enum1 { INSTANCE; private int asd; private Enum1 () { } public static Enum1 getInstance () { return INSTANCE; } public String toString () { return "Enum1{asd=" + this .asd + '}' ; } }
用反射的方式,通过无参构造方法创建对象。
1 2 3 4 5 6 7 8 9 10 11 12 public class Crack4 { public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { Enum1 enum1 = Enum1.getInstance(); Constructor<Enum1> constructor = Enum1.class.getDeclaredConstructor(); constructor.setAccessible(true ); Enum1 enum2 = constructor.newInstance(); System.out.println(enum1); System.out.println(enum2); } }
1 2 3 4 5 6 Exception in thread "main" java.lang.NoSuchMethodException: com.yn.Demo31_懒汉式单例破解攻坚战.Enum1.<init>() at java.lang.Class.getConstructor0(Class.java:3082) at java.lang.Class.getDeclaredConstructor(Class.java:2178) at com.yn.Demo31_懒汉式单例破解攻坚战.Crack4.main(Crack4.java:11) Process finished with exit code 1
出现错误:没有这个方法。
结论:没有相关的无参构造方法。
通过javap -p 反编译class字节码文件,查看源代码。
也许Idea骗了我们?
1 2 3 4 5 6 7 8 9 10 11 12 Compiled from "Enum1.java" public final class com .yn.Demo31_懒汉式单例破解攻坚战.Enum1 extends java .lang.Enum<com.yn.Demo31_懒汉式单例破解攻坚战.Enum1> { public static final com.yn.Demo31_懒汉式单例破解攻坚战.Enum1 INSTANCE; private int asd; private static final com.yn.Demo31_懒汉式单例破解攻坚战.Enum1[] $VALUES; public static com.yn.Demo31_懒汉式单例破解攻坚战.Enum1[] values(); public static com.yn.Demo31_懒汉式单例破解攻坚战.Enum1 valueOf (java.lang.String) ; private com.yn.Demo31_懒汉式单例破解攻坚战.Enum1(); public static com.yn.Demo31_懒汉式单例破解攻坚战.Enum1 getInstance () ; public java.lang.String toString () ; static {}; }
通过反编译得知
public final class com.yn.Demo31_懒汉式单例破解攻坚战.Enum1 extends java.lang.Enum<com.yn.Demo31_懒汉式单例破解攻坚战.Enum1>
。枚举确实是一个用class修饰的类,不过是继承了java.lang.Enum成为枚举。
private com.yn.Demo31_懒汉式单例破解攻坚战.Enum1();
。Enmu1类确实有一个无参构造方法。?!
尝试使用jad反编译class字节码文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.yn.Demo31_61D26C495F0F53554F8B783489E3653B575A6218;public final class Enum1 extends Enum { public static Enum1[] values() { return (Enum1[])$VALUES.clone(); } public static Enum1 valueOf (String name) { return (Enum1)Enum.valueOf(com/yn/Demo31_61D26C495F0F53554F8B783489E3653B575A6218/Enum1, name); } private Enum1 (String s, int i) { super (s, i); } public static Enum1 getInstance () { return INSTANCE; } public String toString () { return (new StringBuilder ()).append("Enum1{asd=" ).append(asd).append('}' ).toString(); } public static final Enum1 INSTANCE; private int asd; private static final Enum1 $VALUES[]; static { INSTANCE = new Enum1 ("INSTANCE" , 0 ); $VALUES = (new Enum1 [] { INSTANCE }); } }
由反编译结果可知,private Enum1(String s, int i)
是一个有参构造方法。
通过有参构造方法创建对象
1 2 3 4 5 6 7 8 9 10 11 12 public class Crack4 { public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { Enum1 enum1 = Enum1.getInstance(); Constructor<Enum1> constructor = Enum1.class.getDeclaredConstructor(String.class,int .class); constructor.setAccessible(true ); Enum1 enum2 = constructor.newInstance(); System.out.println(enum1); System.out.println(enum2); } }
1 2 3 4 5 Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417 ) at com.yn.Demo31_懒汉式单例破解攻坚战.Crack4.main(Crack4.java:13 ) Process finished with exit code 1
通过输出得知,不能通过反射创建枚举对象。
其他
enum枚举
枚举本身是一个class类
使用jad将class文件转换为java文件
jad -sjava 字节码文件.class