Skip to content

Class 与反射常用 API ​

学习目标 ​

  • 会获取 Class,并读取构造器、方法、字段和父类信息。
  • 能安全地创建对象、调用方法、读取字段和处理反射异常。
  • 能理解泛型擦除、数组类型、模块边界与缓存反射元数据的原因。

核心知识点 ​

专业术语 ​

  • Class:Java 类型在运行时的元数据入口。
  • Constructor/Method/Field:分别描述构造器、方法和字段,并提供动态调用能力。
  • AccessibleObject:反射成员的访问控制入口,受 Java 访问权限和模块封装限制。
  • Type:泛型等更丰富的类型信息接口,具体实现可能是 ParameterizedType、GenericArrayType。

白话解释与边界 ​

反射 API 的名称和参数是字符串/对象组合,编译器无法像普通调用一样提前检查;拼错方法名、参数类型或访问权限都会在运行时报错。把反射集中在框架边界,缓存稳定的 Method/Field,并解包 InvocationTargetException 还原业务异常。模块化后,能否访问还取决于 exports/opens。

常用用法 ​

获取运行时类型:按已知类型、对象或类名选择入口 ​

类字面量适合已知类型,getClass 反映对象的实际运行时类型,Class.forName 按名称加载并可能初始化类;不要把外部输入的类名直接交给它。

java
// 说明:获取运行时类型:按已知类型、对象或类名选择入口。
public class ClassGetDemo {
    public static void main(String[] args) throws Exception {
        Class<String> literal = String.class;
// 初始状态:literal = String.class。
        Class<?> object = "java".getClass();
// 初始状态:object = "java".getClass()。
        Class<?> loaded = Class.forName("java.lang.String");
// 初始状态:loaded = Class.forName("java.lang.String")。
// 作用:Class<?> loaded = Class.forName("java.lang.String");;按已知类型、对象或类名选择入口。
        System.out.println(literal == object && object == loaded);
// 输出:true
    }
}

查询类型名称:选择完整名、简单名或包名 ​

日志和配置映射要明确使用哪种名称;内部类、数组和匿名类的 getName 可能包含特殊格式,不能简单当作展示文本。

java
// 说明:查询类型名称:选择完整名、简单名或包名。
public class ClassNameDemo {
    public static void main(String[] args) {
        Class<?> type = java.util.ArrayList.class;
// 作用:Class<?> type = java.util.ArrayList.class;;选择完整名、简单名或包名。
// 初始状态:type = java.util.ArrayList.class。
        System.out.println(type.getName() + " / " + type.getSimpleName() + " / " + type.getPackageName());
// 输出:java.util.ArrayList / ArrayList / java.util
    }
}

查询直接类型层次:读取父类与接口 ​

getSuperclass 只返回直接父类,接口没有父类对象;getInterfaces 只列出当前类直接声明的接口,完整层次要递归遍历。

java
// 说明:查询直接类型层次:读取父类与接口。
import java.util.ArrayList;

public class ClassHierarchyDemo {
    public static void main(String[] args) {
        Class<?> type = ArrayList.class;
// 作用:Class<?> type = ArrayList.class;;读取父类与接口。
// 初始状态:type = ArrayList.class。
        System.out.println(type.getSuperclass().getSimpleName());
// 输出:AbstractList
        System.out.println(type.getInterfaces()[0].getSimpleName());
// 输出:List
    }
}

反射创建对象:取得构造器后实例化 ​

参数类型必须精确匹配构造器签名,基本类型和包装类型也不是同一个 Class;没有无参构造器时不能假设 getDeclaredConstructor() 存在。

java
// 说明:反射创建对象:取得构造器后实例化。
import java.lang.reflect.Constructor;

class User {
    private final String name;

    User(String name) { this.name = name; }
// 初始状态:name = name; }。

    String name() { return name; }
}

public class ConstructorReflectDemo {
    public static void main(String[] args) throws Exception {
        Constructor<User> constructor = User.class.getDeclaredConstructor(String.class);
// 初始状态:constructor = User.class.getDeclaredConstructor(String.class)。
// 作用:Constructor<User> constructor = User.class.getDeclaredConstructor(String.class);;取得构造器后实例化。
        User user = constructor.newInstance("Ann");
// 初始状态:user = constructor.newInstance("Ann")。
// 作用:User user = constructor.newInstance("Ann");;取得构造器后实例化。
        System.out.println(user.name());
// 输出:Ann
    }
}

