Class<T> 类方法详解
Java 反射机制允许程序在运行时动态获取类的信息(如属性、方法、构造器等)并操作其成员,即使在编译期未知这些信息。其实现原理与 JVM 的类加载机制、运行时数据结构以及动态代理技术密切相关,核心可概括为:通过类的运行时元数据(
Class 对象),突破编译期访问限制,动态调用 JVM 内部接口操作类成员。一、反射的核心:Class 对象与类的元数据
Java 中每个类被加载后,JVM 会为其创建一个唯一的
java.lang.Class 对象,该对象存储了类的所有元数据(如类名、父类、接口、字段、方法、注解等)。反射的所有操作都围绕 Class 对象展开,它是连接运行时程序与类元数据的桥梁。Class 对象的生成时机:当类被类加载器(ClassLoader)加载到 JVM 时,JVM 会解析类的字节码(.class 文件),提取元数据并生成 Class 对象,存储在方法区(JDK 8+ 为元空间)。一个类的 Class 对象在 JVM 中是单例的,无论通过 Class.forName()、类名.class 还是 对象.getClass() 获取,都是同一个实例。二、反射的实现流程(以调用方法为例)
反射操作(如获取方法、调用方法)的底层流程可分为以下步骤,涉及 JVM 对类元数据的解析和 native 方法的调用:
1. 获取 Class 对象
通过类的全限定名、类字面量或实例对象获取目标类的
Class 对象,本质是从 JVM 方法区中查询该类的元数据引用。2. 查找目标成员(如方法、字段)
Class 类提供的 getMethod()、getDeclaredField() 等方法,会在 Class 对象存储的元数据中搜索匹配的成员(根据名称、参数类型等)。- 对于
public成员,会递归搜索父类和接口的元数据; - 对于非
public成员(private、protected),仅在当前类的元数据中查找(通过getDeclaredXXX方法)。
3. 突破访问权限检查(setAccessible(true))
默认情况下,反射遵循类的访问控制规则(如私有成员不可访问)。
AccessibleObject 类(Field、Method、Constructor 的父类)提供 setAccessible(boolean) 方法,其底层通过 JVM 的 sun.reflect.Reflection 类调用 native 方法,修改访问权限标志位,实现 “暴力反射”。4. 动态调用成员(如 Method.invoke())
调用方法的核心是
Method.invoke(Object obj, Object... args),其底层实现涉及:- 参数类型检查:验证传入的参数类型与方法声明的参数类型是否匹配(自动装箱 / 拆箱、类型转换);
- native 方法调用:通过 JVM 的
sun.reflect.NativeMethodAccessorImpl或sun.reflect.DelegatingMethodAccessorImpl调用 native 方法(如invoke0),直接操作 JVM 内部的方法调用栈,执行目标方法; - 返回值处理:将方法的返回值(基本类型自动装箱)返回给调用者,若方法抛出异常,会包装为
InvocationTargetException。
三、底层技术支撑:JVM 与 native 方法
反射的核心功能依赖 JVM 的 native 方法实现,这些方法绕过了 Java 语言的编译期检查,直接操作内存中的类元数据和对象:
-
类元数据的存储结构:JVM 在方法区中以特定数据结构(如
instanceKlass结构体)存储类的元数据,包含字段表、方法表、常量池等信息。Class对象通过指针指向这些底层结构,反射方法(如getMethods())本质是解析这些结构并返回封装后的Method数组。 -
native 方法的角色:反射中的关键操作(如
Class.forName()、Method.invoke()、Field.set())最终会调用 JVM 的 native 方法(用 C/C++ 实现),例如:Class.forName()调用JVM_FindClassFromCaller,触发类加载;Method.invoke()调用JVM_InvokeMethod,直接在 JVM 层面执行方法调用。
-
动态代理的辅助:注解(
Annotation)的实例本质是 JVM 生成的动态代理对象(实现了注解接口),当调用注解的属性方法(如@MyAnn.value())时,代理对象会从类元数据中提取属性值并返回。
四、反射的性能问题及原因
反射的性能通常比直接调用低(约慢 10-100 倍),主要原因包括:
- 运行时类型检查:反射需要在运行时验证参数类型、访问权限等,而直接调用在编译期已确定;
- native 方法调用开销:Java 调用 native 方法需要切换 JVM 执行模式(从解释执行到本地执行),存在额外开销;
- 自动装箱 / 拆箱:反射处理基本类型时需频繁装箱 / 拆箱,增加内存操作;
- 元数据查找开销:每次反射调用可能需要重新查找类元数据(可通过缓存
Method、Field对象缓解)。
五、反射的安全限制
为防止滥用反射破坏封装性,JVM 提供了一些安全限制:
- 安全管理器(
SecurityManager):早期 JVM 可通过安全管理器限制setAccessible(true)的调用(如禁止访问私有成员),但 JDK 17 已移除安全管理器; - 模块系统(JPMS):Java 9 引入的模块系统中,反射访问其他模块的私有成员需在
module-info.java中显式声明opens权限,否则会被拒绝。
总结
Java 反射的实现原理可概括为:通过
Class 对象访问 JVM 方法区中的类元数据,利用 native 方法突破编译期限制,动态操作类的成员。其核心是 JVM 对类元数据的管理和 native 接口的支持,使得程序能在运行时灵活操作类信息,这也是 Spring、MyBatis 等框架实现依赖注入、动态代理的基础。但反射的灵活性是以性能和封装性为代价的,需合理使用。-------------------------------------------------------------------------------------------------------------------
Class<T> 类方法详解
在 Java 中,
Class<T> 类是反射机制的核心类之一,它代表 Java 中类和接口的运行时类型信息。通过 Class<T> 类的方法,我们可以在运行时获取类的结构信息(如属性、方法、构造器等)、创建对象、调用方法等。以下是 Class<T> 类的常用方法详解:一、获取 Class 对象的方法
在使用
Class<T> 的方法前,需先获取目标类的 Class 对象,常见方式有:Class.forName("全类名"):通过类的全限定名(包名 + 类名)加载类并返回Class对象(常用)。类名.class:通过类字面量直接获取(编译期确定,无需处理异常)。对象.getClass():通过实例对象获取其运行时类的Class对象。
二、Class<T> 核心方法分类详解
1. 类基本信息相关
-
String getName()返回类的全限定名(包名 + 类名),例如java.lang.String。 -
String getSimpleName()返回类的简单名称(不含包名),例如String。 -
Class<?> getSuperclass()返回当前类的直接父类的Class对象(对于Object类,返回null)。 -
Class<?>[] getInterfaces()返回当前类实现的所有接口的Class数组(若为接口,返回其继承的接口)。 -
boolean isInterface()判断当前Class对象是否代表一个接口。 -
boolean isEnum()判断当前Class对象是否代表一个枚举类。 -
boolean isAnnotation()判断当前Class对象是否代表一个注解。 -
boolean isPrimitive()判断当前Class对象是否代表基本数据类型(如int.class返回true)。 -
ClassLoader getClassLoader()返回加载当前类的类加载器(引导类加载器返回null)。
2. 构造器相关(反射创建对象)
-
Constructor<T> getConstructor(Class<?>... parameterTypes)返回当前类中public 修饰的、参数类型匹配的构造器(parameterTypes为参数类型的Class数组)。若不存在匹配的 public 构造器,抛出NoSuchMethodException。 -
Constructor<?>[] getConstructors()返回当前类中所有public 构造器的数组。 -
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)返回当前类中所有修饰符(包括 private)的、参数类型匹配的构造器(可获取私有构造器)。 -
Constructor<?>[] getDeclaredConstructors()返回当前类中所有构造器(包括 private)的数组。
示例:通过构造器创建对象
java
运行
Class<?> clazz = User.class;
// 获取无参构造器并创建实例
Constructor<?> constructor = clazz.getConstructor();
User user = (User) constructor.newInstance();
// 获取带参构造器(如 String name, int age)
Constructor<?> constructor2 = clazz.getConstructor(String.class, int.class);
User user2 = (User) constructor2.newInstance("Alice", 20);
3. 方法相关(反射调用方法)
-
Method getMethod(String name, Class<?>... parameterTypes)返回当前类中public 修饰的、方法名和参数类型匹配的方法(包括从父类继承的 public 方法)。name:方法名;parameterTypes:参数类型的Class数组。
-
Method[] getMethods()返回当前类中所有public 方法(包括继承的)的数组。 -
Method getDeclaredMethod(String name, Class<?>... parameterTypes)返回当前类中所有修饰符(包括 private)的、方法名和参数类型匹配的方法(仅当前类声明的,不包括继承的)。 -
Method[] getDeclaredMethods()返回当前类中所有声明的方法(包括 private,不包括继承的)的数组。
示例:通过反射调用方法
java
运行
Class<?> clazz = User.class;
User user = new User();
// 获取 public 方法 setName(String name)
Method setNameMethod = clazz.getMethod("setName", String.class);
// 调用方法:参数为实例对象 + 方法参数
setNameMethod.invoke(user, "Bob");
// 获取 private 方法 getAge()
Method getAgeMethod = clazz.getDeclaredMethod("getAge");
getAgeMethod.setAccessible(true); // 暴力访问私有方法
int age = (int) getAgeMethod.invoke(user);
4. 字段相关(反射操作属性)
-
Field getField(String name)返回当前类中public 修饰的、名称匹配的字段(包括从父类继承的 public 字段)。 -
Field[] getFields()返回当前类中所有public 字段(包括继承的)的数组。 -
Field getDeclaredField(String name)返回当前类中所有修饰符(包括 private)的、名称匹配的字段(仅当前类声明的,不包括继承的)。 -
Field[] getDeclaredFields()返回当前类中所有声明的字段(包括 private,不包括继承的)的数组。
示例:通过反射操作字段
java
运行
Class<?> clazz = User.class;
User user = new User();
// 获取 public 字段 name
Field nameField = clazz.getField("name");
nameField.set(user, "Charlie"); // 设置字段值
// 获取 private 字段 age
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // 暴力访问私有字段
ageField.set(user, 25);
int age = (int) ageField.get(user); // 获取字段值
5. 实例化对象相关
T newInstance()(JDK 9 后过时,推荐用构造器的newInstance())通过类的无参 public 构造器创建实例。若类无无参 public 构造器,抛出InstantiationException。
6. 注解相关
-
Annotation getAnnotation(Class<? extends Annotation> annotationClass)返回当前类上指定类型的 public 注解(若未标注,返回null)。 -
Annotation[] getAnnotations()返回当前类上所有public 注解的数组。 -
Annotation getDeclaredAnnotation(Class<? extends Annotation> annotationClass)返回当前类上直接标注的指定类型注解(包括非 public 注解)。 -
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)判断当前类是否标注了指定类型的注解。
7. 其他常用方法
-
Class<?> getComponentType()若当前类是数组(如int[]、String[]),返回数组元素的Class对象(如int.class、String.class);否则返回null。 -
boolean isAssignableFrom(Class<?> cls)判断当前类是否是cls类的父类或接口(即cls是否可以赋值给当前类类型的变量)。例如:Object.class.isAssignableFrom(String.class)返回true(String是Object的子类)。
三、注意事项
- 访问权限:
getDeclaredXXX方法可以获取私有成员,但需调用setAccessible(true)才能访问(暴力反射),否则会抛出IllegalAccessException。 - 性能:反射操作绕过了编译期检查,性能略低于直接调用,频繁使用需谨慎。
- 异常处理:反射方法可能抛出多种受检异常(如
NoSuchMethodException、IllegalAccessException等),需显式处理或声明抛出。
Class<T> 类是 Java 反射机制的基础,通过它可以在运行时动态操作类的结构,广泛应用于框架(如 Spring、MyBatis)、注解处理器、序列化等场景。Field 类方法和属性详解
在 Java 反射机制中,
Field 类代表类或接口的成员变量(包括实例字段和静态字段),通过它可以在运行时获取字段的信息(如类型、修饰符、注解等),并动态修改字段的值(即使是私有字段)。以下是 Field 类的核心方法和属性详解:一、Field 类的核心属性(继承自 AccessibleObject)
Field 类继承自 AccessibleObject,该父类提供了一个关键属性用于控制访问权限:boolean accessible用于标识是否忽略访问权限检查(即 “暴力反射”)。默认值为false,表示遵循类的访问控制(如私有字段不可直接访问);若设为true,则可绕过访问权限限制,直接操作私有字段。
二、Field 类的核心方法分类详解
1. 获取字段基本信息
-
String getName()返回字段的名称(如name、age)。 -
Class<?> getType()返回字段的类型对应的Class对象(如字段为String name,则返回String.class)。 -
Class<?> getDeclaringClass()返回声明该字段的类或接口的Class对象(即字段所属的类)。 -
int getModifiers()返回字段的修饰符对应的整数编码(通过Modifier工具类可解析为具体修饰符,如public、private、static等)。示例:解析修饰符java运行Field field = User.class.getDeclaredField("age"); int modifiers = field.getModifiers(); System.out.println("是否为私有:" + Modifier.isPrivate(modifiers)); // true System.out.println("是否为静态:" + Modifier.isStatic(modifiers)); // false
2. 访问权限控制
-
void setAccessible(boolean flag)设置accessible属性的值:flag = true表示忽略访问权限检查(允许操作私有字段),flag = false则遵循原有访问控制。这是操作私有字段的关键方法,若不设置为true,直接访问私有字段会抛出IllegalAccessException。 -
boolean isAccessible()返回当前accessible属性的值,判断是否允许暴力访问。
3. 字段值的读取与修改
根据字段是否为静态,读取 / 修改值的方法略有差异(静态字段无需实例对象,非静态字段需要)。
-
Object get(Object obj)读取非静态字段的值:obj为字段所属的实例对象;返回字段的值(基本类型会自动装箱为包装类)。示例:读取实例字段java运行User user = new User("Alice", 20); Field nameField = User.class.getDeclaredField("name"); nameField.setAccessible(true); // 若为私有字段 String name = (String) nameField.get(user); // 读取 user 对象的 name 字段值 -
void set(Object obj, Object value)修改非静态字段的值:obj为实例对象,value为新值(基本类型需传入包装类,如int对应Integer)。示例:修改实例字段java运行nameField.set(user, "Bob"); // 将 user 对象的 name 改为 "Bob" -
Object getStatic(Object obj)(JDK 9+,推荐)读取静态字段的值:obj可传null(因为静态字段属于类,与实例无关)。替代旧写法get(null),更清晰地标识静态字段。 -
void setStatic(Object obj, Object value)(JDK 9+,推荐)修改静态字段的值:obj可传null。示例:操作静态字段java运行Field countField = User.class.getDeclaredField("count"); // 假设 count 是 static 字段 countField.setAccessible(true); int count = (Integer) countField.getStatic(null); // 读取静态字段 countField.setStatic(null, 100); // 修改静态字段为 100 -
针对基本类型的便捷方法(避免装箱 / 拆箱):若字段类型为基本类型(如
int、boolean),可直接使用以下方法,性能更优:getInt(obj)、setInt(obj, int value)getBoolean(obj)、setBoolean(obj, boolean value)同理还有getByte()、getChar()、getDouble()、getFloat()、getLong()、getShort()及对应的setXxx方法。示例:操作 int 类型字段java运行Field ageField = User.class.getDeclaredField("age"); ageField.setAccessible(true); int age = ageField.getInt(user); // 直接获取 int 值(无需拆箱) ageField.setInt(user, 25); // 直接设置 int 值(无需装箱)
4. 注解相关方法
-
Annotation getAnnotation(Class<? extends Annotation> annotationClass)返回字段上指定类型的public 注解(若未标注,返回null)。 -
Annotation[] getAnnotations()返回字段上所有public 注解的数组。 -
Annotation getDeclaredAnnotation(Class<? extends Annotation> annotationClass)返回字段上直接标注的指定类型注解(包括非 public 注解)。 -
Annotation[] getDeclaredAnnotations()返回字段上所有直接标注的注解(包括非 public,不包括继承的注解)。 -
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)判断字段是否标注了指定类型的注解。示例:检查字段是否有@NotNull注解java运行boolean hasNotNull = ageField.isAnnotationPresent(NotNull.class);
5. 其他常用方法
-
boolean isEnumConstant()判断当前字段是否是枚举类的常量(枚举类的每个枚举值都是一个public static final字段)。 -
Object getGenericType()返回字段的泛型类型(如List<String>字段,返回ParameterizedType实例),用于处理泛型信息。 -
String toGenericString()返回字段的字符串表示,包含修饰符、泛型类型、字段名和所属类(比toString()更详细)。
三、使用注意事项
- 访问私有字段必须设置
setAccessible(true):否则会抛出IllegalAccessException。 - 静态字段操作无需实例对象:调用
get()/set()时,obj参数可传null(推荐 JDK 9+ 的getStatic()/setStatic()更清晰)。 - 基本类型与包装类的区别:
get()方法返回基本类型的包装类,而getInt()等方法直接返回基本类型,避免装箱 / 拆箱开销。 - 性能影响:反射操作(尤其是修改
accessible属性)会绕过编译期检查,性能略低于直接访问字段,频繁操作需谨慎。 - 安全管理器限制:若 JVM 启用了安全管理器(
SecurityManager),setAccessible(true)可能被拒绝(抛出SecurityException),但现代 Java 中安全管理器已逐渐被废弃(JDK 17 中移除)。
Field 类是反射操作字段的核心工具,广泛应用于对象序列化、ORM 框架(如 MyBatis 映射数据库字段)、注解处理器等场景,通过它可以突破编译期的访问限制,实现动态修改对象状态。Method 类方法和属性详解
在 Java 反射机制中,
Method 类代表类或接口的方法(包括实例方法、静态方法、抽象方法等),通过它可以在运行时获取方法的信息(如名称、参数、返回值、修饰符等),并动态调用方法(即使是私有方法)。以下是 Method 类的核心方法和属性详解:一、Method 类的继承与核心属性
Method 类继承自 AccessibleObject(父类提供访问权限控制),并实现了 Member 接口(定义成员的基本信息)。其核心属性(继承自 AccessibleObject):boolean accessible用于控制是否忽略访问权限检查(默认false,遵循访问控制;设为true可 “暴力反射” 访问私有方法)。
二、Method 类的核心方法分类详解
1. 获取方法基本信息
-
String getName()返回方法的名称(如setName、getAge)。 -
Class<?> getReturnType()返回方法的返回值类型对应的Class对象(如方法返回String,则返回String.class;无返回值则返回void.class)。 -
Class<?> getDeclaringClass()返回声明该方法的类或接口的Class对象(即方法所属的类)。 -
int getModifiers()返回方法的修饰符对应的整数编码(通过Modifier工具类解析,如public、private、static、final、abstract等)。示例:解析修饰符java运行Method method = User.class.getDeclaredMethod("getAge"); int modifiers = method.getModifiers(); System.out.println("是否为私有:" + Modifier.isPrivate(modifiers)); // true System.out.println("是否为静态:" + Modifier.isStatic(modifiers)); // false -
Class<?>[] getParameterTypes()返回方法参数类型的Class数组(按参数声明顺序排列)。例如:void setName(String name, int age)方法,返回[String.class, int.class]。 -
Class<?>[] getExceptionTypes()返回方法声明抛出的异常类型的Class数组(即throws后的异常类型)。
2. 访问权限控制
-
void setAccessible(boolean flag)设置accessible属性:flag = true表示忽略访问权限检查(允许调用私有方法),flag = false则遵循原有访问控制。调用私有方法前必须设置为true,否则会抛出IllegalAccessException。 -
boolean isAccessible()返回当前accessible属性的值,判断是否允许暴力访问。
3. 方法调用(核心功能)
通过
invoke 方法动态调用目标方法,根据方法是否为静态,参数略有差异:-
Object invoke(Object obj, Object... args)调用方法并返回结果:obj:方法所属的实例对象(静态方法可传null);args:方法的参数列表(基本类型需传入包装类,如int对应Integer;无参数则传空或null);- 返回值:方法的返回结果(无返回值则返回
null;基本类型会自动装箱为包装类)。
示例 1:调用实例方法java运行User user = new User(); // 获取 public 方法 setName(String name) Method setNameMethod = User.class.getMethod("setName", String.class); // 调用方法:参数为实例对象 + 方法参数 setNameMethod.invoke(user, "Alice"); // 等价于 user.setName("Alice")示例 2:调用私有方法java运行// 获取 private 方法 getAge() Method getAgeMethod = User.class.getDeclaredMethod("getAge"); getAgeMethod.setAccessible(true); // 暴力访问私有方法 int age = (Integer) getAgeMethod.invoke(user); // 等价于 user.getAge()示例 3:调用静态方法java运行// 获取静态方法 static void printInfo() Method printMethod = User.class.getMethod("printInfo"); printMethod.invoke(null); // 静态方法无需实例,obj 传 null,等价于 User.printInfo()
4. 泛型相关方法
用于获取方法的泛型信息(如泛型返回值、泛型参数):
-
Type getGenericReturnType()返回方法的泛型返回值类型(如List<String>类型的返回值,返回ParameterizedType实例)。 -
Type[] getGenericParameterTypes()返回方法泛型参数类型的数组(如void method(List<T> list),返回泛型参数类型)。 -
Type[] getGenericExceptionTypes()返回方法声明抛出的泛型异常类型数组。示例:处理泛型返回值java运行Method method = MyClass.class.getMethod("getList"); Type returnType = method.getGenericReturnType(); if (returnType instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType) returnType; Type[] actualTypeArgs = paramType.getActualTypeArguments(); // 获取泛型实际类型(如 String.class) }
5. 注解相关方法
-
Annotation getAnnotation(Class<? extends Annotation> annotationClass)返回方法上指定类型的public 注解(未标注则返回null)。 -
Annotation[] getAnnotations()返回方法上所有public 注解的数组。 -
Annotation getDeclaredAnnotation(Class<? extends Annotation> annotationClass)返回方法上直接标注的指定类型注解(包括非 public 注解)。 -
Annotation[] getDeclaredAnnotations()返回方法上所有直接标注的注解(包括非 public,不包括继承的注解)。 -
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)判断方法是否标注了指定类型的注解。示例:检查方法是否有@Override注解java运行boolean isOverride = method.isAnnotationPresent(Override.class);
6. 其他常用方法
-
boolean isDefault()判断当前方法是否是接口中的默认方法(即带default修饰符的方法,JDK 8+ 新增)。 -
boolean isVarArgs()判断方法是否包含可变参数(如void method(String... args)则返回true)。 -
String toGenericString()返回方法的详细字符串表示,包含修饰符、泛型信息、返回值类型、方法名、参数和所属类(比toString()更完整)。 -
Method getBridgeMethod()返回当前方法对应的桥接方法(桥接方法是编译器为泛型类型擦除生成的合成方法,通常用于实现泛型的多态)。
三、使用注意事项
- 调用私有方法必须设置
setAccessible(true):否则会抛出IllegalAccessException。 - 静态方法调用时
obj传null:静态方法属于类,与实例无关,传入实例对象会被忽略。 - 参数类型匹配:
invoke的args参数类型必须与方法声明的参数类型兼容(基本类型会自动装箱,如int可传Integer),否则会抛出IllegalArgumentException。 - 方法抛出异常的处理:若被调用的方法本身抛出异常,
invoke会将其包装为InvocationTargetException,需通过getCause()获取原始异常。示例:java运行try { method.invoke(obj); } catch (InvocationTargetException e) { Throwable originalException = e.getCause(); // 获取方法实际抛出的异常 } - 性能影响:反射调用比直接调用性能低(约慢 10-100 倍),频繁调用需谨慎(可通过
MethodHandle优化)。 - 合成方法:编译器可能生成合成方法(如内部类访问外部类私有成员时),
isSynthetic()方法可判断是否为合成方法(通常无需手动处理)。
Method 类是反射调用方法的核心工具,广泛应用于依赖注入(如 Spring)、动态代理、框架底层(如 MyBatis 动态调用 SQL 方法)等场景,通过它可以在运行时灵活调用任意方法,突破编译期的限制。Annotation 类方法和属性详解
在 Java 中,
Annotation 是所有注解的顶级接口,所有注解 类型都隐式实现了该接口(类似所有类隐式继承 Object)。Annotation 接口本身不定义具体注解的属性,而是提供了用于操作注解的通用方法。以下是 Annotation 接口的核心方法及注解类型的属性特性详解:一、Annotation 接口的核心方法
Annotation 接口定义了 4 个核心方法,所有注解类型都继承这些方法:-
Class<? extends Annotation> annotationType()返回当前注解实例对应的注解类型(即注解的Class对象)。例如:对于@Override注解实例,调用该方法返回Override.class。java运行Override overrideAnn = MyClass.class.getMethod("toString").getAnnotation(Override.class); Class<? extends Annotation> annType = overrideAnn.annotationType(); // 返回 Override.class -
boolean equals(Object obj)重写Object的equals方法,判断两个注解实例是否相等。注解相等的条件:- 类型相同(
annotationType()返回的Class对象相同); - 所有属性值对应相等(基本类型值相等,引用类型对象
equals为true)。
- 类型相同(
-
int hashCode()重写Object的hashCode方法,返回注解的哈希值。哈希值计算规则:所有属性的哈希值之和(属性值为基本类型则直接计算,引用类型则取其hashCode;若属性为数组,则按数组元素计算)。公式:(127 * "属性名".hashCode()) ^ 属性值.hashCode()累加。 -
String toString()返回注解的字符串表示,格式通常为@注解类型(属性1=值1, 属性2=值2, ...)。例如:@MyAnnotation(name="test", age=20)。
二、注解类型的属性(自定义注解的成员)
注解的 “属性” 本质是注解类型中声明的抽象方法(无参数、无异常声明),这些方法的返回值即为属性的类型。当使用注解时,需为属性赋值(除非有默认值)。
1. 注解属性的声明规则
-
只能声明为抽象方法,不能有方法体;
-
方法无参数(不能声明参数);
-
返回值类型只能是:
- 基本数据类型(
int、boolean、char等); String、Class、enum(枚举);- 其他注解类型;
- 以上类型的一维数组(如
int[]、String[])。
示例:自定义注解的属性声明java运行public @interface MyAnnotation { // 基本类型属性 int age(); // String 类型属性 String name() default "defaultName"; // 带默认值 // 枚举类型属性 Gender gender() default Gender.MALE; // 其他注解类型属性 MySubAnnotation subAnn(); // 数组类型属性 String[] hobbies(); } enum Gender { MALE, FEMALE } @interface MySubAnnotation { String value(); } - 基本数据类型(
2. 注解属性的赋值规则
使用注解时,需通过键值对为属性赋值(格式:
@注解名(属性1=值1, 属性2=值2, ...)),特殊规则:-
默认值属性可省略赋值:若属性声明了
default,使用时可不指定该属性(自动使用默认值)。例如:@MyAnnotation(age=20, subAnn=@MySubAnnotation("sub"), hobbies={})(name和gender用默认值)。 -
单属性名
value的简写:若注解只有一个属性且名为value,赋值时可省略value=。例如:@MySubAnnotation("sub")等价于@MySubAnnotation(value="sub")。 -
数组属性赋值:
- 单个元素可省略大括号:
hobbies="reading"等价于hobbies={"reading"}; - 多个元素必须用大括号:
hobbies={"reading", "sports"}。
- 单个元素可省略大括号:
-
枚举 / 注解属性赋值:
- 枚举值:直接使用枚举常量(如
gender=Gender.FEMALE); - 注解属性:嵌套注解(如
subAnn=@MySubAnnotation("sub"))。
- 枚举值:直接使用枚举常量(如
3. 获取注解属性的值(通过反射)
通过
Annotation 接口无法直接获取属性值,需结合反射(如 Field、Method、Class 的 getAnnotation 方法)和注解类型的方法调用:java
运行
// 1. 获取注解实例
MyAnnotation ann = MyClass.class.getAnnotation(MyAnnotation.class);
// 2. 调用注解的抽象方法获取属性值(本质是通过动态代理实现)
int age = ann.age(); // 获取 age 属性值
String name = ann.name(); // 获取 name 属性值(默认值或显式赋值)
Gender gender = ann.gender(); // 获取枚举属性值
MySubAnnotation subAnn = ann.subAnn(); // 获取嵌套注解
String[] hobbies = ann.hobbies(); // 获取数组属性值
原理:注解实例本质是 JVM 生成的动态代理对象,调用注解的抽象方法(如
age())时,代理对象会返回该属性的赋值或默认值。三、注解的特殊类型与标记注解
-
标记注解(Marker Annotation):无任何属性的注解(仅作为标记),例如
@Override、@Deprecated。使用时无需赋值:@Override。 -
元注解(Meta Annotation):用于修饰其他注解的注解(如
@Target、@Retention),其属性控制被修饰注解的行为(如生效范围、保留周期)。
四、注意事项
-
注解属性无默认值则必须赋值:使用注解时,若属性未声明
default,必须显式赋值,否则编译报错。 -
数组属性不能为多维数组:注解属性的数组类型只能是一维(如
int[][]不允许)。 -
注解属性的返回值不能为
null:赋值时需确保属性值非null(基本类型有默认值,引用类型需显式赋值非null)。 -
注解是 “被动” 的:注解本身不包含逻辑,需通过反射或注解处理器(如
AnnotationProcessor)解析才能生效(例如 Spring 的@Autowired需要框架解析并执行依赖注入)。
Annotation 接口是 Java 注解机制的基础,其方法提供了注解的通用操作,而具体注解的功能则通过属性定义和外部解析逻辑实现。注解广泛应用于代码标记(如 @Override)、配置声明(如 Spring 注解)、编译期检查(如 @NonNull)等场景。-------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------

浙公网安备 33010602011771号