Java 基础(十一)反射
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对象唯一性):
// 方式1:全类名加载Classclazz1=Class.forName("AA426.Animal");// 方式2:类名.classClassclazz2=Animal.class;// 方式3:对象.getClass()Animalanimal=newAnimal("cat",18,"white");Classclazz3=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())。
实战示例(操作私有构造):
// 获取Student类的Class对象Classclazz=Student.class;// 获取私有无参构造Constructorconstructor=clazz.getDeclaredConstructor();// 开启暴力反射(突破private限制)constructor.setAccessible(true);// 创建Student实例Studentstudent=(Student)constructor.newInstance();// 同理:获取私有有参构造Constructorconstructor2=clazz.getDeclaredConstructor(String.class,Integer.class);constructor2.setAccessible(true);Studentstudent2=(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字段:反射可强制修改(需开启暴力反射)。
实战示例(读写字段):
Classclazz=Student.class;Studentstudent=(Student)clazz.getDeclaredConstructor().newInstance();// 1. 操作私有字段nameFieldnameField=clazz.getDeclaredField("name");nameField.setAccessible(true);// 暴力反射nameField.set(student,"张三");// 写值Stringname=(String)nameField.get(student);// 取值System.out.println(name);// 输出:张三// 2. 操作final字段stuIdFieldstuIdField=clazz.getDeclaredField("stuId");stuIdField.setAccessible(true);stuIdField.set(student,1002);// 强制修改final字段System.out.println(stuIdField.get(student));// 输出:1002// 3. 操作静态字段school(对象传null)FieldschoolField=clazz.getDeclaredField("school");schoolField.setAccessible(true);schoolField.set(null,"清华大学");// 静态字段对象传nullSystem.out.println(schoolField.get(null));// 输出:清华大学五、反射操作成员方法
成员方法实现对象的行为,反射可调用任意权限的方法,包括静态方法、私有方法。
1. 获取方法的核心API
| API | 作用 |
|---|---|
getDeclaredMethods() | 获取所有方法(含私有、protected、默认、public) |
getMethods() | 仅获取public方法(含父类继承的public方法) |
getDeclaredMethod("方法名", 参数类型...) | 获取指定方法(含私有) |
2. 关键操作:调用方法
- 调用方法:
Method.invoke(对象实例, 方法参数...); - 私有方法:需
setAccessible(true); - 静态方法:
invoke时对象参数传null。
实战示例(调用方法):
Classclazz=Student.class;Studentstudent=(Student)clazz.getDeclaredConstructor().newInstance();// 1. 调用静态方法showSchool(对象传null)MethodshowSchoolMethod=clazz.getDeclaredMethod("showSchool");showSchoolMethod.invoke(null);// 输出:静态方法执行// 2. 调用私有方法flayMethodflayMethod=clazz.getDeclaredMethod("flay");flayMethod.setAccessible(true);// 暴力反射flayMethod.invoke(student);// 输出:私有自定义方法执行// 3. 调用带参方法(如Animal的show方法)ClassanimalClazz=Animal.class;Animalanimal=(Animal)animalClazz.getDeclaredConstructor().newInstance();MethodshowMethod=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底层,也能更好地使用和扩展各类框架。