反射调用公共方法:查找后执行 ​

getMethod 只找 public 方法(含继承),invoke 的返回值是 Object;目标方法抛出的异常通常被包装在 InvocationTargetException 中。

java
// 说明:反射调用公共方法:查找后执行。
import java.lang.reflect.Method;

public class MethodInvokeDemo {
    public static void main(String[] args) throws Exception {
        Method method = String.class.getMethod("substring", int.class, int.class);
// 初始状态:method = String.class.getMethod("substring", int.class, int.class)。
// 作用:Method method = String.class.getMethod("substring", int.class, int.class);;查找后执行。
        String result = (String) method.invoke("java", 1, 3);
// 初始状态:result = (String) method.invoke("java", 1, 3)。
// 作用:String result = (String) method.invoke("java", 1, 3);;查找后执行。
        System.out.println(result);
// 输出:av
    }
}

查询本类方法:按签名或批量读取 ​

getDeclared* 只看当前类声明,包括 private,但不自动包含父类成员;框架扫描时要明确是否需要递归父类。

java
// 说明:查询本类方法:按签名或批量读取。
import java.lang.reflect.Method;

class Commands {
    private void hidden() { }
    public void visible() { }
}

public class DeclaredMethodDemo {
    public static void main(String[] args) throws Exception {
        Method hidden = Commands.class.getDeclaredMethod("hidden");
// 初始状态:hidden = Commands.class.getDeclaredMethod("hidden")。
// 作用:Method hidden = Commands.class.getDeclaredMethod("hidden");;按签名或批量读取。
        long count = java.util.Arrays.stream(Commands.class.getDeclaredMethods()).count();
// 初始状态:count = java.util.Arrays.stream(Commands.class.getDeclaredMethods()).count()。
// 作用:long count = java.util.Arrays.stream(Commands.class.getDeclaredMethods()).count();;按签名或批量读取。
        System.out.println(hidden.getName() + " / " + count);
// 输出:hidden / 2
    }
}

反射访问字段:查找后读取或写入 ​

修改 private 字段会破坏封装,也可能在强模块边界失败;已知对象应优先提供方法或构造器,反射字段只留给受控框架。

java
// 说明:反射访问字段:查找后读取或写入。
import java.lang.reflect.Field;

class Config {
    private String value = "old";
// 初始状态:value = "old"。
}

public class FieldReflectDemo {
    public static void main(String[] args) throws Exception {
        Config config = new Config();
// 初始状态:config = new Config()。
// 作用:Config config = new Config();;查找后读取或写入。
        Field field = Config.class.getDeclaredField("value");
// 初始状态:field = Config.class.getDeclaredField("value")。
// 作用:Field field = Config.class.getDeclaredField("value");;查找后读取或写入。
        field.setAccessible(true);
// 初始状态:field.setAccessible(true)。
// 作用:field.setAccessible(true);;查找后读取或写入。
        System.out.println(field.get(config));
// 输出:old
        field.set(config, "new");
// 初始状态:field.set(config, "new")。
// 作用:field.set(config, "new");;查找后读取或写入。
        System.out.println(field.get(config));
// 输出:new
    }
}

查询字段集合:选择公共继承或本类声明范围 ​

getFields 返回 public 字段(含继承),getDeclaredFields 只返回当前类声明(含非 public);字段顺序不应当作业务顺序依赖。

java
// 说明:查询字段集合:选择公共继承或本类声明范围。
class Parent { public int parent; }
class Child extends Parent { private int child; public int own; }

public class FieldScopeDemo {
    public static void main(String[] args) {
        System.out.println(Child.class.getFields().length);
// 输出:2
        System.out.println(Child.class.getDeclaredFields().length);
// 输出:2(第2次输出)
    }
}

isAssignableFrom:判断类型兼容 ​

调用方向是“左侧能否接收右侧对象”;它比比较类名更可靠,适合插件注册和参数校验。

java
// 说明:isAssignableFrom:判断类型兼容。
import java.util.ArrayList;
import java.util.List;

public class AssignableDemo {
    public static void main(String[] args) {
        System.out.println(List.class.isAssignableFrom(ArrayList.class));
// 输出:true
        System.out.println(ArrayList.class.isAssignableFrom(List.class));
// 输出:false
    }
}

isInstance:判断对象运行时类型 ​

isInstance 处理对象与 Class 的关系,空引用会返回 false;它不提供泛型参数的运行时判断。

