Java中的反射
大约 5 分钟
Java反射(Reflection)是Java语言的一个重要特性,它允许程序在运行时检查和操作类、接口、字段和方法。通过反射,我们可以在运行时动态地创建对象、调用方法、访问字段等,即使在编译时并不知道这些类的具体信息。
反射的基本概念
反射机制的核心是java.lang.Class类和java.lang.reflect包中的类,如Field、Method、Constructor等。通过这些类,我们可以获取类的元数据并进行操作。
1. 反射访问字段
通过反射可以访问类的公共和私有字段。
import java.lang.reflect.Field;
class Person {
public String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class FieldReflectionDemo {
public static void main(String[] args) throws Exception {
// 获取Class对象
Class<?> clazz = Person.class;
// 创建实例
Person person = (Person) clazz.getConstructor(String.class, int.class)
.newInstance("张三", 25);
// 访问公共字段
Field nameField = clazz.getField("name");
System.out.println("name字段值: " + nameField.get(person));
nameField.set(person, "李四");
System.out.println("修改后的name字段值: " + nameField.get(person));
// 访问私有字段
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // 设置可访问私有字段
System.out.println("age字段值: " + ageField.get(person));
ageField.set(person, 30);
System.out.println("修改后的age字段值: " + ageField.get(person));
}
}2. 反射调用方法
通过反射可以调用类的公共和私有方法。
import java.lang.reflect.Method;
class Calculator {
public int add(int a, int b) {
return a + b;
}
private int multiply(int a, int b) {
return a * b;
}
public static String getVersion() {
return "1.0";
}
}
public class MethodReflectionDemo {
public static void main(String[] args) throws Exception {
Class<?> clazz = Calculator.class;
Object calculator = clazz.newInstance();
// 调用公共实例方法
Method addMethod = clazz.getMethod("add", int.class, int.class);
Object result = addMethod.invoke(calculator, 5, 3);
System.out.println("add方法结果: " + result);
// 调用私有实例方法
Method multiplyMethod = clazz.getDeclaredMethod("multiply", int.class, int.class);
multiplyMethod.setAccessible(true);
Object multiplyResult = multiplyMethod.invoke(calculator, 4, 6);
System.out.println("multiply方法结果: " + multiplyResult);
// 调用静态方法
Method versionMethod = clazz.getMethod("getVersion");
Object version = versionMethod.invoke(null); // 静态方法不需要实例
System.out.println("版本号: " + version);
}
}3. 反射调用构造方法
通过反射可以调用不同的构造方法创建对象。
import java.lang.reflect.Constructor;
class Student {
private String name;
private int age;
private String school;
public Student() {
this.name = "未知";
this.age = 0;
this.school = "未知学校";
}
public Student(String name) {
this.name = name;
this.age = 0;
this.school = "未知学校";
}
public Student(String name, int age) {
this.name = name;
this.age = age;
this.school = "未知学校";
}
public Student(String name, int age, String school) {
this.name = name;
this.age = age;
this.school = school;
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + ", school='" + school + "'}";
}
}
public class ConstructorReflectionDemo {
public static void main(String[] args) throws Exception {
Class<?> clazz = Student.class;
// 调用无参构造方法
Constructor<?> constructor1 = clazz.getConstructor();
Student student1 = (Student) constructor1.newInstance();
System.out.println("无参构造: " + student1);
// 调用一个参数的构造方法
Constructor<?> constructor2 = clazz.getConstructor(String.class);
Student student2 = (Student) constructor2.newInstance("张三");
System.out.println("一个参数构造: " + student2);
// 调用两个参数的构造方法
Constructor<?> constructor3 = clazz.getConstructor(String.class, int.class);
Student student3 = (Student) constructor3.newInstance("李四", 20);
System.out.println("两个参数构造: " + student3);
// 调用三个参数的构造方法
Constructor<?> constructor4 = clazz.getConstructor(String.class, int.class, String.class);
Student student4 = (Student) constructor4.newInstance("王五", 22, "清华大学");
System.out.println("三个参数构造: " + student4);
}
}4. 获取继承关系
通过反射可以获取类的继承关系和实现的接口。
import java.lang.reflect.Method;
import java.util.Arrays;
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
class Animal {
public void eat() {
System.out.println("动物在吃东西");
}
}
class Bird extends Animal implements Flyable {
@Override
public void fly() {
System.out.println("鸟在飞翔");
}
}
class Duck extends Bird implements Swimmable {
@Override
public void swim() {
System.out.println("鸭子在游泳");
}
public void quack() {
System.out.println("鸭子在嘎嘎叫");
}
}
public class InheritanceReflectionDemo {
public static void main(String[] args) {
Class<?> duckClass = Duck.class;
// 获取父类
Class<?> superClass = duckClass.getSuperclass();
System.out.println("Duck的父类: " + superClass.getName());
// 获取所有父类
System.out.println("Duck的继承链:");
Class<?> currentClass = duckClass;
while (currentClass != null) {
System.out.println(" " + currentClass.getName());
currentClass = currentClass.getSuperclass();
}
// 获取实现的接口
Class<?>[] interfaces = duckClass.getInterfaces();
System.out.println("Duck实现的接口: " + Arrays.toString(interfaces));
// 获取所有接口(包括父类实现的接口)
System.out.println("Duck的所有接口:");
getAllInterfaces(duckClass);
// 检查继承关系
System.out.println("Duck是Animal的子类吗? " + Animal.class.isAssignableFrom(duckClass));
System.out.println("Duck实现了Flyable接口吗? " + Flyable.class.isAssignableFrom(duckClass));
System.out.println("Duck实现了Swimmable接口吗? " + Swimmable.class.isAssignableFrom(duckClass));
}
public static void getAllInterfaces(Class<?> clazz) {
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> intf : interfaces) {
System.out.println(" " + intf.getName());
}
Class<?> superClass = clazz.getSuperclass();
if (superClass != null) {
getAllInterfaces(superClass);
}
}
}5. 动态代理
动态代理允许在运行时创建一个实现指定接口的代理对象,可以在调用方法前后添加额外的处理逻辑。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义接口
interface UserService {
void addUser(String username);
void deleteUser(String username);
String getUser(String username);
}
// 实现类
class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("添加用户: " + username);
}
@Override
public void deleteUser(String username) {
System.out.println("删除用户: " + username);
}
@Override
public String getUser(String username) {
System.out.println("查询用户: " + username);
return "用户信息: " + username;
}
}
// 动态代理处理器
class LoggingInvocationHandler implements InvocationHandler {
private Object target;
public LoggingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始执行方法: " + method.getName());
long startTime = System.currentTimeMillis();
Object result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println("方法执行完成: " + method.getName() + ", 耗时: " + (endTime - startTime) + "ms");
return result;
}
}
public class DynamicProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService userService = new UserServiceImpl();
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(), // 类加载器
new Class[]{UserService.class}, // 要实现的接口
new LoggingInvocationHandler(userService) // 调用处理器
);
// 通过代理对象调用方法
proxy.addUser("张三");
System.out.println("-------------------");
proxy.deleteUser("李四");
System.out.println("-------------------");
String result = proxy.getUser("王五");
System.out.println("返回结果: " + result);
// 验证代理对象的类型
System.out.println("proxy是UserService的实例吗? " + (proxy instanceof UserService));
System.out.println("proxy的类型: " + proxy.getClass().getName());
}
}反射的优缺点
优点:
- 灵活性:可以在运行时动态地操作类和对象
- 通用性:可以编写通用的代码来处理不同类型的对象
- 框架支持:许多框架(如Spring、Hibernate)都依赖反射机制
缺点:
- 性能开销:反射操作比直接代码调用慢
- 安全性问题:可以访问私有成员,破坏封装性
- 代码复杂性:使代码变得复杂,难以理解和维护
- 编译时检查缺失:类型检查推迟到运行时,容易出现运行时错误
使用场景
反射机制常用于以下场景:
- 框架开发(如Spring的依赖注入)
- 序列化和反序列化
- 注解处理
- 动态代理
- 配置文件解析
- JDBC驱动加载
通过以上示例,我们可以看到反射机制的强大功能,但同时也需要注意其性能和安全方面的考虑,在实际开发中应谨慎使用。