一、前言:什么是反射
在 Java 开发中,反射(Reflection)是一项强大的技术,可以在运行时动态地访问类的信息、创建对象、调用方法或访问字段。它在框架设计、注解处理、动态代理等方面扮演着重要角色。本文将从基础概念、常见用法、实际应用和注意事项等方面系统地介绍 Java 的反射机制。
二、反射的核心
Java 的反射 API 主要集中在 java.lang.reflect 包中,主要包括以下几个类:
类名作用Class
三、常见反射操作示例
1. 获取 Class 对象
// 方式一:通过对象调用 getClass()
Person p = new Person();
Class> clazz1 = p.getClass();
// 方式二:通过类名.class
Class> clazz2 = Person.class;
// 方式三:通过 Class.forName("全限定类名")
Class> clazz3 = Class.forName("com.example.Person");
补充:
Class 对象是 Java 反射 API 的入口点,通过Class可以获取和操作类的几乎所有信息,使得 Java 具有强大的动态能力
Class对象是怎么产生的,这就需要掌握类加载过程,请看这篇博客JVM基础--类加载过程-CSDN博客
2. 获取类信息
System.out.println(clazz.getName()); // 类的全限定名
System.out.println(clazz.getSimpleName()); // 类名
3. 创建对象
Constructor> privateCtor = clazz.getDeclaredConstructor();
privateCtor .setAccessible(true); // 解除私有限制
Object obj = privateCtor .newInstance();
getDeclaredConstructor()可以获取任意可见性(包括私有)
4. 通过反射提供的 Constructor 获取对象
Constructor> constructor = clazz.getConstructor(String.class, int.class);
Object newObj = constructor.newInstance("李四", 25);
getConstructor(...)只能调用公有的构造器
5. 获取并设置字段
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);//允许访问private字段
field.set(obj, "张三");
System.out.println(field.get(obj)); // 输出:张三
6. 调用方法
//"sayHello"为方法名 String.class是参数的类型
Method method = clazz.getDeclaredMethod("sayHello", String.class);
//关闭java语言访问机制 可以访问private方法,但绕开了 Java 的封装性,引申出来了一个问题
method.setAccessible(true);
//传入的obj是对象实例,如果调用静态方法可以不用传obj(因为静态方法属于类不需要实例即可调用)
//传入的args是方法的参数
method.invoke(obj, args);
四、反射的应用场景
1. 框架设计(如 Spring、Hibernate)
Spring 框架通过反射实现 Bean 的实例化与依赖注入,极大地解耦了代码。
拓展:Spring是怎么通过反射实现的以及Bean的生命周期和循环依赖问题 请看这篇博客
2. 动态代理
JDK 动态代理通过反射构造代理类,在 AOP(面向切面编程)中尤为常见。
拓展:
JAVA中两种动态代理方式,JDK动态代理和CGLIB动态代理 请看这篇博客
AOP(面向切面的编程)
3. 注解处理
通过反射获取类、方法、字段上的注解信息,执行相应逻辑。
五、反射的优缺点
优点:
灵活:在运行时决定行为。
通用:支持编写更通用的框架和工具。
解耦:可减少代码间的耦合性。
缺点:
性能较差:反射操作通常比直接调用慢很多。
安全限制:反射可以访问和修改私有成员,破坏了封装性和安全性。
既然面向对象最重要的特性之一就是面向对象,那么反射破坏了封装性,这是否与封装性相违背?
答案是否定的,因为反射是一种“受控的特权”,Java 并不是默认允许你访问私有成员的,而是通过 AccessibleObject.setAccessible(true) 这个“声明式”方式显式授权的,反射不是为了破坏封装,而是为了解决特殊场景下的通用性需求,比如框架底层开发、测试、序列化等。
可读性差:代码难以维护和理解。