java
// 说明:isInstance:判断对象运行时类型。
import java.util.ArrayList;
import java.util.List;

public class InstanceReflectDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
// 初始状态:list = new ArrayList<>()。
// 作用:List<String> list = new ArrayList<>();;判断对象运行时类型。
        System.out.println(List.class.isInstance(list));
// 输出:true
    }
}

操作反射数组:识别组件类型并读写元素 ​

Array 可创建运行时才知道组件类型的数组;基本类型数组和引用类型数组的反射读写规则不同,越界和类型不匹配会抛异常。

java
// 说明:操作反射数组:识别组件类型并读写元素。
import java.lang.reflect.Array;

public class ArrayReflectDemo {
    public static void main(String[] args) {
        Class<?> type = String[].class;
// 初始状态:type = String[].class。
        Object values = Array.newInstance(type.getComponentType(), 2);
// 初始状态:values = Array.newInstance(type.getComponentType(), 2)。
// 作用:Object values = Array.newInstance(type.getComponentType(), 2);;识别组件类型并读写元素。
        Array.set(values, 0, "java");
// 初始状态:Array.set(values, 0, "java")。
// 作用:Array.set(values, 0, "java");;识别组件类型并读写元素。
        System.out.println(type.isArray() + " / " + Array.get(values, 0));
// 输出:true / java
    }
}

不常用但需要知道 ​

trySetAccessible:探测访问是否可打开 ​

相比直接 setAccessible(true),trySetAccessible 可以把当前访问是否成功作为结果处理;强模块边界下仍可能返回 false。

java
// 作用:通过 trySetAccessible 探测访问是否可打开。
import java.lang.reflect.Field;

class PrivateValue { private int value = 1; }
// 初始状态:value = 1; }。

public class TryAccessibleDemo {
    public static void main(String[] args) throws Exception {
        Field field = PrivateValue.class.getDeclaredField("value");
// 初始状态:field = PrivateValue.class.getDeclaredField("value")。
// 作用:Field field = PrivateValue.class.getDeclaredField("value");;探测访问是否可打开,返回读取结果。
        System.out.println(field.trySetAccessible());
// 输出:true
    }
}

getGenericSuperclass:读取部分泛型签名 ​

只有声明位置的泛型签名可能保留在 class 文件中;普通 new ArrayList<String>() 的对象实例本身通常不知道 String。

java
// 作用:通过 getGenericSuperclass 读取部分泛型签名。
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;

class Names extends ArrayList<String> { }

public class GenericSuperclassDemo {
    public static void main(String[] args) {
        var type = (ParameterizedType) Names.class.getGenericSuperclass();
// 初始状态:type = (ParameterizedType) Names.class.getGenericSuperclass()。
// 作用:var type = (ParameterizedType) Names.class.getGenericSuperclass();;读取部分泛型签名。
        System.out.println(type.getActualTypeArguments()[0].getTypeName());
// 输出:java.lang.String
    }
}

getRecordComponents:读取 record 组件 ​

record 组件提供名称、类型和访问器信息,但反射读取不改变 record 的浅不可变语义。

java
// 作用:通过 getRecordComponents 读取 record 组件。
record Point(int x, int y) { }

public class RecordComponentReflectDemo {
    public static void main(String[] args) {
        var components = Point.class.getRecordComponents();
// 初始状态:components = Point.class.getRecordComponents()。
// 作用:var components = Point.class.getRecordComponents();;读取 record 组件。
        System.out.println(components[0].getName() + " / " + components.length);
// 输出:x / 2
    }
}

查询嵌套关系:读取宿主与成员 ​

Nestmate 信息用于编译器和运行时表达嵌套类的访问关系;业务框架很少需要直接依赖它。

java
public class NestReflectDemo {
    static class Inner { }

    public static void main(String[] args) {
        System.out.println(Inner.class.getNestHost() == NestReflectDemo.class);
// 输出:true
    }
}

InvocationTargetException:还原目标异常 ​

框架日志和异常转换应优先记录 getCause();只打印 InvocationTargetException 会丢失真正业务根因。

