0x01 前言

了解到如何获取一个类的Class对象是我们入门反射的第一步。
因为在Java中反射操作的是java.lang.Class对象。
因此我们需要学会的第一步就是如何获取到Class对象, 为后面打好基础。

0x02 获得类实例的方法

0x02.1 例子-类(用于理解的)

  1. // 例如现在有一个 Foo类
  2. // 请实例化它并调用 print() 方法
  3. // 额外提问: 它没有构造方法,能不能被反射new呢?
  4. // 答案是可以的,在本文0x02.5.1中可以看得到结果
  5. package 反射;
  6. class Foo {
  7. void print() {
  8. System.out.println("foo的print方法冲冲冲");
  9. }
  10. }

0x02.2 类的.class语法

  1. package 反射;
  2. public class Test {
  3. public static void main(String[] args) {
  4. Class fooClass1 = Foo.class;
  5. try {
  6. Foo foo = (Foo) fooClass1.newInstance();
  7. foo.print();
  8. } catch (InstantiationException e) {
  9. e.printStackTrace();
  10. } catch (IllegalAccessException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  1. # 运行结果
  2. fooprint方法冲冲冲
  3. 进程已结束,退出代码 0

0x02.3 对象的getClass()方法

  1. package 反射;
  2. public class Test {
  3. public static void main(String[] args) {
  4. Foo f = new Foo();
  5. Class fooClass2 = f.getClass();
  6. try {
  7. Foo foo = (Foo) fooClass2.newInstance();
  8. foo.print();
  9. } catch (InstantiationException e) {
  10. e.printStackTrace();
  11. } catch (IllegalAccessException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. }
  1. # 运行结果
  2. fooprint方法冲冲冲
  3. 进程已结束,退出代码 0

0x02.4 通过-类加载器

  1. package 反射;
  2. public class Test {
  3. public static void main(String[] args) {
  4. Class fooClass3 = null;
  5. try {
  6. fooClass3 = ClassLoader.getSystemClassLoader().loadClass("反射.Foo");
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. try {
  11. Foo foo = (Foo) fooClass3.newInstance();
  12. foo.print();
  13. } catch (InstantiationException e) {
  14. e.printStackTrace();
  15. } catch (IllegalAccessException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }
  1. # 运行结果
  2. fooprint方法冲冲冲
  3. 进程已结束,退出代码 0

0x02.5 Class的静态方法 forName(String classPath)

0x02.5.1 实例化-例子

  1. package 反射;
  2. public class Test {
  3. public static void main(String[] args) {
  4. Class fooClass4 = null;
  5. try {
  6. fooClass4 = Class.forName("反射.Foo");
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. try {
  11. // 注意: 如果反射的这个类,没有构造方法,那么也是可以被new出来的
  12. // 因为Java的类没有构造方法的时候,也会为类添加一个默认的空构造方法
  13. // 记得这一点,代码审计时遇到可控Class.forName但是不可控newInstance()的情况
  14. // 那么就可以去找那种没有设置构造方法的工具类,进行实例化
  15. Foo foo = (Foo) fooClass4.newInstance();
  16. foo.print();
  17. } catch (InstantiationException e) {
  18. e.printStackTrace();
  19. } catch (IllegalAccessException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }
  1. # 运行结果
  2. fooprint方法冲冲冲
  3. 进程已结束,退出代码 0

0x02.5.2 额外知识一

如果说获取的Class对象为数组类型的话,需要特殊注意。
这种情况需要使用 Java JNI字段描述符

  1. 注意事项:
  2. 一个数组 [Ljava.lang.String;
  3. 二维数组 [[Ljava.lang.String;
  4. 三维数组 [[[Ljava.lang.String;

0x02.5.2.1 String[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> stringArray = Class.forName("[Ljava.lang.String;");
  5. Class<String[]> test = String[].class;
  6. System.out.println(stringArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.2.2 Object[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> objectArray = Class.forName("[Ljava.lang.Object;");
  5. Class<Object[]> test = Object[].class;
  6. System.out.println(objectArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.2.3 Integer[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> integerArray = Class.forName("[Ljava.lang.Integer;");
  5. Class<Integer[]> test = Integer[].class;
  6. System.out.println(integerArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.2.4 Boolean[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> booleanArray = Class.forName("[Ljava.lang.Boolean;");
  5. Class<Boolean[]> test = Boolean[].class;
  6. System.out.println(booleanArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.2.5 byte[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> byteArray = Class.forName("[B");
  5. Class<byte[]> test = byte[].class;
  6. System.out.println(byteArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.2.6 char[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> charArray = Class.forName("[C");
  5. Class<char[]> test = char[].class;
  6. System.out.println(charArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.2.7 short[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> shortArray = Class.forName("[S");
  5. Class<short[]> test = short[].class;
  6. System.out.println(shortArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.2.8 int[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> intArray = Class.forName("[I");
  5. Class<int[]> test = int[].class;
  6. System.out.println(intArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.2.9 long[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> longArray = Class.forName("[J");
  5. Class<long[]> test = long[].class;
  6. System.out.println(longArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.2.10 float[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> floatArray = Class.forName("[F");
  5. Class<float[]> test = float[].class;
  6. System.out.println(floatArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.2.11 double[].class-JNI字段描述符

  1. public class JNITest {
  2. public static void main(String[] args) {
  3. try {
  4. Class<?> doubleArray = Class.forName("[D");
  5. Class<double[]> test = double[].class;
  6. System.out.println(doubleArray == test);
  7. } catch (ClassNotFoundException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

0x02.5.3 额外知识二

  1. # 在使用 Class.forName() 调用内部类的时候需要使用 $符号 来代替 .(点号)
  2. # 例如现在有一个 com.alibaba.Test里面有一个内部类叫Hello
  3. # 然后现在想调用 om.alibaba.Test 里面的 Hello
  4. # 那么调用的时候就应该这么写 com.alibaba.Test$Hello
  5. # 换成代码就是
  6. public class Test {
  7. public static void main(String[] args) {
  8. Class tClass = null;
  9. try {
  10. tClass = Class.forName("com.alibaba.Test$Hello");
  11. } catch (ClassNotFoundException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. }