1 课程总结
1.1 课程总结
1.2 注意事项
2 代码演练
2.1 反射攻击(通过反射拿到单例的对象)
2.2 (应用)饿汉式防御
2.3 (应用)静态类防御
2.4 懒汉式防御1
2.5 针对懒汉式防御1反射攻击1
2.6 懒汉式防御2
2.7 针对懒汉式防御2反射攻击2
1 课程总结
1.1 课程总结
反射攻击对单例模式威胁是存在的,饿汉式单例模式和静态类的单例模式可以作相应的处理来防御反射攻击,懒汉式的单例模式无法防御(即使防御了也完全可以破解,防御是无效的)。
原因是饿汉式和静态类在类加载的时候对象就已经附上值,可以根据这点判断对象是否为空来进行防御。懒汉式的无法这样做。
1.2 注意事项
构造方法为私有方法,通过反射调用创建实例对象时,需要将权限打州constructor.setAccessible(true):
2 代码演练
2.1 反射攻击(通过反射拿到单例的对象)
package com.geely.design.pattern.creational.singleton; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Test { /*public static void main(String [] args){ //这样写异常,因为构造方法私有 // LazySingleton lazySingleton = new LazySingleton(); LazySingleton lazySingleton = LazySingleton.getInstance(); System.out.println(lazySingleton); }*/ /* public static void main(String [] args){ Thread thread1 = new Thread(new T()); Thread thread2 = new Thread(new T()); thread1.start(); thread2.start(); System.out.println("结束了!!!"); }*/ /** * 序列化代码演练 * 将 HungrySingleton对象放入文件,再从文件读取该对象,还是同一个对象吗? * 实际应用:在从文件存入后读取,用equals时需要注意(equals比较的是hash码) * @param args */ public static void main(String [] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { /* try { //将singleton对象写入到输出流中 HangrySingleton instance = HangrySingleton.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file")); oos.writeObject(instance); //从输入流中读取到该对象 File file = new File("singleton_file"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); HangrySingleton instance2 = (HangrySingleton) ois.readObject(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } */ //反射攻击 Class objectClass = HangrySingleton.class; // Class.forName(HangrySingleton.class.getName()); //通过反射拿到构造器 Constructor constructor = objectClass.getDeclaredConstructor(); //设置权限为true,不设置的话,私有权限反射报错 constructor.setAccessible(true); //通过单例拿到对象 HangrySingleton instance = HangrySingleton.getInstance(); //通过反射拿到对象 HangrySingleton instance2 = (HangrySingleton)constructor.newInstance(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); } }
打印日志:
"C:\Program Files\Java\jdk1.7.0_79\bin\java.exe" "-javaagent:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\lib\idea_rt.jar=27494:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_79\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\rt.jar;F:\xiangmu3\Xin\Idea\design_pattern\target\classes" com.geely.design.pattern.creational.singleton.Test com.geely.design.pattern.creational.singleton.HangrySingleton@6cff7cd8 com.geely.design.pattern.creational.singleton.HangrySingleton@795d80cf false Process finished with exit code 0
2.2 (应用)饿汉式防御
测试类:
package com.geely.design.pattern.creational.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Test { /*public static void main(String [] args){ //这样写异常,因为构造方法私有 // LazySingleton lazySingleton = new LazySingleton(); LazySingleton lazySingleton = LazySingleton.getInstance(); System.out.println(lazySingleton); }*/ /* public static void main(String [] args){ Thread thread1 = new Thread(new T()); Thread thread2 = new Thread(new T()); thread1.start(); thread2.start(); System.out.println("结束了!!!"); }*/ /** * 序列化代码演练 * 将 HungrySingleton对象放入文件,再从文件读取该对象,还是同一个对象吗? * 实际应用:在从文件存入后读取,用equals时需要注意(equals比较的是hash码) * @param args */ public static void main(String [] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { /* try { //将singleton对象写入到输出流中 HangrySingleton instance = HangrySingleton.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file")); oos.writeObject(instance); //从输入流中读取到该对象 File file = new File("singleton_file"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); HangrySingleton instance2 = (HangrySingleton) ois.readObject(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } */ //反射攻击 Class objectClass = HangrySingleton.class; // Class.forName(HangrySingleton.class.getName()); //通过反射拿到构造器 Constructor constructor = objectClass.getDeclaredConstructor(); //设置权限为true,不设置的话,私有权限反射报错 constructor.setAccessible(true); //通过单例拿到对象 HangrySingleton instance = HangrySingleton.getInstance(); //通过反射拿到对象 HangrySingleton instance2 = (HangrySingleton)constructor.newInstance(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); } }
单例类:
package com.geely.design.pattern.creational.singleton; import java.io.Serializable; public class HangrySingleton implements Serializable { /** * 声明私有常量,当类初始化的时候就已经赋值了。饿汉式在类初始化的时候只加载一次。 * 所以也不会存在多线程的问题。 */ private final static HangrySingleton hangrySingleton; static { hangrySingleton= new HangrySingleton(); } /** * 声明私有构造方法 * 因为饿汉式和静态类在类初始化的时候,已经附上了对象,反射取值的时候该对象一定有值。 */ private HangrySingleton(){ if(hangrySingleton != null){ throw new RuntimeException("单例构造器禁止反射!"); } } /** * 防止序列化和反序列化对单例模式进行破坏 */ private Object readResolve(){ return hangrySingleton; } /** * 提供对外接口,获得对象 * @return */ public static HangrySingleton getInstance(){ return hangrySingleton; } }
打印结果:
"C:\Program Files\Java\jdk1.7.0_79\bin\java.exe" "-javaagent:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\lib\idea_rt.jar=35955:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_79\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\rt.jar;F:\xiangmu3\Xin\Idea\design_pattern\target\classes" com.geely.design.pattern.creational.singleton.Test Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:526) at com.geely.design.pattern.creational.singleton.Test.main(Test.java:62) Caused by: java.lang.RuntimeException: 单例构造器禁止反射! at com.geely.design.pattern.creational.singleton.HangrySingleton.<init>(HangrySingleton.java:24) ... 5 more Process finished with exit code 1
2.3 (应用)静态类防御(方法同2.2)
测试类:
package com.geely.design.pattern.creational.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Test { /*public static void main(String [] args){ //这样写异常,因为构造方法私有 // LazySingleton lazySingleton = new LazySingleton(); LazySingleton lazySingleton = LazySingleton.getInstance(); System.out.println(lazySingleton); }*/ /* public static void main(String [] args){ Thread thread1 = new Thread(new T()); Thread thread2 = new Thread(new T()); thread1.start(); thread2.start(); System.out.println("结束了!!!"); }*/ /** * 序列化代码演练 * 将 HungrySingleton对象放入文件,再从文件读取该对象,还是同一个对象吗? * 实际应用:在从文件存入后读取,用equals时需要注意(equals比较的是hash码) * @param args */ /*public static void main(String [] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { *//* try { //将singleton对象写入到输出流中 HangrySingleton instance = HangrySingleton.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file")); oos.writeObject(instance); //从输入流中读取到该对象 File file = new File("singleton_file"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); HangrySingleton instance2 = (HangrySingleton) ois.readObject(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } *//* //反射攻击 Class objectClass = HangrySingleton.class; // Class.forName(HangrySingleton.class.getName()); //通过反射拿到构造器 Constructor constructor = objectClass.getDeclaredConstructor(); //设置权限为true,不设置的话,私有权限反射报错 constructor.setAccessible(true); //通过单例拿到对象 HangrySingleton instance = HangrySingleton.getInstance(); //通过反射拿到对象 HangrySingleton instance2 = (HangrySingleton)constructor.newInstance(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); }*/ /** * 静态内部类 防御反射 * @param args */ public static void main(String [] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //反射攻击 Class objectClass = StaticInnerClassSingleton.class; // Class.forName(HangrySingleton.class.getName()); //通过反射拿到构造器 Constructor constructor = objectClass.getDeclaredConstructor(); //设置权限为true,不设置的话,私有权限反射报错 constructor.setAccessible(true); //通过单例拿到对象 StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance(); //通过反射拿到对象 StaticInnerClassSingleton instance2 = (StaticInnerClassSingleton) constructor.newInstance(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); }
静态内部类:
package com.geely.design.pattern.creational.singleton; public class StaticInnerClassSingleton { /** * 静态内部类 * 自己容易犯的错误:静态内部类不要加(),不是方法。 */ private static class InnerClass{ private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton(); } public static StaticInnerClassSingleton getInstance(){ //这里如何进行初始化呢? return InnerClass.staticInnerClassSingleton; } /** * 注意一定要写私有的构造函数,否则的话容易被外部类实例化该类 */ private StaticInnerClassSingleton(){ if(InnerClass.staticInnerClassSingleton != null){ throw new RuntimeException("单例构造器禁止反射!"); } } }
打印结果:
"C:\Program Files\Java\jdk1.7.0_79\bin\java.exe" "-javaagent:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\lib\idea_rt.jar=36141:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_79\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\rt.jar;F:\xiangmu3\Xin\Idea\design_pattern\target\classes" com.geely.design.pattern.creational.singleton.Test Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:526) at com.geely.design.pattern.creational.singleton.Test.main(Test.java:85) Caused by: java.lang.RuntimeException: 单例构造器禁止反射! at com.geely.design.pattern.creational.singleton.StaticInnerClassSingleton.<init>(StaticInnerClassSingleton.java:22) ... 5 more Process finished with exit code 1
2.4 懒汉式防御1(方法同2.2)
测试类:
package com.geely.design.pattern.creational.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Test { /** * 懒汉模式 防御反射1 * @param args */ public static void main(String [] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //反射攻击 Class objectClass = LazySingleton.class; // Class.forName(HangrySingleton.class.getName()); //通过反射拿到构造器 Constructor constructor = objectClass.getDeclaredConstructor(); //设置权限为true,不设置的话,私有权限反射报错 constructor.setAccessible(true); //通过单例拿到对象 LazySingleton instance = LazySingleton.getInstance(); //通过反射拿到对象 LazySingleton instance2 = (LazySingleton) constructor.newInstance(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); } }
单例类:
package com.geely.design.pattern.creational.singleton; public class LazySingleton { /* 属性私有,其他外部类,无法调用该属性,安全 */ private static LazySingleton lazySingleton = null; /** * 构造方法私有,其他类无法实例化该类 */ private LazySingleton(){ if(lazySingleton != null){ throw new RuntimeException("单例构造器禁止反射!"); } } /** * 这里写静态方法:因为外部类无法实例化创建出该类, * 只能通过该类的静态方法获取到该类。 * @return */ /* public static synchronized LazySingleton getInstance(){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } return lazySingleton; }*/ /** * 换一种写法, * * @return */ public static LazySingleton getInstance(){ synchronized (LazySingleton.class){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } } return lazySingleton; } }
打印结果:
"C:\Program Files\Java\jdk1.7.0_79\bin\java.exe" "-javaagent:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\lib\idea_rt.jar=36332:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_79\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\rt.jar;F:\xiangmu3\Xin\Idea\design_pattern\target\classes" com.geely.design.pattern.creational.singleton.Test Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:526) at com.geely.design.pattern.creational.singleton.Test.main(Test.java:109) Caused by: java.lang.RuntimeException: 单例构造器禁止反射! at com.geely.design.pattern.creational.singleton.LazySingleton.<init>(LazySingleton.java:14) ... 5 more Process finished with exit code 1
2.5 针对懒汉式防御1反射攻击1
测试类:
package com.geely.design.pattern.creational.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Test { /** * 懒汉模式 防御反射1 * @param args */ public static void main(String [] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //反射攻击 Class objectClass = LazySingleton.class; // Class.forName(HangrySingleton.class.getName()); //通过反射拿到构造器 Constructor constructor = objectClass.getDeclaredConstructor(); //设置权限为true,不设置的话,私有权限反射报错 constructor.setAccessible(true);
//调换了顺序,这样懒汉式单例类的对象为空判断失效,同样情况下饿汉和静态类不受影响,因为饿汉最类加载的时候就完成了对象的初始化,类的使用过程中对象是一直有值的 //通过反射拿到对象 LazySingleton instance2 = (LazySingleton) constructor.newInstance(); //通过单例拿到对象 LazySingleton instance = LazySingleton.getInstance(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); } }
单例类:同上
打印结果:
"C:\Program Files\Java\jdk1.7.0_79\bin\java.exe" "-javaagent:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\lib\idea_rt.jar=36386:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_79\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\rt.jar;F:\xiangmu3\Xin\Idea\design_pattern\target\classes" com.geely.design.pattern.creational.singleton.Test com.geely.design.pattern.creational.singleton.LazySingleton@795d80cf com.geely.design.pattern.creational.singleton.LazySingleton@69b3d448 false Process finished with exit code 0
2.6 懒汉式防御2(通过复杂逻辑判断)
注意:
通过复杂逻辑判断,2.5方法同样可以攻击到2.6,这里做2.6和2.7对比是为了引入如何用反射获取单例类的属性和更改单例类的属性
此处逻辑:
定义布尔静态变量,当第二次访问时,一定抛出异常
打印结果:
"C:\Program Files\Java\jdk1.7.0_79\bin\java.exe" "-javaagent:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\lib\idea_rt.jar=36725:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_79\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\rt.jar;F:\xiangmu3\Xin\Idea\design_pattern\target\classes" com.geely.design.pattern.creational.singleton.LazySingleton Exception in thread "main" java.lang.RuntimeException: 单例构造器禁止反射! at com.geely.design.pattern.creational.singleton.LazySingleton.<init>(LazySingleton.java:21) at com.geely.design.pattern.creational.singleton.LazySingleton.getInstance(LazySingleton.java:45) at com.geely.design.pattern.creational.singleton.LazySingleton.main(LazySingleton.java:65) Process finished with exit code 1
懒汉式单例模式:
package com.geely.design.pattern.creational.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class LazySingleton { /* 属性私有,其他外部类,无法调用该属性,安全 */ private static LazySingleton lazySingleton = null; private static boolean flag = true; /** * 构造方法私有,其他类无法实例化该类 */ private LazySingleton(){ if(flag){ flag = false; }else{ throw new RuntimeException("单例构造器禁止反射!"); } } /** * 换一种写法, * * @return */ public static LazySingleton getInstance(){ synchronized (LazySingleton.class){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } } return lazySingleton; } public static void main(String [] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //反射攻击 Class objectClass = LazySingleton.class; // Class.forName(HangrySingleton.class.getName()); //通过反射拿到构造器 Constructor constructor = objectClass.getDeclaredConstructor(); //设置权限为true,不设置的话,私有权限反射报错 constructor.setAccessible(true); //通过反射拿到对象 LazySingleton instance2 = (LazySingleton) constructor.newInstance(); //通过单例拿到对象 LazySingleton instance = LazySingleton.getInstance(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); } }
2.7 针对懒汉式防御2反射攻击2
注意:
通过复杂逻辑判断,2.5方法同样可以攻击到2.6,这里做2.6和2.7对比是为了引入如何用反射获取单例类的属性和更改单例类的属性
打印结果:
"C:\Program Files\Java\jdk1.7.0_79\bin\java.exe" "-javaagent:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\lib\idea_rt.jar=37113:D:\java\devolopKit\idea\anZh\IntelliJ IDEA Community Edition 2018.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_79\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_79\jre\lib\rt.jar;F:\xiangmu3\Xin\Idea\design_pattern\target\classes" com.geely.design.pattern.creational.singleton.LazySingleton com.geely.design.pattern.creational.singleton.LazySingleton@5323cf50 com.geely.design.pattern.creational.singleton.LazySingleton@611cfa21 false Process finished with exit code 0
懒汉式单例模式:
package com.geely.design.pattern.creational.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; public class LazySingleton { /* 属性私有,其他外部类,无法调用该属性,安全 */ private static LazySingleton lazySingleton = null; private static boolean flag = true; /** * 构造方法私有,其他类无法实例化该类 */ private LazySingleton(){ if(flag){ flag = false; }else{ throw new RuntimeException("单例构造器禁止反射!"); } } /** * 这里写静态方法:因为外部类无法实例化创建出该类, * 只能通过该类的静态方法获取到该类。 * @return */ /* public static synchronized LazySingleton getInstance(){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } return lazySingleton; }*/ /** * 换一种写法, * * @return */ public static LazySingleton getInstance(){ synchronized (LazySingleton.class){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } } return lazySingleton; } public static void main(String [] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { //反射攻击 Class objectClass = LazySingleton.class; // Class.forName(HangrySingleton.class.getName()); //通过反射拿到构造器 Constructor constructor = objectClass.getDeclaredConstructor(); //设置权限为true,不设置的话,私有权限反射报错 constructor.setAccessible(true); //通过单例拿到对象 LazySingleton instance = LazySingleton.getInstance(); //在反射之前 获取flag对象并把其权限置成true Field field = instance.getClass().getDeclaredField("flag"); field.setAccessible(true); field.set(instance,true); //通过反射拿到对象 LazySingleton instance2 = (LazySingleton) constructor.newInstance(); System.out.println(instance); System.out.println(instance2); System.out.println(instance==instance2); } }
诸葛