Java 反射是一种强大的特性,它允许在运行时动态地获取和操作类、对象、字段和方法的信息,可以说是强化版的自省(有点类似于 Python 的自省)。说简单点,就是可以在调用某个类的时候,能够获取和修改这个类一些东西。这也是 Java 被认为是半静态半动态语言的重要原因。
优点
缺点
反射是一项强大的特性,但在使用时需要权衡其优点和缺点,并根据具体情况进行选择。在某些情况下,反射是必需的,但在其他情况下,应该谨慎使用以避免潜在的性能和安全问题。简单说,就是用的好,能大大提高程序质量,用的不好,会浪费大量性能且产生安全隐患。
反射机制里面相关类有 Class、Field、Methods、Paramter 等,这里给出几个做讲解。
Class 类位于 java.lang.Class,不需要我们手动进行导入。得到一个类的 Class 对象的方法有三种,这三种方法得到的 Class 对象是同一个东西。
这种方法适用于已经有对象实例的情况,可以通过对象实例来获取其所属类的 Class 对象。
- String str = "Java";
- Class cls = str.getClass();
这种方法适用于已知类名的情况,可以直接在代码中获取该类的 Class 对象。
- String str = "Java";
- Class cls = String.class;
这种方法最常用,适用于在运行时动态加载类的情况,可以通过类的全限定名来获取 Class 对象,这样可以在运行时根据需要加载不同的类,但是有可能会抛出 ClassNotFoundException 的异常。
Class cls = Class.forName("java.lang.String");
下面的代码分别展示了三种方法,且得到的 Class 对象是同一个东西:
- String str = "Java";
- Class cls2 = str.getClass(); // 实例的 getClass 方法
- Class cls1 = String.class; // 类的 class 属性
- Class cls3 = Class.forName("java.lang.String"); // Class 的 forName 方法
- System.out.println(cls1 == cls2); // Output: true
- System.out.println(cls2 == cls3); // Output: true
Field 位于 java.lang.reflect.Field,是需要我们手动导入的,它可以用来获取字段。
下面是该类的基本方法:
| 方法 | 描述 |
| get(Object obj) | 返回 obj 对象该字段的值 |
| set(Object obj, Object value) | 修改 obj 对象该字段的值为 value |
| setAccessible(boolean flag) | 将该字段的访问修改为 flag(true 可访问,false 不可访问) |
| getName() | 返回该字段的名称 |
| getType() | 返回该字段的类型 |
| getModifiers() | 返回该字段的修饰符类(不是直接返回修饰符) |
Method 位于 java.lang.reflect.Method,也是需要我们手动导入的,它可以用来获取方法。
下面是该类的基本方法:
| 方法 | 描述 |
| getName() | 返回该方法的方法名 |
| setAccessible(boolean flag) | 将该方法的访问修改为 flag(true 可访问,false 不可访问) |
| getReturnType() | 返回该方法的返回类型 |
| getModifiers() | 返回该方法的修饰符类(不是直接返回修饰符) |
| getParameterTypes() | 以数组形式返回该方法的参数类型的 Class 对象 |
Modifier 位于 java.lang.reflect.Modifier,同上,它可以用来获取修饰符。
下面是该类的基本方法:
| 方法 | 描述 |
| toString(int mod) | 通过 mod 值来返回对应的修饰符 |
| isPublic(int modifiers) | 判断给定的修饰符是否是 public |
| isStatic(int modifiers) | 判断给定的修饰符是否是 static |
| isFinal(int modifiers) | 判断给定的修饰符是否是 final |
| isAbstract(int modifiers) | 判断给定的修饰符是否是 abstract |
| isInterface(int modifiers) | 判断给定的修饰符是否是 interface |
下面是 Class 对象部分用于获取的方法:
| 方法 | 描述 |
| getName() | 返回类的全限定名 |
| getSimpleName() | 返回类的简单名称 |
| getFields() | 返回类的 public 类型的字段 |
| getDeclaredFields() | 返回类的所有字段,包括 private 声明的和继承类的 |
| getMethod(String name, Class[] parameterTypes) | 返回类特定的方法 |
| getMethods() | 返回类的 public 类型的方法 |
| getDeclaredMethods() | 返回类的所有方法,包括 private 声明的和继承类的 |
| getPackage() | 返回类所在的包 |
| getSuperclass() | 返回类的父类 |
| getInterfaces() | 返回类实现的接口 |
| getDeclaredFields() | 返回类声明的所有字段 |
| getConstructors() | 返回类的 public 类型的构造函数 |
| getDeclaredConstructors() | 返回类声明的所有构造函数 |
| getDeclaredAnnotations() | 返回类声明的所有注解 |
| getInterfaces() | 返回类的接口 |
示例:
- import java.lang.reflect.*; // 反射相关的类在这个包中
-
- interface Interface {} // 接口
-
- class Father {} // 父类
-
- class Son extends Father implements Interface { // 子类
- public String var_public = "str";
- private String var_private;
-
- Son() {};
-
- public void method_public(Integer param) {};
-
- private void method_private(Double param) {};
-
- }
-
-
- public class Test {
- public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException {
- Son son = new Son();
- Class cls = son.getClass();
- // 获取类名
- System.out.println(cls.getName()); // Output: Son
- // 获取接口(返回一个数组)
- for (Class aClass: cls.getInterfaces()) System.out.println(aClass.getName()); // Output: Interface
- // 获取父类
- System.out.println(cls.getSuperclass().getName()); // Output: Father
- // 获取修饰符(需要用到 java.lang.reflect.Modifier)
- // 类 Son 没有修饰符,输出将为空,故此处用类 Test 来代替
- System.out.println(Modifier.toString(Test.class.getModifiers())); // Output: public
- // 获取属性(需要用到 java.lang.reflect.Field)
- System.out.println(cls.getField("var_public").get(son)); // Output: str
- // 获取方法(返回一个数组)(此处只获取声明的方法,不然太多了)
- for (Method method: cls.getDeclaredMethods()) System.out.println(method.getName()); // Output: method_public \n method_private
- // 获取方法的返回类型
- System.out.println(cls.getMethod("method_public", Integer.class).getReturnType()); // Output: void
- // 获取方法的参数类型(返回一个数组)
- for (Class aClass: cls.getMethod("method_public", Integer.class).getParameterTypes()) System.out.println(aClass.getSimpleName()); // Output: Integer
- }
- }
下面反射机制中用于修改的示例:
- import java.lang.reflect.*;
-
- interface Interface {}
-
- class Father {}
-
- class Son extends Father implements Interface {
- public String var_public = "str";
- private String var_private;
-
- Son() {};
-
- public void method_public(Integer param) {};
-
- private void method_private(Double param) {};
-
- }
-
-
- public class Test {
- public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
- Son son = new Son();
- Class cls = son.getClass();
- Field field = cls.getDeclaredField("var_public");
- // 让该属性能够被访问(当属性为 private 的时候,需要用此修改其访问修饰符)
- field.setAccessible(true); // 当然,也可以根据需要修改为 false
- // 修改属性 var_public 的值之前
- System.out.println(field.get(son)); // Output: str
- // 修改值
- field.set(son, "new");
- // 修改属性 var_public 的值之后
- System.out.println(field.get(son)); // Output: new
- }
- }