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对象唯一性)

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(如NoSuchMethodExceptionIllegalAccessException),需手动捕获或声明抛出。

4. 性能注意事项

反射比直接调用成员的性能略低,可通过缓存Class/Constructor/Method/Field对象 优化(如框架中常用缓存池)。

七、反射的应用场景

  • 框架底层:Spring IOC(动态创建Bean)、MyBatis(动态生成SQL映射);
  • 动态代理:AOP的核心实现(如JDK动态代理);
  • 自定义工具:序列化/反序列化、对象拷贝(如BeanUtils);
  • 插件化开发:动态加载外部类,实现功能扩展。

八、总结

反射是Java"动态性"的核心体现,核心逻辑是:获取Class对象 → 定位目标成员(构造/字段/方法) → 按需开启暴力反射 → 操作成员

虽然反射打破了Java的封装性(可能带来安全风险),但它是框架开发、动态化功能的基石。掌握反射的核心API和易错点,能让我们更深入理解Java底层,也能更好地使用和扩展各类框架。

相关推荐
凤山老林2 小时前
Spring Boot 集成 TigerGraph 实现图谱分析技术方案
java·spring boot·后端·图谱分析·tigergraph
t***5442 小时前
Dev-C++中使用Clang调试有哪些常见错误
java·开发语言·c++
ydmy2 小时前
强化学习/对齐(个人理解)
开发语言·python
xuhaoyu_cpp_java2 小时前
Mybatis学习(四)
java·经验分享·笔记·学习·mybatis
.生产的驴2 小时前
SpringBoot 大文件分片上传 文件切片、断点续传与性能优化 切片技术与优化方案 文件高效上传
java·服务器·spring boot·后端·spring·spring cloud·状态模式
一叶之秋14122 小时前
哈希密钥:解锁unordered容器的极速潜能
开发语言·c++·哈希算法
t***5442 小时前
如何在Dev-C++中设置Clang编译参数
开发语言·c++
csbysj20203 小时前
PHP If...Else 语句详解
开发语言
清水白石00810 小时前
Python 编程实战全景:从基础语法到插件架构、异步性能与工程最佳实践
开发语言·python·架构