第40章 Java反射(Reflection)
运行时类信息获取与动态操作详解
什么是Java反射
Java反射(Reflection)是Java语言的一个重要特性,它允许程序在运行时检查和操作类、接口、字段和方法的信息。通过反射,我们可以在运行时动态地创建对象、调用方法、访问和修改字段,甚至可以获取类的结构信息。
反射的核心概念
反射机制允许程序在运行期间获取任意一个类的内部信息,并能够操作任意一个对象的内部属性及方法。这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
反射的主要用途
运行时类型检查
在运行时确定对象的类型,获取类的详细信息,包括类名、包名、父类、接口等。
动态对象创建
根据类名动态创建对象实例,支持不同参数的构造器调用。
动态方法调用
在运行时调用对象的方法,包括私有方法,实现灵活的程序控制。
动态字段访问
获取和修改对象的字段值,包括私有字段,突破访问限制。
反射核心类
Java反射机制主要通过以下几个核心类来实现:
Class类
代表类的实体,在运行的Java应用程序中表示类和接口。每个类都有一个Class对象。
Field类
代表类的成员变量(字段),提供有关类或接口的单个字段的信息和动态访问权限。
Method类
代表类的方法,提供关于类或接口上单独某个方法的信息和访问权限。
Constructor类
代表类的构造器,提供关于类的单个构造器的信息以及对它的访问权限。
获取Class对象
获取Class对象是使用反射的第一步,Java提供了三种方式来获取Class对象:
import java.lang.reflect.*;
public class ReflectionBasics {
public static void demonstrateClassObject() {
System.out.println("=== 获取Class对象的三种方式 ===");
try {
// 方式1:类名.class
Class> clazz1 = String.class;
System.out.println("方式1 - 类名.class: " + clazz1.getName());
// 方式2:对象.getClass()
String str = "Hello";
Class> clazz2 = str.getClass();
System.out.println("方式2 - 对象.getClass(): " + clazz2.getName());
// 方式3:Class.forName()
Class> clazz3 = Class.forName("java.lang.String");
System.out.println("方式3 - Class.forName(): " + clazz3.getName());
// 验证三种方式获取的是同一个Class对象
System.out.println("三种方式获取的Class对象相等: " + (clazz1 == clazz2 && clazz2 == clazz3));
} catch (ClassNotFoundException e) {
System.err.println("类未找到: " + e.getMessage());
}
}
public static void demonstrateClassInfo() {
System.out.println("\n=== 获取类的基本信息 ===");
Class> clazz = String.class;
System.out.println("类名: " + clazz.getName());
System.out.println("简单类名: " + clazz.getSimpleName());
System.out.println("规范类名: " + clazz.getCanonicalName());
Package pkg = clazz.getPackage();
if (pkg != null) {
System.out.println("包名: " + pkg.getName());
}
int modifiers = clazz.getModifiers();
System.out.println("修饰符: " + Modifier.toString(modifiers));
System.out.println("是否为public: " + Modifier.isPublic(modifiers));
System.out.println("是否为final: " + Modifier.isFinal(modifiers));
Class> superClass = clazz.getSuperclass();
if (superClass != null) {
System.out.println("父类: " + superClass.getName());
}
Class>[] interfaces = clazz.getInterfaces();
System.out.println("实现的接口数量: " + interfaces.length);
for (Class> intf : interfaces) {
System.out.println(" - " + intf.getName());
}
}
public static void main(String[] args) {
demonstrateClassObject();
demonstrateClassInfo();
}
}
获取Class对象的方式对比
- 类名.class:编译时就确定,效率最高,适用于已知具体类的情况
- 对象.getClass():运行时获取,适用于已有对象实例的情况
- Class.forName():动态加载类,适用于类名以字符串形式提供的情况
字段反射操作
通过反射可以获取类的字段信息,并对字段进行读取和修改操作,甚至可以访问私有字段:
import java.lang.reflect.*;
public class FieldReflection {
public String publicField = "公共字段";
private String privateField = "私有字段";
protected int protectedField = 100;
static final String CONSTANT = "常量字段";
public static void demonstrateGetFields() {
System.out.println("=== 获取类的字段信息 ===");
Class> clazz = FieldReflection.class;
// 获取所有公共字段(包括继承的)
System.out.println("\n公共字段(getFields):");
Field[] publicFields = clazz.getFields();
for (Field field : publicFields) {
System.out.println(" " + field.getName() + " - " + field.getType().getSimpleName());
}
// 获取所有声明的字段(不包括继承的)
System.out.println("\n所有声明的字段(getDeclaredFields):");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
String modifiers = Modifier.toString(field.getModifiers());
System.out.println(" " + modifiers + " " + field.getType().getSimpleName() + " " + field.getName());
}
}
public static void demonstrateFieldAccess() {
System.out.println("\n=== 字段访问和修改 ===");
try {
FieldReflection obj = new FieldReflection();
Class> clazz = obj.getClass();
// 访问公共字段
Field publicField = clazz.getField("publicField");
System.out.println("公共字段原值: " + publicField.get(obj));
publicField.set(obj, "修改后的公共字段");
System.out.println("公共字段新值: " + publicField.get(obj));
// 访问私有字段
Field privateField = clazz.getDeclaredField("privateField");
privateField.setAccessible(true); // 设置可访问
System.out.println("私有字段原值: " + privateField.get(obj));
privateField.set(obj, "修改后的私有字段");
System.out.println("私有字段新值: " + privateField.get(obj));
// 访问静态字段
Field constantField = clazz.getDeclaredField("CONSTANT");
System.out.println("静态常量字段: " + constantField.get(null));
} catch (NoSuchFieldException | IllegalAccessException e) {
System.err.println("字段访问错误: " + e.getMessage());
}
}
public static void main(String[] args) {
demonstrateGetFields();
demonstrateFieldAccess();
}
}
方法反射操作
方法反射允许我们在运行时获取类的方法信息并动态调用方法,包括私有方法和重载方法:
import java.lang.reflect.*;
public class MethodReflection {
public String publicMethod(String param) {
return "公共方法被调用,参数: " + param;
}
private int privateMethod(int a, int b) {
return a + b;
}
public static void staticMethod() {
System.out.println("静态方法被调用");
}
// 重载方法示例
public void overloadedMethod() {
System.out.println("无参数的重载方法");
}
public void overloadedMethod(String param) {
System.out.println("带String参数的重载方法: " + param);
}
public void overloadedMethod(int param) {
System.out.println("带int参数的重载方法: " + param);
}
public static void demonstrateMethodInvocation() {
System.out.println("=== 方法调用 ===");
try {
MethodReflection obj = new MethodReflection();
Class> clazz = obj.getClass();
// 调用公共方法
Method publicMethod = clazz.getMethod("publicMethod", String.class);
Object result1 = publicMethod.invoke(obj, "测试参数");
System.out.println("公共方法调用结果: " + result1);
// 调用私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod", int.class, int.class);
privateMethod.setAccessible(true);
Object result2 = privateMethod.invoke(obj, 10, 20);
System.out.println("私有方法调用结果: " + result2);
// 调用静态方法
Method staticMethod = clazz.getMethod("staticMethod");
staticMethod.invoke(null); // 静态方法传入null
// 调用重载方法
Method overloaded1 = clazz.getMethod("overloadedMethod");
overloaded1.invoke(obj);
Method overloaded2 = clazz.getMethod("overloadedMethod", String.class);
overloaded2.invoke(obj, "字符串参数");
Method overloaded3 = clazz.getMethod("overloadedMethod", int.class);
overloaded3.invoke(obj, 42);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
System.err.println("方法调用错误: " + e.getMessage());
}
}
public static void main(String[] args) {
demonstrateMethodInvocation();
}
}
构造器反射操作
构造器反射允许我们在运行时动态创建对象实例,支持不同参数的构造器:
import java.lang.reflect.*;
public class ConstructorReflection {
private String name;
private int age;
private String email;
// 多个构造器示例
public ConstructorReflection() {
this.name = "默认姓名";
this.age = 0;
this.email = "default@example.com";
System.out.println("无参构造器被调用");
}
public ConstructorReflection(String name) {
this.name = name;
this.age = 0;
this.email = "default@example.com";
System.out.println("单参数构造器被调用: " + name);
}
public ConstructorReflection(String name, int age) {
this.name = name;
this.age = age;
this.email = "default@example.com";
System.out.println("双参数构造器被调用: " + name + ", " + age);
}
public ConstructorReflection(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
System.out.println("全参数构造器被调用: " + name + ", " + age + ", " + email);
}
private ConstructorReflection(int age) {
this.name = "私有构造";
this.age = age;
this.email = "private@example.com";
System.out.println("私有构造器被调用: " + age);
}
public String getInfo() {
return String.format("姓名: %s, 年龄: %d, 邮箱: %s", name, age, email);
}
public static void demonstrateObjectCreation() {
System.out.println("=== 通过反射创建对象 ===");
try {
Class> clazz = ConstructorReflection.class;
// 使用无参构造器
Constructor> noArgConstructor = clazz.getConstructor();
Object obj1 = noArgConstructor.newInstance();
System.out.println("无参构造对象: " + ((ConstructorReflection) obj1).getInfo());
// 使用单参数构造器
Constructor> oneArgConstructor = clazz.getConstructor(String.class);
Object obj2 = oneArgConstructor.newInstance("张三");
System.out.println("单参数构造对象: " + ((ConstructorReflection) obj2).getInfo());
// 使用双参数构造器
Constructor> twoArgConstructor = clazz.getConstructor(String.class, int.class);
Object obj3 = twoArgConstructor.newInstance("李四", 25);
System.out.println("双参数构造对象: " + ((ConstructorReflection) obj3).getInfo());
// 使用全参数构造器
Constructor> fullArgConstructor = clazz.getConstructor(String.class, int.class, String.class);
Object obj4 = fullArgConstructor.newInstance("王五", 30, "wangwu@example.com");
System.out.println("全参数构造对象: " + ((ConstructorReflection) obj4).getInfo());
// 使用私有构造器
Constructor> privateConstructor = clazz.getDeclaredConstructor(int.class);
privateConstructor.setAccessible(true);
Object obj5 = privateConstructor.newInstance(35);
System.out.println("私有构造对象: " + ((ConstructorReflection) obj5).getInfo());
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
System.err.println("对象创建错误: " + e.getMessage());
}
}
public static void main(String[] args) {
demonstrateObjectCreation();
}
}
反射最佳实践
使用建议
- 谨慎使用反射,优先考虑常规的面向对象编程方式
- 反射操作要进行异常处理,捕获相关的反射异常
- 使用setAccessible(true)时要考虑安全性问题
- 反射操作性能较低,避免在性能敏感的代码中频繁使用
- 缓存反射获取的Class、Method、Field对象以提高性能
- 在框架开发中合理使用反射,提供灵活性和扩展性
注意事项
- 性能影响:反射操作比直接调用慢,不适合性能敏感场景
- 安全限制:可能受到安全管理器的限制
- 代码维护:反射代码难以理解和维护,降低代码可读性
- 编译时检查:反射操作无法在编译时进行类型检查
反射应用场景
框架开发
Spring、Hibernate等框架大量使用反射实现依赖注入、ORM映射等功能。
序列化
JSON、XML序列化库使用反射将对象转换为字符串格式。
调试工具
IDE调试器使用反射显示对象的内部状态和结构信息。
插件系统
动态加载和执行插件代码,实现可扩展的应用架构。