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 成员(privateprotected),仅在当前类的元数据中查找(通过 getDeclaredXXX 方法)。

3. 突破访问权限检查(setAccessible(true)

默认情况下,反射遵循类的访问控制规则(如私有成员不可访问)。AccessibleObject 类(FieldMethodConstructor 的父类)提供 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 语言的编译期检查,直接操作内存中的类元数据和对象:
  1. 类元数据的存储结构:JVM 在方法区中以特定数据结构(如 instanceKlass 结构体)存储类的元数据,包含字段表、方法表、常量池等信息。Class 对象通过指针指向这些底层结构,反射方法(如 getMethods())本质是解析这些结构并返回封装后的 Method 数组。
  2. native 方法的角色:反射中的关键操作(如 Class.forName()Method.invoke()Field.set())最终会调用 JVM 的 native 方法(用 C/C++ 实现),例如:
    • Class.forName() 调用 JVM_FindClassFromCaller,触发类加载;
    • Method.invoke() 调用 JVM_InvokeMethod,直接在 JVM 层面执行方法调用。
  3. 动态代理的辅助:注解(Annotation)的实例本质是 JVM 生成的动态代理对象(实现了注解接口),当调用注解的属性方法(如 @MyAnn.value())时,代理对象会从类元数据中提取属性值并返回。

四、反射的性能问题及原因

反射的性能通常比直接调用低(约慢 10-100 倍),主要原因包括:
  1. 运行时类型检查:反射需要在运行时验证参数类型、访问权限等,而直接调用在编译期已确定;
  2. native 方法调用开销:Java 调用 native 方法需要切换 JVM 执行模式(从解释执行到本地执行),存在额外开销;
  3. 自动装箱 / 拆箱:反射处理基本类型时需频繁装箱 / 拆箱,增加内存操作;
  4. 元数据查找开销:每次反射调用可能需要重新查找类元数据(可通过缓存 MethodField 对象缓解)。

五、反射的安全限制

为防止滥用反射破坏封装性,JVM 提供了一些安全限制:
  1. 安全管理器(SecurityManager):早期 JVM 可通过安全管理器限制 setAccessible(true) 的调用(如禁止访问私有成员),但 JDK 17 已移除安全管理器;
  2. 模块系统(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 对象,常见方式有:
  1. Class.forName("全类名"):通过类的全限定名(包名 + 类名)加载类并返回 Class 对象(常用)。
  2. 类名.class:通过类字面量直接获取(编译期确定,无需处理异常)。
  3. 对象.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.classString.class);否则返回 null
  • boolean isAssignableFrom(Class<?> cls)判断当前类是否是 cls 类的父类或接口(即 cls 是否可以赋值给当前类类型的变量)。例如:Object.class.isAssignableFrom(String.class) 返回 trueString 是 Object 的子类)。

三、注意事项

  1. 访问权限:getDeclaredXXX 方法可以获取私有成员,但需调用 setAccessible(true) 才能访问(暴力反射),否则会抛出 IllegalAccessException
  2. 性能:反射操作绕过了编译期检查,性能略低于直接调用,频繁使用需谨慎。
  3. 异常处理:反射方法可能抛出多种受检异常(如 NoSuchMethodExceptionIllegalAccessException 等),需显式处理或声明抛出。
Class<T> 类是 Java 反射机制的基础,通过它可以在运行时动态操作类的结构,广泛应用于框架(如 Spring、MyBatis)、注解处理器、序列化等场景。
 
Field 类方法和属性详解
在 Java 反射机制中,Field 类代表类或接口的成员变量(包括实例字段和静态字段),通过它可以在运行时获取字段的信息(如类型、修饰符、注解等),并动态修改字段的值(即使是私有字段)。以下是 Field 类的核心方法和属性详解:

一、Field 类的核心属性(继承自 AccessibleObject

Field 类继承自 AccessibleObject,该父类提供了一个关键属性用于控制访问权限:
  • boolean accessible
     
    用于标识是否忽略访问权限检查(即 “暴力反射”)。默认值为 false,表示遵循类的访问控制(如私有字段不可直接访问);若设为 true,则可绕过访问权限限制,直接操作私有字段。

二、Field 类的核心方法分类详解

1. 获取字段基本信息

  • String getName()返回字段的名称(如 nameage)。
  • Class<?> getType()返回字段的类型对应的 Class 对象(如字段为 String name,则返回 String.class)。
  • Class<?> getDeclaringClass()返回声明该字段的类或接口的 Class 对象(即字段所属的类)。
  • int getModifiers()返回字段的修饰符对应的整数编码(通过 Modifier 工具类可解析为具体修饰符,如 publicprivatestatic 等)。示例:解析修饰符
    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
    
     
     
  • 针对基本类型的便捷方法(避免装箱 / 拆箱):若字段类型为基本类型(如 intboolean),可直接使用以下方法,性能更优: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() 更详细)。

