Java反射核心笔记|从基础到实战,吃透反射机制
反射是Java语言的核心特性之一,也是实现"动态编程"的关键------它允许程序在运行时获取类的完整结构(构造方法、成员变量、成员方法),甚至能突破访问权限修饰符的限制,操作私有成员。不管是Spring、MyBatis等框架的底层实现,还是自定义动态化功能,反射都是必备技能。本文结合实战代码,梳理反射的核心知识点和实操要点。
一、反射的核心概念
1. 什么是反射?
反射(Reflection)是Java提供的一种机制,使得程序可以在运行期间:
- 获取任意类的完整信息(类名、构造方法、字段、方法、访问权限等);
- 操作任意对象的成员(即使是
private修饰的私有成员); - 动态创建对象、调用方法、修改字段值。
2. 反射的核心思想
Java中所有类的字节码文件(.class)加载后,都会生成一个唯一的Class类对象------反射的所有操作,都是围绕这个Class对象展开的。
二、获取Class类对象的三种方式
想要操作类的成员,第一步必须获取该类的Class对象(同一个类的Class对象全局唯一)。以下是三种核心方式,结合代码示例理解:
| 方式 | 语法 | 适用场景 |
|---|---|---|
| 全类名加载 | Class clazz = Class.forName("包名.类名"); |
动态加载类(如配置文件指定类名),需处理ClassNotFoundException |
| 类名.class | Class clazz = 类名.class; |
编译期确定类,类型安全,无需异常处理 |
| 对象.getClass() | Class clazz = 实例对象.getClass(); |
已有对象实例,需获取其类信息 |
代码验证(Class对象唯一性):
java
// 方式1:全类名加载
Class clazz1 = Class.forName("AA426.Animal");
// 方式2:类名.class
Class clazz2 = Animal.class;
// 方式3:对象.getClass()
Animal animal = new Animal("cat",18,"white");
Class clazz3 = animal.getClass();
// 结果:true、true(同一个类的Class对象唯一)
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
三、反射操作构造方法
构造方法用于创建对象,反射可以获取任意访问权限的构造方法,并创建实例。
1. 获取构造方法的核心API
| API | 作用 |
|---|---|
getDeclaredConstructors() |
获取所有构造方法(含private、protected、默认、public) |
getConstructors() |
仅获取public构造方法 |
getDeclaredConstructor(参数类型...) |
获取指定参数的构造方法(含私有) |
2. 关键操作:创建对象 + 暴力反射
- 私有构造方法默认无法访问,需通过
setAccessible(true)开启"暴力反射"; - 创建对象:
Constructor.newInstance(构造参数...)(JDK9后替代过时的Class.newInstance())。
实战示例(操作私有构造):
java
// 获取Student类的Class对象
Class clazz = Student.class;
// 获取私有无参构造
Constructor constructor = clazz.getDeclaredConstructor();
// 开启暴力反射(突破private限制)
constructor.setAccessible(true);
// 创建Student实例
Student student = (Student) constructor.newInstance();
// 同理:获取私有有参构造
Constructor constructor2 = clazz.getDeclaredConstructor(String.class, Integer.class);
constructor2.setAccessible(true);
Student student2 = (Student) constructor2.newInstance("李四", 1002);
四、反射操作成员变量
成员变量(字段)存储对象的状态,反射可读写任意权限的字段,包括静态字段、final字段。
1. 获取字段的核心API
| API | 作用 |
|---|---|
getDeclaredFields() |
获取所有字段(含私有、protected、默认、public) |
getFields() |
仅获取public字段 |
getDeclaredField("字段名") |
获取指定名称的字段(含私有) |
2. 关键操作:读/写字段值
- 读字段:
field.get(对象实例); - 写字段:
field.set(对象实例, 新值); - 私有字段:需
setAccessible(true); - 静态字段:
get/set时对象参数传null(静态字段属于类,不属于实例); - final字段:反射可强制修改(需开启暴力反射)。
实战示例(读写字段):
java
Class clazz = Student.class;
Student student = (Student) clazz.getDeclaredConstructor().newInstance();
// 1. 操作私有字段name
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 暴力反射
nameField.set(student, "张三"); // 写值
String name = (String) nameField.get(student); // 取值
System.out.println(name); // 输出:张三
// 2. 操作final字段stuId
Field stuIdField = clazz.getDeclaredField("stuId");
stuIdField.setAccessible(true);
stuIdField.set(student, 1002); // 强制修改final字段
System.out.println(stuIdField.get(student)); // 输出:1002
// 3. 操作静态字段school(对象传null)
Field schoolField = clazz.getDeclaredField("school");
schoolField.setAccessible(true);
schoolField.set(null, "清华大学"); // 静态字段对象传null
System.out.println(schoolField.get(null)); // 输出:清华大学
五、反射操作成员方法
成员方法实现对象的行为,反射可调用任意权限的方法,包括静态方法、私有方法。
1. 获取方法的核心API
| API | 作用 |
|---|---|
getDeclaredMethods() |
获取所有方法(含私有、protected、默认、public) |
getMethods() |
仅获取public方法(含父类继承的public方法) |
getDeclaredMethod("方法名", 参数类型...) |
获取指定方法(含私有) |
2. 关键操作:调用方法
- 调用方法:
Method.invoke(对象实例, 方法参数...); - 私有方法:需
setAccessible(true); - 静态方法:
invoke时对象参数传null。
实战示例(调用方法):
java
Class clazz = Student.class;
Student student = (Student) clazz.getDeclaredConstructor().newInstance();
// 1. 调用静态方法showSchool(对象传null)
Method showSchoolMethod = clazz.getDeclaredMethod("showSchool");
showSchoolMethod.invoke(null); // 输出:静态方法执行
// 2. 调用私有方法flay
Method flayMethod = clazz.getDeclaredMethod("flay");
flayMethod.setAccessible(true); // 暴力反射
flayMethod.invoke(student); // 输出:私有自定义方法执行
// 3. 调用带参方法(如Animal的show方法)
Class animalClazz = Animal.class;
Animal animal = (Animal) animalClazz.getDeclaredConstructor().newInstance();
Method showMethod = animalClazz.getDeclaredMethod("show", String.class);
showMethod.invoke(animal, "小花"); // 输出:show()
六、反射核心易错点总结
1. Declared关键字的核心区别
- 带
Declared(如getDeclaredField):无视访问权限修饰符,获取所有成员(私、保、默、公); - 不带
Declared(如getField):仅获取public成员。
2. 暴力反射(setAccessible(true))的使用场景
仅当操作私有(private) 成员(构造、字段、方法)时需要,public/protected/默认权限的成员无需开启。
3. 异常处理
反射相关操作会抛出CheckedException(如NoSuchMethodException、IllegalAccessException),需手动捕获或声明抛出。
4. 性能注意事项
反射比直接调用成员的性能略低,可通过缓存Class/Constructor/Method/Field对象 优化(如框架中常用缓存池)。
七、反射的应用场景
- 框架底层:Spring IOC(动态创建Bean)、MyBatis(动态生成SQL映射);
- 动态代理:AOP的核心实现(如JDK动态代理);
- 自定义工具:序列化/反序列化、对象拷贝(如BeanUtils);
- 插件化开发:动态加载外部类,实现功能扩展。
八、总结
反射是Java"动态性"的核心体现,核心逻辑是:获取Class对象 → 定位目标成员(构造/字段/方法) → 按需开启暴力反射 → 操作成员。
虽然反射打破了Java的封装性(可能带来安全风险),但它是框架开发、动态化功能的基石。掌握反射的核心API和易错点,能让我们更深入理解Java底层,也能更好地使用和扩展各类框架。