获取类的方式
根据类名,获取类的字面量引用,不会触发类加载
类已经被加载,已经实例化了该对象的情况下,根据类名获取类的信息,故该方法不会触发类加载过程
根据类的全额限定名,寻找到该类,然后会触发类的加载过程(包括加载→链接→初始化)。注意调用forName并不会触发构造方法
代码调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package org.javasec.FirstReflect; import static java.lang.Class.forName;
public class TestReflect { static void test() throws ClassNotFoundException { System.out.println("===.class-开始==="); Class<User> c1 =User.class; System.out.println("===.class-结束===");
System.out.println("===getClass-开始==="); User user1 = new User(); Class<? extends User> c2 =user1.getClass(); System.out.println("===getClass-结束===");
System.out.println("===forName-开始==="); Class<?> c3 = forName("org.javasec.FirstReflect.User"); System.out.println("===forName-结束===");
} }
|
我们先注释掉其他的操作,仅仅看.class,输出
1 2 3
| ===.class-开始=== org.javasec.FirstReflect.User ===.class-结束===
|
确定证明了.class不会触发类加载
然后仅仅看getClass,输出
1 2 3 4 5 6 7
| System.out.println("===getClass-开始==="); System.out.println("=======创建对象-开始==="); User user1 = new User(); System.out.println("=======创建对象-结束==="); Class<? extends User> c2 =user1.getClass(); System.out.println(c2.getName()); System.out.println("===getClass-结束===");
|
1 2 3 4 5 6 7 8
| ===getClass-开始=== =======创建对象-开始=== [User]: 静态初始化块-执行成功 [User]: 实例初始化-执行成功 [User]: 无参构造方法-执行成功 =======创建对象-结束=== org.javasec.FirstReflect.User ===getClass-结束===
|
可以看出getClass本身也是不会触发类加载的,而是直接用已经加载的类
再看forName,输出
1 2 3 4
| System.out.println("===forName-开始==="); Class<?> c3 = forName("org.javasec.FirstReflect.User"); System.out.println(c3.getName()); System.out.println("===forName-结束===");
|
1 2 3 4
| ===forName-开始=== [User]: 静态初始化块-执行成功 org.javasec.FirstReflect.User ===forName-结束===
|
可以看出只有forName触发了类加载机制,直接调用了静态块的方法!这是一个很重要的特性
如果静态块里面有危险方法,那么攻击者无需拿到类的实例
1 2 3 4 5 6 7 8 9
| static { System.out.println("[User]: 静态初始化块-执行成功"); try { Process p1 =Runtime.getRuntime().exec("calc"); } catch (IOException e) { throw new RuntimeException(e); } }
|
仅仅调用forName就能触发危险代码执行!
forName重载
1 2 3
| // 三个参数的方法可以控制是否初始化和使用哪个ClassLoader Class.forName("com.example.MyClass", false, classLoader); // 不初始化 Class.forName("com.example.MyClass", true, classLoader); // 初始化(默认)
|
默认会进行初始化