Java反射机制指的是在程序运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,这种动态获取信息以及动态调用对象方法的功能就称为Java的反射机制。

反射的核心类
Java反射机制的实现主要依赖以下几个核心类,它们都位于java.lang.reflect包下:
- Class类:反射的入口,每个类被加载后都会在内存中生成一个对应的Class对象,通过这个对象可以获取类的所有信息
- Constructor类:表示类的构造方法,通过它可以创建类的实例
- Field类:表示类的成员变量,通过它可以获取和设置成员变量的值
- Method类:表示类的方法,通过它可以调用类的方法
获取Class对象的三种方式
要使用反射,首先需要获取目标类的Class对象,常见的有三种方式:
1. 通过类名.class获取
这种方式在编译期就知道类的类型,直接使用类的class属性即可:
// 获取String类的Class对象 Class<String> stringClass = String.class; System.out.println(stringClass.getName());
2. 通过对象.getClass()获取
如果已经有类的实例对象,可以调用对象的getClass()方法获取Class对象:
String str = "test"; Class<? extends String> strClass = str.getClass(); System.out.println(strClass.getName());
3. 通过Class.forName()获取
这种方式只需要知道类的全限定名,适合编译期无法确定类类型的场景,会触发类的加载:
try {
// 传入类的全限定名,注意要处理ClassNotFoundException异常
Class<?> clazz = Class.forName("java.lang.String");
System.out.println(clazz.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}通过反射操作类的成员
获取并操作构造方法
通过Class对象可以获取类的构造方法,进而创建实例:
class User {
private String name;
private int age;
// 无参构造
public User() {
}
// 有参构造
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 获取User类的Class对象
Class<?> userClass = Class.forName("User");
// 获取无参构造并创建实例
Constructor<?> noArgConstructor = userClass.getDeclaredConstructor();
Object user1 = noArgConstructor.newInstance();
System.out.println(user1);
// 获取有参构造并创建实例,参数传入构造方法的参数类型
Constructor<?> hasArgConstructor = userClass.getDeclaredConstructor(String.class, int.class);
Object user2 = hasArgConstructor.newInstance("张三", 20);
System.out.println(user2);
}
}获取并操作成员变量
通过Class对象可以获取类的成员变量,包括私有成员,需要设置可访问性:
public class ReflectFieldDemo {
public static void main(String[] args) throws Exception {
Class<?> userClass = Class.forName("User");
// 创建User实例
Constructor<?> constructor = userClass.getDeclaredConstructor();
Object user = constructor.newInstance();
// 获取私有成员变量name,getDeclaredField可以获取所有访问权限的成员变量
Field nameField = userClass.getDeclaredField("name");
// 设置私有成员可访问
nameField.setAccessible(true);
// 给成员变量赋值
nameField.set(user, "李四");
// 获取成员变量的值
Object nameValue = nameField.get(user);
System.out.println("name的值:" + nameValue);
// 获取私有成员变量age
Field ageField = userClass.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(user, 25);
System.out.println("age的值:" + ageField.get(user));
}
}获取并调用成员方法
通过Class对象可以获取类的方法,包括私有方法,同样需要设置可访问性:
class UserService {
private String sayHello(String name) {
return "Hello, " + name;
}
public int add(int a, int b) {
return a + b;
}
}
public class ReflectMethodDemo {
public static void main(String[] args) throws Exception {
Class<?> serviceClass = Class.forName("UserService");
Constructor<?> constructor = serviceClass.getDeclaredConstructor();
Object service = constructor.newInstance();
// 获取并调用私有方法sayHello
Method sayHelloMethod = serviceClass.getDeclaredMethod("sayHello", String.class);
sayHelloMethod.setAccessible(true);
Object result1 = sayHelloMethod.invoke(service, "王五");
System.out.println("调用sayHello结果:" + result1);
// 获取并调用公有方法add
Method addMethod = serviceClass.getDeclaredMethod("add", int.class, int.class);
Object result2 = addMethod.invoke(service, 10, 20);
System.out.println("调用add结果:" + result2);
}
}反射的常见使用场景
- 框架开发:比如Spring框架的IoC容器,就是通过反射来创建和管理Bean对象
- 动态代理:JDK动态代理的实现依赖反射机制来生成代理类并调用方法
- 配置文件驱动:很多框架通过读取配置文件中的类名,利用反射动态加载类并实例化
- 注解处理:很多注解相关的功能需要反射获取标注了注解的类、方法、成员变量来进行对应处理
反射使用的注意事项
- 性能开销:反射的操作比直接调用代码性能更低,因为需要动态解析类信息,不适合在高频调用的场景中使用
- 安全限制:在使用安全管理器的环境中,反射可能会受到权限限制,无法访问私有成员
- 破坏封装性:反射可以访问类的私有成员,打破了面向对象的封装特性,使用时需要谨慎