一、基本介绍
Java 反射是一种强大的特性,它允许在运行时动态地获取和操作类、对象、字段和方法的信息,可以说是强化版的自省(有点类似于 Python 的自省)。说简单点,就是可以在调用某个类的时候,能够获取和修改这个类一些东西。这也是 Java 被认为是半静态半动态语言的重要原因。
优点
- 动态性:反射允许在运行时动态地获取和操作类的信息,而不需要在编译时知道类的具体细节。这使得编写通用的、可扩展的代码变得更加容易。
- 灵活性:反射提供了一种灵活的方式来处理类、对象、字段和方法。它可以用于实现一些复杂的功能,如依赖注入、ORM(对象关系映射)等。
- 框架和工具支持:许多框架和工具(如 Spring、Hibernate 等)使用反射来实现它们的功能。反射使得这些框架和工具能够在运行时自动地处理和操作类的信息。
缺点
- 性能开销:反射通常比直接调用代码更慢。由于反射涉及到动态解析和调用,它需要更多的时间和资源。因此,在性能敏感的应用程序中,过度使用反射可能会导致性能下降。
- 安全性问题:反射可以绕过访问控制,访问和修改私有成员。这可能会导致安全漏洞,因此在使用反射时需要谨慎处理,并确保只有授权的代码可以访问敏感信息。
- 可读性和维护性:由于反射允许在运行时动态地操作类和对象,它可能导致代码更加复杂和难以理解。使用反射的代码可能会缺乏清晰的结构和可读性,从而增加了维护的难度。
反射是一项强大的特性,但在使用时需要权衡其优点和缺点,并根据具体情况进行选择。在某些情况下,反射是必需的,但在其他情况下,应该谨慎使用以避免潜在的性能和安全问题。简单说,就是用的好,能大大提高程序质量,用的不好,会浪费大量性能且产生安全隐患。
二、反射相关类
反射机制里面相关类有 Class、Field、Methods、Paramter 等,这里给出几个做讲解。
2.1 Class 对象
Class 类位于 java.lang.Class,不需要我们手动进行导入。得到一个类的 Class 对象的方法有三种,这三种方法得到的 Class 对象是同一个东西。
2.1.1 类的 getClass 方法
这种方法适用于已经有对象实例的情况,可以通过对象实例来获取其所属类的 Class 对象。
String str = "Java"; Class cls = str.getClass();
2.1.2 类的 class 字段
这种方法适用于已知类名的情况,可以直接在代码中获取该类的 Class 对象。
String str = "Java"; Class cls = String.class;
2.1.3 Class 的 forName 方法
这种方法最常用,适用于在运行时动态加载类的情况,可以通过类的全限定名来获取 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
2.2 Field 类
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() | 返回该字段的修饰符类(不是直接返回修饰符) |
2.3 Method 类
Method 位于 java.lang.reflect.Method,也是需要我们手动导入的,它可以用来获取方法。
下面是该类的基本方法:
方法 | 描述 |
getName() | 返回该方法的方法名 |
setAccessible(boolean flag) | 将该方法的访问修改为 flag(true 可访问,false 不可访问) |
getReturnType() | 返回该方法的返回类型 |
getModifiers() | 返回该方法的修饰符类(不是直接返回修饰符) |
getParameterTypes() | 以数组形式返回该方法的参数类型的 Class 对象 |
2.4 Modifier 类
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 |
三、基本使用方法
3.1 获取
下面是 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 } }
3.2 修改
下面反射机制中用于修改的示例:
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 } }