java注解与反射(非常详细, 带有很多样例)
下面是详细地讲解 Java 中的注解与反射,并提供了很多的示例来帮助理解。
Java 注解(Annotations)
1. 注解的基本概念
注解(Annotation)是 Java 5 引入的一种用于为代码元素(类、方法、字段、参数等)添加元数据的机制。这些元数据可以在编译时、类加载时或运行时被读取并使用。注解不会直接影响代码的执行,但可以通过工具或框架来处理这些元数据,以实现一些功能。
2. 内置注解
Java 提供了一些常用的内置注解:
@Override
:用于标注方法,表明该方法重写了父类的方法。@Deprecated
:用于标注过时的元素,编译器看到使用了这个元素会发出警告。@SuppressWarnings
:用于抑制编译器警告。
public class AnnotationExample { @Override public String toString() { return "AnnotationExample"; } @Deprecated public void deprecatedMethod() { // 这个方法已经过时 } @SuppressWarnings("unchecked") public void suppressWarningsExample() { List rawList = new ArrayList(); rawList.add("example"); } }
3. 自定义注解
你可以定义自己的注解,并通过元注解(meta-annotation)来指定注解的行为。
元注解:
@Retention
:指定注解的保留策略,取值有RetentionPolicy.SOURCE
、RetentionPolicy.CLASS
、RetentionPolicy.RUNTIME
。@Target
:指定注解可以应用的程序元素,取值有ElementType.TYPE
、ElementType.FIELD
、ElementType.METHOD
、ElementType.PARAMETER
等。@Documented
:指定注解是否包含在 Javadoc 中。@Inherited
:指定注解是否可以被子类继承。
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) // 运行时保留 @Target(ElementType.METHOD) // 作用于方法 public @interface MyAnnotation { String value(); } public class MyClass { // 当 MyClass 的 myMethod 方法被注释了这个注解后,可以通过反射在运行时获取这个注解及其值。下面的"结合注解与反射"有例子 @MyAnnotation(value = "example") public void myMethod() { System.out.println("Method with MyAnnotation"); } }
Java 反射(Reflection)
1. 反射的基本概念
反射是一种运行时机制,允许程序在运行时检查和操作类、方法、字段等。通过反射,你可以:
- 获取类的详细信息(类名、修饰符、父类、接口等)。
- 获取类的方法、构造函数、字段等。
- 动态调用方法或构造函数。
- 动态访问和修改字段的值。
2. 获取类的信息
- 获取 Class 对象
有多种方法可以获取一个类的 Class 对象:
Class.forName(String className):
通过类的完全限定名获取 Class 对象。
ClassName.class:
通过类的字面常量获取 Class 对象。
object.getClass():
通过对象实例获取 Class 对象。
// 获取 Class 对象的三种方式 Class<?> clazz1 = Class.forName("java.util.ArrayList"); Class<?> clazz2 = ArrayList.class; ArrayList<String> list = new ArrayList<>(); Class<?> clazz3 = list.getClass();
3. 获取类的成员
getDeclaredFields():
获取类的所有字段(包括私有字段)。
getDeclaredMethods():
获取类的所有方法(包括私有方法)。
getDeclaredConstructors():
获取类的所有构造函数。
getField(String name):
获取类的指定字段(不包括私有字段)。
getMethod(String name, Class<?>... parameterTypes):
获取类的指定方法(不包括私有方法)。
getConstructor(Class<?>... parameterTypes):
获取类的指定构造函数。
Class<?> clazz = Class.forName("java.util.ArrayList"); // 获取所有声明的字段 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println("字段: " + field.getName()); } // 获取所有声明的方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { System.out.println("方法: " + method.getName()); } // 获取所有声明的构造函数 Constructor<?>[] constructors = clazz.getDeclaredConstructors(); for (Constructor<?> constructor : constructors) { System.out.println("构造函数: " + constructor.getName()); }
4. 动态创建对象
newInstance():
使用无参构造函数创建对象。
Constructor.newInstance(Object... initargs):
使用指定构造函数创建对象。
Class<?> clazz = Class.forName("java.util.ArrayList"); // 使用无参构造函数创建对象 Object obj1 = clazz.newInstance(); // 使用带参数的构造函数创建对象 Constructor<?> constructor = clazz.getConstructor(Collection.class); Collection<String> collection = Arrays.asList("A", "B", "C"); Object obj2 = constructor.newInstance(collection); System.out.println(obj1); System.out.println(obj2);
5. 动态调用方法
Method.invoke(Object obj, Object... args):
调用指定对象的该方法。
Class<?> clazz = Class.forName("java.util.ArrayList"); Method method = clazz.getMethod("add", Object.class); ArrayList<String> list = new ArrayList<>(); method.invoke(list, "Hello"); System.out.println(list); // 输出: [Hello]
6. 动态访问和修改字段
Field.get(Object obj):
获取指定对象中此字段的值。
Field.set(Object obj, Object value):
设置指定对象中此字段的值。
class MyClass { private String field = "Initial Value"; } Class<?> clazz = Class.forName("MyClass"); Field field = clazz.getDeclaredField("field"); field.setAccessible(true); // 如果字段是私有的,需要设置可访问 MyClass obj = new MyClass(); System.out.println("原始字段值: " + field.get(obj)); // 获取字段值 field.set(obj, "New Value"); // 设置字段值 System.out.println("修改后的字段值: " + field.get(obj)); // 获取字段值
结合注解与反射
注解与反射的结合非常常见,尤其在框架中,例如 Spring 和 Hibernate。通过反射机制,你可以在运行时读取注解信息,并根据这些信息执行特定的操作。
示例:简单的依赖注入
以下示例展示了如何通过注解和反射实现简单的依赖注入:
import java.lang.annotation.*; import java.lang.reflect.*; // 定义注解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface Inject { } // 服务类 class Service { public void serve() { System.out.println("Service is serving"); } } // 客户端类 class Client { @Inject private Service service; public void doSomething() { service.serve(); } } // 注入依赖的工具类 public class DependencyInjector { public static void main(String[] args) throws Exception { Client client = new Client(); injectDependencies(client); client.doSomething(); } public static void injectDependencies(Object obj) throws Exception { Class<?> clazz = obj.getClass(); for (Field field : clazz.getDeclaredFields()) { // 遍历client的所有字段(变量). if (field.isAnnotationPresent(Inject.class)) { // 获取带有Inject注解的变量, 把它注入到 Client 中 field.setAccessible(true); Object dependency = field.getType().getConstructor().newInstance(); field.set(obj, dependency); // 通过field的set方法将service实例注入到client中 } } } }
在这个示例中:
@Inject
注解用于标注需要注入的字段。DependencyInjector
类通过反射获取Client
类中带有@Inject
注解的字段,并动态实例化Service
类的对象,注入到Client
类的实例中。Client
类调用doSomething
方法时,Service
类的实例已经被注入并可以使用。
总结
注解和反射是 Java 中非常强大和灵活的机制,通过它们可以实现许多高级功能,例如依赖注入、AOP、动态代理等。在实际开发中,理解和熟练运用这些技术,可以帮助你编写出更加灵活、可扩展的代码。
文章到这里就这束了!~
其他文章地址:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步