第十一章:元编程(反射与注解)
11.1 反射机制
反射机制是 Java 的一项强大特性,它允许在运行时检查类、方法、字段等信息,甚至可以在运行时动态地创建对象、调用方法、修改字段值等。
11.1.1 Class 类
Class 类是反射机制的核心,它表示了类的元数据。在 Java 中,每个类都有一个 Class 对象,它包含了类的所有信息。
Class<?> clazz = String.class; // 获取 String 类的 Class 对象
System.out.println(clazz.getName()); // 输出:java.lang.String
11.1.2 Method 类
Method 类表示类中的方法。通过反射可以动态调用方法。
Class<?> clazz = String.class;
try {
Method method = clazz.getMethod("toUpperCase"); // 获取 String 类的 toUpperCase 方法
String str = "hello";
String result = (String) method.invoke(str); // 动态调用 toUpperCase 方法
System.out.println(result); // 输出:HELLO
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
11.1.3 Field 类
Field 类表示类中的字段。通过反射可以动态访问和修改字段的值。
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
Class<?> clazz = Person.class;
try {
Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // 允许访问私有字段
Person person = new Person("Alice");
String name = (String) field.get(person); // 获取 name 字段的值
System.out.println(name); // 输出:Alice
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
11.2 动态代理
动态代理是 Java 提供的一种机制,允许在运行时创建代理对象,代理对象可以将方法调用委托给目标对象。Java 提供了两种方式来实现动态代理:JDK 动态代理和 CGLIB 代理。
11.2.1 JDK 动态代理
JDK 动态代理基于接口实现,代理对象必须实现接口。java.lang.reflect.Proxy 类和 InvocationHandler 接口是 JDK 动态代理的核心。
interface HelloService {
void sayHello(String name);
}
class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method call");
Object result = method.invoke(target, args);
System.out.println("After method call");
return result;
}
}
public class JDKProxyExample {
public static void main(String[] args) {
HelloService helloService = new HelloServiceImpl();
HelloService proxy = (HelloService) Proxy.newProxyInstance(
HelloService.class.getClassLoader(),
new Class<?>[] { HelloService.class },
new MyInvocationHandler(helloService)
);
proxy.sayHello("Alice");
}
}
输出:
Before method call
Hello, Alice
After method call
11.2.2 CGLIB 代理
CGLIB (Code Generation Library) 通过继承目标类来实现动态代理,不要求目标类实现接口。CGLIB 比 JDK 动态代理更强大,但它需要对目标类进行字节码增强。
class HelloService {
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}
class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method call");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method call");
return result;
}
}
public class CGLIBProxyExample {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloService.class);
enhancer.setCallback(new MyMethodInterceptor());
HelloService proxy = (HelloService) enhancer.create();
proxy.sayHello("Alice");
}
}
输出:
Before method call
Hello, Alice
After method call
11.3 注解(Annotation)
注解是 Java 提供的一种元数据机制,用于给程序元素(类、方法、字段等)添加信息。注解本身不直接影响程序的执行,但可以通过工具或框架进行处理。
11.3.1 定义和使用注解
注解通过 @ 符号定义,可以应用于类、方法、字段等。自定义注解可以通过 @interface 关键字来定义。
@interface MyAnnotation {
String value() default "default value";
}
@MyAnnotation(value = "Hello World")
public class MyClass {
@MyAnnotation
public void myMethod() {
System.out.println("Method executed");
}
}
11.3.2 注解的元注解
Java 提供了几种元注解,用于注解其他注解的定义。常用的元注解有:
@Retention:指定注解的保留策略,决定注解在编译后是否可用。
@Target:指定注解可以应用于哪些程序元素。
@Documented:表示该注解应包含在 Javadoc 中。
@Inherited:指示该注解可以被子类继承。
@Retention(RetentionPolicy.RUNTIME) // 在运行时保留
@Target(ElementType.METHOD) // 只允许用于方法
@interface MyRuntimeAnnotation {
String description() default "Default description";
}
总结
反射机制:通过反射,Java 程序能够在运行时动态地获取类的信息,并进行修改。Class、Method、Field 是反射的核心类。
动态代理:动态代理允许我们在运行时创建代理对象,JDK 动态代理通过接口实现,而 CGLIB 代理则通过继承目标类实现。
注解:注解是一种元数据机制,可以用于给类、方法等元素添加额外信息,常用于框架设计和代码生成等场景。通过元注解可以控制注解的生命周期和使用场景。
掌握 Java 的反射机制、动态代理和注解,可以帮助你更好地理解框架设计,并在实际开发中灵活应用这些特性。