一、什么是反射机制?
在 Java 中,反射机制是在运行时对类进行分析和操作的一种能力。它允许程序在运行时获取类的信息,如类的名称、成员变量、方法等,并且可以动态地调用这些方法、访问和修改成员变量的值。例如,可以使用反射机制在运行时根据条件调用不同的方法、访问私有成员变量和方法、实现框架和插件化开发以及实现依赖注入等。反射机制虽然功能强大,但也有性能开销较大和可能降低代码可读性与可维护性的缺点。
二、反射机制主要提供的功能
一、获取类信息
- 在运行时获取类的名称。可以使用
Class.getName()
方法来获取类的全限定名。
代码示例:Class<?> clazz = MyClass.class; String className = clazz.getName();
- 获取类的修饰符,如 public、private、protected 等。可以使用
Class.getModifiers()
方法来获取类的修饰符标志。
代码示例:int modifiers = clazz.getModifiers(); if (Modifier.isPublic(modifiers)) { // 类是 public 的 }
- 获取类的父类信息。可以使用
Class.getSuperclass()
方法来获取类的父类。
代码示例:Class<?> superclass = clazz.getSuperclass();
二、访问成员变量
- 获取类的所有成员变量,包括私有成员变量。可以使用
Class.getDeclaredFields()
方法来获取类声明的所有字段。
代码示例:Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { // 可以获取字段的名称、类型等信息 }
- 设置和获取成员变量的值。可以使用
Field.set()
和Field.get()
方法来设置和获取成员变量的值。
代码示例:Field field = clazz.getDeclaredField("myField"); field.setAccessible(true); Object value = field.get(instance); field.set(instance, newValue);
三、调用方法
- 获取类的所有方法,包括私有方法。可以使用
Class.getDeclaredMethods()
方法来获取类声明的所有方法。
代码示例:Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { // 可以获取方法的名称、参数类型等信息 }
- 调用方法。可以使用
Method.invoke()
方法来调用对象的方法。
代码示例:Method method = clazz.getDeclaredMethod("myMethod", int.class); method.setAccessible(true); Object result = method.invoke(instance, argument);
四、创建对象
- 可以使用
Class.newInstance()
方法来创建类的实例,但这个方法只能调用无参的构造函数。
代码示例:Class<?> clazz = MyClass.class; Object instance = clazz.newInstance();
- 如果需要调用有参数的构造函数,可以使用
Constructor.newInstance()
方法。
代码示例:Constructor<?> constructor = clazz.getConstructor(int.class, String.class); Object instance = constructor.newInstance(10, "Hello");
三、java Reflection API简介
-
Class类:代表一个类,位于java.lang包下
-
Field类:代表类的成员变量(成员变量也称为类的属性)
-
Method类:代表类的方法
-
Constructor类:代表类的构造方法
-
Array类:提供了动态创建数组,以及访问数组的元素的静态方法
四、java中的Class介绍
Class 类十分特殊,它没有共有的构造方法,被jvm调用的(简单的理解:new对象或者被类加载器加载的时候),在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。
五、java中的Class三种获取方式
-
利用对象调用getClass()方法获取该对象的Class实例;
-
使用Class类的静态方法forName(),用类的名字获取一个Class实例 ;
-
运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例;
说明:在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。
//方式一
Person person = new Person();
Class<? extends Person> personClazz01 = person.getClass();
//方式二
try {
Class<?> personClazz02 = Class.forName("Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//方式三
Class<? extends Person> personClazz03 = Person.class;
六、java中的Class中一些重要的方法
-
public Annotation[] getAnnotations () 获取这个类中所有注解
-
getClassLoader() 获取加载这个类的类加载器
-
getDeclaredMethods() 获取这个类中的所有方法
-
getReturnType() 获取方法的返回类型
-
getParameterTypes() 获取方法的传入参数类型
-
isAnnotation() 测试这类是否是一个注解类
-
getDeclaredConstructors() 获取所有的构造方法
-
getDeclaredMethod(String name, Class… parameterTypes) 获取指定的构造方法(参数:参数类型.class)
-
getSuperclass() 获取这个类的父类
-
getInterfaces() 获取这个类实现的所有接口
-
getFields() 获取这个类中所有被public修饰的成员变量
-
getField(String name) 获取指定名字的被public修饰的成员变量
-
newInstance() 返回此Class所表示的类,通过调用默认的(即无参数)构造函数创建的一个新实例
七、如何通过反射获取私有成员变量和私有方法
/**
* Created by yuanyc on 2024/1/28.
*/
public class Person {
private String name = "zhangsan";
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class ReflectionUtil {
/**
* 通过反射获取私有的成员变量
*
* @param obj 目标对象
* @param fieldName 成员变量名称
* @param <T> 对象类型
* @return 成员变量的值
*/
public static <T> Object getPrivateValue(T obj, String fieldName) {
try {
// 获取对象的类
Class<?> clazz = obj.getClass();
// 获取指定名称的私有字段
Field field = clazz.getDeclaredField(fieldName);
// 设置可访问私有字段
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException e) {
System.err.println("找不到指定的字段:" + fieldName);
e.printStackTrace();
} catch (IllegalAccessException e) {
System.err.println("无法访问指定的字段:" + fieldName);
e.printStackTrace();
}
return null;
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person();
// 打印没有改变属性之前的 name 值
System.out.println("before:" + ReflectionUtil.getPrivateValue(person, "name"));
person.setName("lisi");
// 打印修改之后的 name 值
System.out.println("after:" + ReflectionUtil.getPrivateValue(person, "name"));
}
}
八、案例演示反射
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class Person {
private String name;
private int age;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class ReflectionUtils {
/**
* Demo1: 通过 Java 反射机制得到类的包名和类名
*/
public static void test1(Person person) {
System.out.println("Test1: 包名: " + person.getClass().getPackage().getName() + "," + "完整类名: " + person.getClass().getName());
}
/**
* Demo2: 验证所有的类都是 Class 类的实例对象
*/
public static void test2() throws ClassNotFoundException {
Class<?> class1 = null;
Class<?> class2 = null;
// 写法 1, 可能抛出 ClassNotFoundException [多用这个写法]
class1 = Class.forName("com.example.Person");
System.out.println("Test2:(写法 1) 包名: " + class1.getPackage().getName() + "," + "完整类名: " + class1.getName());
// 写法 2
class2 = Person.class;
System.out.println("Test2:(写法 2) 包名: " + class2.getPackage().getName() + "," + "完整类名: " + class2.getName());
}
/**
* Demo3: 通过 Java 反射机制,用 Class 创建类对象[这也就是反射存在的意义所在]
*/
public static void test3() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class<?> class1 = Class.forName("com.example.Person");
// 由于这里不能带参数,所以你要实例化的这个类 Person,一定要有无参构造函数
Person person = (Person) class1.newInstance();
person.setAge(26);
person.setName("kaiven");
System.out.println("Test3: " + person.getName() + " : " + person.getAge());
}
/**
* Demo4: 通过 Java 反射机制得到一个类的构造函数,并实现创建带参实例对象
*/
public static void test4() throws ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<?> class1 = Class.forName("com.example.Person");
// 得到一系列构造函数集合
Constructor<?>[] constructors = class1.getConstructors();
Person person1 = null;
Person person2 = null;
try {
person1 = (Person) constructors[0].newInstance();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
person1.setAge(28);
person1.setName("zhuk");
person2 = (Person) constructors[1].newInstance(29, "zhuk");
System.out.println("Test4: " + person1.getName() + " : " + person1.getAge() + ", " + person2.getName() + " : " + person2.getAge());
}
/**
* Demo5: 通过 Java 反射机制操作成员变量, set 和 get
*/
public static void test5() throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException, InstantiationException, ClassNotFoundException {
Class<?> class1 = Class.forName("com.example.Person");
Object obj = class1.newInstance();
Field nameField = class1.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(obj, "cyy");
System.out.println("Test5: 修改属性之后得到属性变量的值:" + nameField.get(obj));
}
/**
* Demo6: 通过 Java 反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等
*/
public static void test6() throws ClassNotFoundException {
Class<?> class1 = Class.forName("com.example.Person");
// 取得父类名称
Class<?> superClass = class1.getSuperclass();
System.out.println("Test6: Person 类的父类名: " + superClass.getName());
System.out.println("===============================================");
Field[] fields = class1.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
System.out.println("类中的成员: " + fields[i]);
}
System.out.println("===============================================");
// 取得类方法
Method[] methods = class1.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println("Test6,取得 Person 类的方法:");
System.out.println("函数名:" + methods[i].getName());
System.out.println("函数返回类型:" + methods[i].getReturnType());
System.out.println("函数访问修饰符:" + java.lang.reflect.Modifier.toString(methods[i].getModifiers()));
System.out.println("函数代码写法: " + methods[i]);
}
System.out.println("===============================================");
Class<?> interfaces[] = class1.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
System.out.println("实现的接口类名: " + interfaces[i].getName());
}
}
/**
* Demo7: 通过 Java 反射机制调用类方法
*/
public static void test7() throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> class1 = Class.forName("com.example.Person");
System.out.println("Test7: \n调用无参方法(如果有):");
Method method = class1.getMethod("yourMethodWithoutParams");
Object result = method.invoke(class1.newInstance());
System.out.println("无参方法调用结果:" + result);
System.out.println("调用有参方法(如果有):");
method = class1.getMethod("yourMethodWithParams", int.class);
result = method.invoke(class1.newInstance(), 100);
System.out.println("有参方法调用结果:" + result);
}
/**
* Demo8: 通过 Java 反射机制得到类加载器信息
*/
public static void test8() throws ClassNotFoundException {
Class<?> class1 = Class.forName("com.example.Person");
String name = class1.getClassLoader().getClass().getName();
System.out.println("Test8: 类加载器类名: " + name);
}
}
public class MainActivityExtended extends android.app.Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tests();
}
private void tests() {
try {
// 通过 Java 反射机制得到类的包名和类名
Person person = new Person();
ReflectionUtils.test1(person);
System.out.println("===============================================");
// 验证所有的类都是 Class 类的实例对象
ReflectionUtils.test2();
System.out.println("===============================================");
// 通过 Java 反射机制,用 Class 创建类对象[这也就是反射存在的意义所在],无参构造
ReflectionUtils.test3();
System.out.println("===============================================");
// 通过 Java 反射机制得到一个类的构造函数,并实现构造带参实例对象
ReflectionUtils.test4();
System.out.println("===============================================");
// 通过 Java 反射机制操作成员变量, set 和 get
ReflectionUtils.test5();
System.out.println("===============================================");
// 通过 Java 反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等
ReflectionUtils.test6();
System.out.println("===============================================");
// 通过 Java 反射机制调用类中方法
ReflectionUtils.test7();
System.out.println("===============================================");
// 通过 Java 反射机制获得类加载器
ReflectionUtils.test8();
System.out.println("===============================================");
} catch (Exception e) {
e.printStackTrace();
}
}
}
九、反射机制的运用场景
一、动态调用方法
-
当需要在运行时根据条件调用不同的方法时,可以使用反射。例如,根据用户的操作或配置文件中的设置来决定调用哪个特定的方法。
-
代码示例:
-
try {
Class<?> clazz = Class.forName("com.example.MyClass");
Object instance = clazz.newInstance();
Method method = clazz.getMethod("myMethod", int.class);
method.invoke(instance, 10);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
二、访问私有成员变量和方法
-
在某些情况下,可能需要访问一个类的私有成员变量或方法,例如在进行单元测试或实现一些特殊的功能时。反射可以突破访问限制。
-
代码示例:
-
try {
Class<?> clazz = Class.forName("com.example.MyClass");
Object instance = clazz.newInstance();
Field field = clazz.getDeclaredField("privateField");
field.setAccessible(true);
int value = field.getInt(instance);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
三、框架和插件化开发
-
许多 Android 框架和插件化开发中会使用反射机制。例如,在实现热修复、动态加载插件等功能时,反射可以用来加载和实例化未知的类,调用其方法。
-
以热修复为例,通过反射可以替换有问题的类或方法,实现运行时的修复。
四、实现依赖注入
-
可以使用反射来实现依赖注入框架。在运行时自动查找和注入所需的对象,减少手动初始化的代码。
-
例如,一个依赖注入框架可以通过反射查找标记了特定注解的字段,并自动将对应的对象实例注入到这些字段中。