java
// 作用:通过 InvocationTargetException 还原目标异常。
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class InvocationTargetDemo {
    static void fail() { throw new IllegalArgumentException("bad input"); }
// 初始状态:static void fail() { throw new IllegalArgumentException("bad input"); 。
// 作用:static void fail() { throw new IllegalArgumentException("bad input"); };还原目标异常。

    public static void main(String[] args) throws Exception {
        Method method = InvocationTargetDemo.class.getDeclaredMethod("fail");
// 初始状态:method = InvocationTargetDemo.class.getDeclaredMethod("fail")。
// 作用:Method method = InvocationTargetDemo.class.getDeclaredMethod("fail");;还原目标异常。
        try {
            method.invoke(null);
// 作用:method.invoke(null);;还原目标异常。
        } catch (InvocationTargetException e) {
            System.out.println(e.getCause().getClass().getSimpleName());
// 输出:IllegalArgumentException
        }
    }
}

getAnnotatedType:读取类型使用位置注解 ​

AnnotatedType 关注类型使用位置,而不是方法/字段声明本身;它通常服务于校验框架或静态/运行时类型工具。

java
// 作用:通过 getAnnotatedType 读取类型使用位置注解。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface NonNull { }

public class AnnotatedTypeDemo {
    static @NonNull String value() { return "java"; }

    public static void main(String[] args) throws Exception {
        var type = AnnotatedTypeDemo.class.getDeclaredMethod("value").getAnnotatedReturnType();
// 初始状态:type = AnnotatedTypeDemo.class.getDeclaredMethod("value").getAnnotatedReturnType()。
// 作用:var type = AnnotatedTypeDemo.class.getDeclaredMethod("value").getAnnotatedReturnType();;读取类型使用位置注解。
        System.out.println(type.isAnnotationPresent(NonNull.class));
// 输出:true
    }
}

简单案例 ​

java
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

class Message {
    private final String text;

    public Message(String text) { this.text = text; }
// 初始状态:text = text; }。
// 作用:public Message(String text) { this.text = text; };读取类型使用位置注解。
    public String upper() { return text.toUpperCase(); }
// 作用:public String upper() { return text.toUpperCase(); };读取类型使用位置注解。
}

public class ClassReflectionDemo {
    public static void main(String[] args) throws Exception {
        Constructor<Message> constructor = Message.class.getDeclaredConstructor(String.class);
// 作用:Constructor<Message> constructor = Message.class.getDeclaredConstructor(String.class);;读取类型使用位置注解。
// 初始状态:constructor = Message.class.getDeclaredConstructor(String.class)。
        Message message = constructor.newInstance("java");
// 作用:Message message = constructor.newInstance("java");;读取类型使用位置注解。
// 初始状态:message = constructor.newInstance("java")。
        Method method = Message.class.getMethod("upper");
// 作用:Method method = Message.class.getMethod("upper");;读取类型使用位置注解。
// 初始状态:method = Message.class.getMethod("upper")。
        System.out.println(method.invoke(message));
// 输出:JAVA
    }
}

反射边界的最小流程是“验证类型 → 找到成员 → 创建/调用 → 处理包装异常”;知道类型时仍应优先直接写 new Message("java").upper()。

易混点 ​

  • getMethod/getMethods 面向 public 成员和继承关系,getDeclared* 面向当前类声明。
  • isAssignableFrom 的方向是左侧接收右侧;反过来通常得到 false。
  • 泛型参数大多会擦除,只有声明签名等位置可能通过 Type 元数据读取。
  • setAccessible、反射调用和模块 opens 是三个相互关联但不等价的边界。

课后小问 ​

  1. 为什么 getDeclaredMethods() 的返回顺序不能当作方法定义顺序? 答案:Java 反射不承诺按源码顺序返回成员。 解析:需要稳定排序时按方法名、参数类型或显式注解排序,不要依赖当前 JVM 输出。

  2. 为什么 InvocationTargetException.getCause() 比外层异常更重要? 答案:外层只是反射调用包装,真正由目标方法抛出的是 cause。 解析:异常日志、重试和业务转换都应根据真实根因决定,而不是把所有反射异常混为一类。

本节小结 ​

  • Class 提供类型入口,Constructor/Method/Field 提供动态成员操作。
  • get* 与 getDeclared* 的可见性和继承范围不同。
  • 反射要校验参数、缓存元数据、处理访问权限和异常包装。
  • 泛型、数组、record 和类型注解提供更低频的元数据读取能力。

快速回顾 ​

  • 类字面量、对象 getClass 和 Class.forName 是常用入口。
  • 创建、调用、字段读写分别使用 Constructor、Method、Field。
  • isAssignableFrom 判断类型兼容,isInstance 判断对象实例。
  • 反射不能替代已知类型的普通调用,也不能绕过模块封装。