Java中的反射(Reflection)是一个非常强大的机制,它允许程序在运行时检查或修改类的行为。这种能力主要通过java.lang.reflect
包中的类和接口来实现。
通过反射,Java程序可以动态地创建对象、调用方法、访问字段,以及获取类的各种信息(如构造器、方法、字段等)。
反射的用途
反射主要用于以下几种情况:
- 动态创建对象:通过类的
Class
对象动态地创建其实例。 - 访问类的字段和方法:即使字段和方法是私有的,也可以通过反射机制来访问和修改它们。
- 动态调用方法:可以在运行时决定调用哪个方法,而不需要在编写代码时就知道。
- 框架开发:很多Java框架(如Spring)都大量使用反射来简化配置和初始化过程。
反射学什么?
- 学习获取类的信息、操作它们:这是反射学习的核心目标。你需要学会如何获取一个类的信息(如类名、方法、字段等),并能够在运行时操作这些信息。
反射第一步:加载类,获取类的字节码:Class对象
- 加载类:Java的类加载器(ClassLoader)负责将类的.class文件加载到JVM中。
- 获取Class对象:一旦类被加载,就可以通过多种方式来获取其
Class
对象。例如,使用Class.forName(String className)
、obj.getClass()
或Type.class
(其中Type是某个类名)。
以下是获取Class
对象的三种主要方式:
1. 使用.class
语法
对于已知的类,你可以直接使用.class
语法来获取其Class
对象。这种方式在编译时就已经确定了要操作的类,因此不需要处理ClassNotFoundException
。
2. 使用Class.forName(String className)
方法
这是最常用的动态加载类的方式。Class.forName(String className)
方法会加载指定的类,并返回该类的Class
对象。如果指定的类不存在于类路径中,或者因为其他原因无法被加载,那么会抛出ClassNotFoundException
异常。
3. 使用对象的getClass()
方法
如果你已经有了类的实例,那么可以通过调用该实例的getClass()
方法来获取其Class
对象。这个方法返回的是该对象的实际运行时类的Class
对象,这允许你在运行时发现对象的实际类型(包括继承体系中的具体类型)。
代码演示3种方式
测试类
package demo14;
public class Test {
public static void main(String[] args) throws Exception {
//通过.class获取
Class c1=Student.class;
//getName()方法返回类的全限定名,即demo14.Student
System.out.println(c1.getName());
//getSimpleName()方法返回类的简单名称,即Student
System.out.println(c1.getSimpleName());
//通过Class.forName()获取
Class c2=Class.forName("demo14.Student");
System.out.println(c1==c2);
//创建了一个Student类的实例并通过该实例的getClass()方法获取Class对象
Student student=new Student();
Class c3=student.getClass();
System.out.println(c3==c2);
//这比较获取的Class对象是否相同。由于Java虚拟机为每个类管理一个唯一的Class对象
// 所以这3个对象是相同的,输出结果为true
}
}
Student类
package demo14;
public class Student {
}
获取类构造器:Constructor对象
Java中Class提供了从类中获取构造器的方法
方法名称 | 描述 | 返回值类型 |
---|---|---|
getConstructor(Class<?>... parameterTypes) | 获取具有指定参数类型的公共构造器 | Constructor<T> |
getDeclaredConstructor(Class<?>... parameterTypes) | 获取具有指定参数类型的构造器(包括私有构造器) | Constructor<T> |
getConstructors() | 获取类的所有公共构造器 | Constructor<?>[] |
getDeclaredConstructors() | 获取类的所有构造器(包括私有构造器) | Constructor<?>[] |
部分方法演示
获取类的成员变量:Field对象
方法声明 | 说明 |
---|---|
void set(Object obj, Object value) | 为对象obj 的某个属性赋值为value |
Object get(Object obj) | 从对象obj 中获取某个属性的值,并返回该值 |
public void setAccessible(boolean flag) | 设置为true 时,表示允许通过反射访问私有成员,绕过Java的访问控制检查 |
代码
假设我们有一个Cat
类,它有两个私有成员变量name
和age
,以及一个无参构造器和一个有参构造器。我们的目标是使用Java反射来获取这些成员变量的信息,并打印它们的名称和类型。
Cat类定义
package com.itheima.d2_reflect;
public class Cat {
private String name;
private int age;
public Cat() {
System.out.println("无参数构造器执行");
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有参数构造器执行");
}
}
反射获取成员变量
接下来是使用反射API获取Cat
类成员变量的代码示例:
package com.itheima.d2_reflect;
import java.lang.reflect.Field;
public class Test3Field {
public static void testGetFields() throws NoSuchFieldException, IllegalAccessException {
// 获取Cat类的Class对象
Class<?> c = Cat.class;
// 获取类的全部成员变量
Field[] fields = c.getDeclaredFields();
// 遍历成员变量数组
for (Field field : fields) {
// 打印成员变量的名称和类型
System.out.println(field.getName() + " ---> " + field.getType());
}
// 定位某个特定的成员变量(例如name)
Field fName = c.getDeclaredField("name");
// 打印该成员变量的名称和类型
System.out.println(fName.getName() + "--->" + fName.getType());
// 注意:直接访问私有成员变量会抛出IllegalAccessException,因此需要设置可访问性
fName.setAccessible(true);
// 创建一个Cat对象
Cat cat = new Cat();
// 通过反射给私有成员变量赋值
fName.set(cat, "卡菲猫");
// 注意:由于我们没有直接获取或修改age字段,这里不展示其操作
// 打印修改后的cat对象(这里假设有合适的toString方法)
System.out.println(cat); // 需要Cat类有适当的toString()实现
}
// 主方法,用于测试
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
testGetFields();
}
}
获取类的成员方法:Method对象
- Method对象:表示类的方法。通过
Class
对象的getMethod(String name, Class<?>... parameterTypes)
或getDeclaredMethod(String name, Class<?>... parameterTypes)
方法,可以获取到方法的Method
对象。 - 使用:获取到
Method
对象后,可以调用其invoke(Object obj, Object... args)
方法来执行对象的方法。同样,如果方法是私有的,可能需要先调用Method.setAccessible(true)
。
类别 | 方法名 | 签名 | 说明 |
---|---|---|---|
Class提供的成员方法 | getMethods() | Method[] getMethods() | 获取类的全部public成员方法(按名称排序) |
Class提供的成员方法 | getDeclaredMethods() | Method[] getDeclaredMethods() | 获取类的全部成员方法(不考虑访问修饰符) |
Class提供的成员方法 | getMethod(String name, Class<?>... parameterTypes) | Method getMethod(String name, Class<?>... parameterTypes) | 获取类的某个public成员方法,需指定方法名和参数类型 |
Class提供的成员方法 | getDeclaredMethod(String name, Class<?>... parameterTypes) | Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 获取类的某个成员方法,需指定方法名和参数类型,不考虑访问修饰符 |
Method提供的成员方法 | setAccessible(boolean flag) | void setAccessible(boolean flag) | 设置方法的访问权限。如果flag为true,则忽略Java的访问控制检查(暴力反射) |
Method提供的成员方法 | invoke(Object obj, Object... args) | Object invoke(Object obj, Object... args) throws IllegalAccessException, InvocationTargetException | 触发某个对象的该方法执行,需传入对象实例和参数 |
代码示例
以下是一个完整的Java代码示例,展示了如何使用上述方法:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 假设有一个类Person
Class<?> personClass = Class.forName("Person");
// 获取所有public方法
Method[] publicMethods = personClass.getMethods();
System.out.println("Public Methods:");
for (Method method : publicMethods) {
System.out.println(method.getName());
}
// 获取所有方法(包括private)
Method[] declaredMethods = personClass.getDeclaredMethods();
System.out.println("\nDeclared Methods:");
for (Method method : declaredMethods) {
System.out.println(method.getName());
}
// 获取特定方法(假设有一个public方法sayHello)
Method sayHelloMethod = personClass.getMethod("sayHello");
// 假设Person类有一个无参构造器
Object personInstance = personClass.newInstance();
// 调用sayHello方法
sayHelloMethod.invoke(personInstance);
// 获取特定方法(假设有一个private方法secretMethod)
Method secretMethod = personClass.getDeclaredMethod("secretMethod");
// 设置访问权限为true(暴力反射)
secretMethod.setAccessible(true);
// 调用secretMethod方法
secretMethod.invoke(personInstance);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
// 假设的Person类
static class Person {
public void sayHello() {
System.out.println("Hello!");
}
private void secretMethod() {
System.out.println("This is a secret method.");
}
}
}