一、反射的核心概念
1. 什么是反射?
反射的本质是在程序运行时,获取并操作类的所有信息的能力。
- 类的信息包括:成员变量、方法、构造函数、父类、接口、修饰符等。
- 反射打破了编译期的访问限制,能直接操作
private/protected修饰的成员。
2. 反射的前置:Java 类的生命周期
- 源代码阶段(.java 文件):程序员编写的源码文件,存储在磁盘上,人可直接阅读。
- 编译阶段(.class 文件):通过
javac命令将.java 文件编译为二进制字节码文件,存储在磁盘上,程序无法直接操作。 - 类加载阶段(内存中的 Class 对象):通过
java命令运行程序,JVM 将.class 文件加载到内存的方法区,生成唯一的Class对象(类对象),这是反射的操作入口。 - 运行时阶段(实例对象):程序运行时,通过
new关键字创建类的实例对象,存储在堆内存中。
二、获取 Class 对象的三种方式
三种方式获取的是同一个 Class 对象(同一个类加载器下,一个类只会被加载一次)。
| 方式 | 适用场景 | 代码示例 |
|---|---|---|
类名.class | 编译期已知类名 | Class clazz = Animal.class; |
对象.getClass() | 已有实例对象 | Animal animal = new Animal(); Class clazz = animal.getClass(); |
Class.forName("全类名") | 运行期动态获取(最常用) | Class clazz = Class.forName("com.qcby.Animal"); |
// 三种方式获取Class对象,结果均为同一个对象
Class<?> clazz1 = Animal.class;
Class<?> clazz2 = new Animal().getClass();
Class<?> clazz3 = Class.forName("com.qcby.animal.Animal");
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz1 == clazz3); // true
三、反射获取类的成员信息
1. 获取成员变量(Field)
核心方法
| 方法 | 作用 | 访问范围 |
|---|---|---|
getFields() | 获取所有public修饰的成员变量 | 仅自身 + 父类的 public 成员 |
getDeclaredFields() | 获取本类中所有修饰符的成员变量 | 仅本类的所有成员(含 private/protected/default) |
getField("变量名") | 获取指定public成员变量 | 仅 public |
getDeclaredField("变量名") | 获取本类中指定成员变量 | 无视修饰符 |
代码示例
Class<?> clazz = Animal.class;
// 获取所有public成员变量
Field[] publicFields = clazz.getFields();
for (Field field : publicFields) {
System.out.println("public成员:" + field.getName());
}
// 获取本类所有成员变量(含private)
Field[] allFields = clazz.getDeclaredFields();
for (Field field : allFields) {
System.out.println("所有成员:" + field.getName());
}
// 获取指定private成员变量
Field privateName = clazz.getDeclaredField("name");
// 暴力反射:解除访问权限限制
privateName.setAccessible(true);
2. 获取成员方法(Method)
核心方法
| 方法 | 作用 | 访问范围 |
|---|---|---|
getMethods() | 获取所有public修饰的方法 | 自身 + 父类的 public 方法 |
getDeclaredMethods() | 获取本类所有修饰符的方法 | 仅本类的所有方法 |
getMethod("方法名", 参数类型.class) | 获取指定public方法 | 仅 public |
getDeclaredMethod("方法名", 参数类型.class) | 获取本类指定方法 | 无视修饰符 |
代码示例
Class<?> clazz = Animal.class;
// 获取所有public方法(含父类)
Method[] publicMethods = clazz.getMethods();
for (Method method : publicMethods) {
System.out.println("public方法:" + method.getName());
}
// 获取本类所有方法(含private)
Method[] allMethods = clazz.getDeclaredMethods();
for (Method method : allMethods) {
System.out.println("所有方法:" + method.getName());
}
// 获取带参数的指定方法
Method flyMethod = clazz.getDeclaredMethod("fly", int.class, String.class);
flyMethod.setAccessible(true);
3. 获取构造函数(Constructor)
核心方法
| 方法 | 作用 | 访问范围 |
|---|---|---|
getConstructors() | 获取所有public构造函数 | 仅 public |
getDeclaredConstructors() | 获取本类所有构造函数 | 无视修饰符 |
getConstructor(参数类型.class) | 获取指定public构造函数 | 仅 public |
getDeclaredConstructor(参数类型.class) | 获取本类指定构造函数 | 无视修饰符 |
代码示例
Class<?> clazz = Animal.class;
// 获取无参构造函数
Constructor<?> noArgConstructor = clazz.getDeclaredConstructor();
noArgConstructor.setAccessible(true);
Animal animal1 = (Animal) noArgConstructor.newInstance();
// 获取带参构造函数
Constructor<?> argConstructor = clazz.getDeclaredConstructor(String.class, int.class);
argConstructor.setAccessible(true);
Animal animal2 = (Animal) argConstructor.newInstance("小花", 18);
四、反射操作类的成员
1. 操作成员变量(赋值 / 取值)
Class<?> clazz = Animal.class;
Animal animal = new Animal();
// 获取private成员变量并暴力反射
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
// 取值:get(实例对象)
String name = (String) nameField.get(animal);
System.out.println("原始name:" + name);
// 赋值:set(实例对象, 值)
nameField.set(animal, "小黑");
System.out.println("修改后name:" + animal.getName());
2. 调用成员方法
Class<?> clazz = Animal.class;
Animal animal = new Animal();
// 获取private方法并暴力反射
Method showMethod = clazz.getDeclaredMethod("show");
showMethod.setAccessible(true);
// 调用方法:invoke(实例对象, 参数列表)
showMethod.invoke(animal);
// 调用带参数方法
Method flyMethod = clazz.getDeclaredMethod("fly", int.class, String.class);
flyMethod.setAccessible(true);
flyMethod.invoke(animal, 10, "黑色");
五、关键知识点总结
1. getDeclaredXXX 和 getXXX 的区别
| 方法 | 修饰符范围 | 父类成员 |
|---|---|---|
getFields()/getMethods()/getConstructors() | 仅public | 包含 |
getDeclaredFields()/getDeclaredMethods()/getDeclaredConstructors() | 所有修饰符(含 private/protected/default) | 不包含 |
2. 暴力反射(setAccessible(true))
- 作用:解除 Java 语言的访问权限检查,允许操作
private/protected修饰的成员。 - 必须场景:操作非
public的成员变量、方法、构造函数时,必须调用此方法。 - 注意:暴力反射会破坏封装性,仅在特殊场景(如框架开发)使用。
六、课后练习代码模板
以Student类为例,完成以下任务:
- 定义
Student类,包含private成员变量、方法、构造函数 - 通过反射获取并修改成员变量
- 通过反射调用普通方法和静态方法
- 反射创建实例对象
// 1. 定义Student类
class Student {
private String name;
public int age;
private static String school = "河北大学";
public Student() {}
private Student(String name) { this.name = name; }
private void study() { System.out.println(name + "正在学习"); }
public static void showSchool() { System.out.println("学校:" + school); }
}
// 2. 反射操作
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.qcby.Student");
// 反射创建对象(私有构造)
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Student student = (Student) constructor.newInstance("小明");
// 修改成员变量
Field ageField = clazz.getField("age");
ageField.set(student, 18);
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(student, "小红");
// 调用方法
Method studyMethod = clazz.getDeclaredMethod("study");
studyMethod.setAccessible(true);
studyMethod.invoke(student);
// 调用静态方法
Method showSchoolMethod = clazz.getMethod("showSchool");
showSchoolMethod.invoke(null); // 静态方法传null
}
}
七、反射的优缺点
优点
- 动态性:程序运行时可操作类的信息,无需提前知道类的具体实现。
- 打破封装:能操作非
public修饰的成员,适配框架开发场景。
缺点
- 性能损耗:反射绕过了编译期优化,运行效率较低。
- 破坏封装:暴力反射可能导致安全问题,降低代码可维护性。
八、例题
一、题目
定义实体类 Student,并利用反射完成以下操作:
1. Student 类定义要求
- 私有属性:
private String nameprivate final Integer stuIdprivate static String school
- 构造方法:
- 私有无参构造
- 私有有参构造
- 方法:
- 普通公有方法
- 私有自定义方法
- 静态私有方法
2. 反射操作任务
- 获取
Class对象,破解私有有无参构造创建实例 - 给普通私有属性赋值、读取(不依赖
get/set,纯字段反射) - 修改
final修饰的私有常量属性(进阶考点) - 修改 / 获取
static静态私有属性 - 调用私有成员方法、静态私有方法
二、答案
1. Student 实体类
public class Student {
// 私有属性
private String name;
private final Integer stuId;
private static String school = "河北大学";
// 私有无参构造
private Student() {
this.stuId = 0;
}
// 私有有参构造
private Student(String name, Integer stuId) {
this.name = name;
this.stuId = stuId;
}
// 普通公有方法
public void showInfo() {
System.out.println("姓名:" + name + ", 学号:" + stuId + ", 学校:" + school);
}
// 私有自定义方法
private void study(String subject) {
System.out.println(name + "正在学习:" + subject);
}
// 静态私有方法
private static void printSchool() {
System.out.println("当前学校:" + school);
}
}
2. 反射操作测试类 Test
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
// 1. 获取 Class 对象
Class<?> clazz = Class.forName("Student");
// 2. 破解私有构造,创建实例
// 私有无参构造创建对象
Constructor<?> noArgConstructor = clazz.getDeclaredConstructor();
noArgConstructor.setAccessible(true);
Student student1 = (Student) noArgConstructor.newInstance();
// 私有有参构造创建对象
Constructor<?> argConstructor = clazz.getDeclaredConstructor(String.class, Integer.class);
argConstructor.setAccessible(true);
Student student2 = (Student) argConstructor.newInstance("小明", 2023001);
// 3. 普通私有属性 赋值、读取(不依赖 get/set)
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
// 赋值
nameField.set(student2, "小红");
// 读取
String nameValue = (String) nameField.get(student2);
System.out.println("普通私有属性name的值:" + nameValue);
// 4. 修改 final 修饰的私有常量属性(进阶考点)
Field stuIdField = clazz.getDeclaredField("stuId");
stuIdField.setAccessible(true);
stuIdField.set(student2, 2023002);
System.out.println("final属性stuId的值:" + stuIdField.get(student2));
// 5. 修改 / 获取 static 静态私有属性
Field schoolField = clazz.getDeclaredField("school");
schoolField.setAccessible(true);
// 获取静态属性值
String schoolValue = (String) schoolField.get(null);
System.out.println("静态属性school的值:" + schoolValue);
// 修改静态属性值
schoolField.set(null, "河北工业大学");
System.out.println("修改后静态属性school的值:" + schoolField.get(null));
// 6. 调用 私有成员方法、静态私有方法
// 私有成员方法
Method studyMethod = clazz.getDeclaredMethod("study", String.class);
studyMethod.setAccessible(true);
studyMethod.invoke(student2, "Java反射");
// 静态私有方法
Method printSchoolMethod = clazz.getDeclaredMethod("printSchool");
printSchoolMethod.setAccessible(true);
printSchoolMethod.invoke(null);
// 验证结果:调用公有方法查看最终数据
student2.showInfo();
}
}
三、运行结果
普通私有属性name的值:小红
final属性stuId的值:2023002
静态属性school的值:河北大学
修改后静态属性school的值:河北工业大学
小红正在学习:Java反射
当前学校:河北工业大学
姓名:小红, 学号:2023002, 学校:河北工业大学
四、拆解说明
| 任务 | 核心实现 | 关键 API |
|---|---|---|
| 破解私有构造创建实例 | 调用getDeclaredConstructor获取私有构造,setAccessible(true)解除权限限制 | getDeclaredConstructor()、newInstance() |
| 普通私有属性读写 | 直接通过Field对象的get/set操作,不依赖getter/setter | getDeclaredField()、get()、set() |
修改final属性 | 反射绕过编译期final赋值限制,直接修改值 | setAccessible(true)、set() |
| 静态属性 / 方法操作 | 静态成员属于类,操作时传入null代替实例对象 | get(null)、set(null, value)、invoke(null) |
| 调用私有方法 | 通过getDeclaredMethod获取私有方法,setAccessible(true)后调用invoke | getDeclaredMethod()、invoke() |
补充说明
setAccessible(true)是反射操作非public成员的核心方法,用于解除 Java 的访问权限检查。- 修改
final属性属于 “反常规操作”,仅作考点理解,实际开发中不推荐使用。 - 静态成员(
static)属于类本身,而非实例对象,因此操作时无需传入实例,直接传null即可。

768

被折叠的 条评论
为什么被折叠?