三、使用注意事项

  1. 访问私有字段必须设置 setAccessible(true):否则会抛出 IllegalAccessException
  2. 静态字段操作无需实例对象:调用 get()/set() 时,obj 参数可传 null(推荐 JDK 9+ 的 getStatic()/setStatic() 更清晰)。
  3. 基本类型与包装类的区别:get() 方法返回基本类型的包装类,而 getInt() 等方法直接返回基本类型,避免装箱 / 拆箱开销。
  4. 性能影响:反射操作(尤其是修改 accessible 属性)会绕过编译期检查,性能略低于直接访问字段,频繁操作需谨慎。
  5. 安全管理器限制:若 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()返回方法的名称(如 setNamegetAge)。
  • Class<?> getReturnType()返回方法的返回值类型对应的 Class 对象(如方法返回 String,则返回 String.class;无返回值则返回 void.class)。
  • Class<?> getDeclaringClass()返回声明该方法的类或接口的 Class 对象(即方法所属的类)。
  • int getModifiers()返回方法的修饰符对应的整数编码(通过 Modifier 工具类解析,如 publicprivatestaticfinalabstract 等)。示例:解析修饰符
    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()返回当前方法对应的桥接方法(桥接方法是编译器为泛型类型擦除生成的合成方法,通常用于实现泛型的多态)。

三、使用注意事项

  1. 调用私有方法必须设置 setAccessible(true):否则会抛出 IllegalAccessException
  2. 静态方法调用时 obj 传 null:静态方法属于类,与实例无关,传入实例对象会被忽略。
  3. 参数类型匹配:invoke 的 args 参数类型必须与方法声明的参数类型兼容(基本类型会自动装箱,如 int 可传 Integer),否则会抛出 IllegalArgumentException
  4. 方法抛出异常的处理:若被调用的方法本身抛出异常,invoke 会将其包装为 InvocationTargetException,需通过 getCause() 获取原始异常。
     
    示例:
    java
     
    运行
     
     
     
     
    try {
        method.invoke(obj);
    } catch (InvocationTargetException e) {
        Throwable originalException = e.getCause(); // 获取方法实际抛出的异常
    }
    
     
     
  5. 性能影响:反射调用比直接调用性能低(约慢 10-100 倍),频繁调用需谨慎(可通过 MethodHandle 优化)。
  6. 合成方法:编译器可能生成合成方法(如内部类访问外部类私有成员时),isSynthetic() 方法可判断是否为合成方法(通常无需手动处理)。
Method 类是反射调用方法的核心工具,广泛应用于依赖注入(如 Spring)、动态代理、框架底层(如 MyBatis 动态调用 SQL 方法)等场景,通过它可以在运行时灵活调用任意方法,突破编译期的限制。
 
Annotation 类方法和属性详解
在 Java 中,Annotation 是所有注解的顶级接口,所有注解 类型都隐式实现了该接口(类似所有类隐式继承 Object)。Annotation 接口本身不定义具体注解的属性,而是提供了用于操作注解的通用方法。以下是 Annotation 接口的核心方法及注解类型的属性特性详解:

一、Annotation 接口的核心方法

Annotation 接口定义了 4 个核心方法,所有注解类型都继承这些方法:
  1. 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
    
     
     
  2. boolean equals(Object obj)重写 Object 的 equals 方法,判断两个注解实例是否相等。注解相等的条件:
    • 类型相同(annotationType() 返回的 Class 对象相同);
    • 所有属性值对应相等(基本类型值相等,引用类型对象 equals 为 true)。
  3. int hashCode()重写 Object 的 hashCode 方法,返回注解的哈希值。哈希值计算规则:所有属性的哈希值之和(属性值为基本类型则直接计算,引用类型则取其 hashCode;若属性为数组,则按数组元素计算)。公式:(127 * "属性名".hashCode()) ^ 属性值.hashCode() 累加。
  4. String toString()返回注解的字符串表示,格式通常为 @注解类型(属性1=值1, 属性2=值2, ...)。例如:@MyAnnotation(name="test", age=20)

二、注解类型的属性(自定义注解的成员)

注解的 “属性” 本质是注解类型中声明的抽象方法(无参数、无异常声明),这些方法的返回值即为属性的类型。当使用注解时,需为属性赋值(除非有默认值)。

1. 注解属性的声明规则

  • 只能声明为抽象方法,不能有方法体;
  • 方法无参数(不能声明参数);
  • 返回值类型只能是:
    • 基本数据类型(intbooleanchar 等);
    • StringClassenum(枚举);
    • 其他注解类型;
    • 以上类型的一维数组(如 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 接口无法直接获取属性值,需结合反射(如 FieldMethodClass 的 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),其属性控制被修饰注解的行为(如生效范围、保留周期)。

四、注意事项

  1. 注解属性无默认值则必须赋值:使用注解时,若属性未声明 default,必须显式赋值,否则编译报错。
  2. 数组属性不能为多维数组:注解属性的数组类型只能是一维(如 int[][] 不允许)。
  3. 注解属性的返回值不能为 null:赋值时需确保属性值非 null(基本类型有默认值,引用类型需显式赋值非 null)。
  4. 注解是 “被动” 的:注解本身不包含逻辑,需通过反射或注解处理器(如 AnnotationProcessor)解析才能生效(例如 Spring 的 @Autowired 需要框架解析并执行依赖注入)。
Annotation 接口是 Java 注解机制的基础,其方法提供了注解的通用操作,而具体注解的功能则通过属性定义和外部解析逻辑实现。注解广泛应用于代码标记(如 @Override)、配置声明(如 Spring 注解)、编译期检查(如 @NonNull)等场景。

-------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------------------

posted @ 2025-11-19 10:47  hanease  阅读(16)  评论(0)    收藏  举报