Skip to content

Java 反射

反射是指能通过类字节码对象剖析类结构,包括成员变量、构造方法和成员方法。

image-20250223133627451

1. 获取Class对象

获取Class对象有三种方式:

  • Class.forName("全类名"):最常用的方式;
  • 类名.class:一般用于参数传递;
  • 对象.getClass():已经有某类对象时才可以使用;

下面的例子基于Student类来获取Class对象:

java
public static void main(String[] args) throws ClassNotFoundException {
    // 方式一:Class.forName()
    Class<?> clazz01 = Class.forName("com.lee.reflect.Student");
    System.out.println(clazz01);

    // 方式二:类名.class
    Class<Student> clazz02 = Student.class;
    System.out.println(clazz02);

    // 方式三:对象.getClass()
    Student student = new Student();
    Class<? extends Student> clazz03 = student.getClass();
    System.out.println(clazz03);

    System.out.println(clazz01 == clazz02);
    System.out.println(clazz02 == clazz03);
}
java
public class Student {
    private String name;
    private int age;
    
    public Student() {
        
    }
    
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    protected Student(String name){
        this.name = name;
    }
    
    private Student(int age){
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

可以看到,只要是同一个类,三种方式获取的Class对象是相同的。

2. 构造方法

2.1 获取构造方法对象

构造方法可以通过Constructor类来表示,在Class类中用于获取构造方法的方法如下:

  • Constructor<?>[] getConstructors():返回所有的公共构造方法对象;
  • Constructor<?>[] getDeclaredConstructors():返回所有的构造方法对象;
  • Constructor<T> getConstructor(Class<?>... parameterTypes):返回单个公共构造方法对象;
  • Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):返回单个构造方法对象;
java
public static void main(String[] args) throws NoSuchMethodException {
    Class<Student> clazz = Student.class;

    System.out.println("====================");
    for (Constructor<?> constructor : clazz.getConstructors()) {
        System.out.println(constructor);
    }

    System.out.println("====================");
    for (Constructor<?> declaredConstructor : clazz.getDeclaredConstructors()) {
        System.out.println(declaredConstructor);
    }

    System.out.println("====================");
    Constructor<Student> constructor = clazz.getConstructor(String.class, int.class);
    System.out.println(constructor);

    System.out.println("====================");
    Constructor<Student> declaredConstructor = clazz.getDeclaredConstructor(int.class);
    System.out.println(declaredConstructor);
}
txt
====================
public com.lee.reflect.Student()
public com.lee.reflect.Student(java.lang.String,int)
====================
public com.lee.reflect.Student()
private com.lee.reflect.Student(int)
protected com.lee.reflect.Student(java.lang.String)
public com.lee.reflect.Student(java.lang.String,int)
====================
public com.lee.reflect.Student(java.lang.String,int)
====================
private com.lee.reflect.Student(int)

2.2 通过构造方法对象创建类对象

我们可以通过以下方法创建对象:

  • T newInstance(Object ... initargs)
java
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    Class<Student> clazz = Student.class;

    Constructor<Student> declaredConstructor = clazz.getDeclaredConstructor(int.class);
    // 设置可访问,即私有方法(构造方法)也是可以被调用的
    declaredConstructor.setAccessible(true);
    // 调用构造方法
    Student s = declaredConstructor.newInstance(1);
    System.out.println(s);
}
txt
Student{name='null', age=1}

3. 成员变量

3.1 获取成员变量

成员变量可以通过类Field表示,在Class类中有以下方法可以获取成员变量对象:

  • Field[] getFields():获取所有公共的成员变量对象;
  • Field[] getDeclaredFields():获取所有成员变量对象;
  • Field getFields(String name):获取单个公共成员变量对象;
  • Field getDeclaredField(String name):获取单个成员变量对象;

3.2 使用成员变量对象

我们可以使用get(Object obj)来获取该成员变量在某个对象上的值:

java
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Class<Student> clazz = Student.class;

    Student student = new Student("张三", 18);
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    // 获取某个对象上该成员变量的值
    Object o = name.get(student);
    System.out.println(o);   // 张三
}

也可以使用set(Object obj, Object value)修改某个对象中该成员变量的值:

java
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Class<Student> clazz = Student.class;

    Student student = new Student("张三", 18);
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);

    // 修改某个对象上该成员变量的值
    name.set(student, "李四");
    System.out.println(student);  // Student{name='李四', age=18}
}

4. 成员方法

4.1 获取成员方法

我们可以使用Method来表示成员方法,在Class中有以下方法可以获取成员方法:

  • Method[] getMethods():获取所有公共成员方法,包括继承的;
  • Method[] getDeclaredMethods():获取所有成员方法对象,不包括继承的;
  • Method getMethod(String name, Class<?>... parameterTypes):获取单个公共的成员方法对象;
  • Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取单个成员方法对象;

4.2 调用成员方法

我们可以使用Method对象上的Object invoke(Object obj, Object... args)方法调用某个对象上的成员方法:

  • 第一个方法参数表示要在哪个对象上调用方法;
  • 第二个方法参数表示成员方法参数;
  • 调用返回值表示成员方法的返回值;
java
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
    Class<Student> clazz = Student.class;

    // 获取成员方法
    Method toString = clazz.getDeclaredMethod("toString");
    toString.setAccessible(true);
    // 调用成员方法
    Object o = toString.invoke(new Student("张三", 18));

    System.out.println(o);
}

5. 其他方法

在Class类中,还有以下方法可以获取相关信息。

5.1 获取父类

  • Class<? super T> getSuperclass():获取父类;
java
public static void main(String[] args) {
    Class<Student> clazz = Student.class;
    Class<? super Student> superclass = clazz.getSuperclass();

    System.out.println(superclass); // class java.lang.Object
}

5.2 获取实现的接口

  • Class<?>[] getInterfaces():获取实现的接口,接口不包括泛型信息或注解信息;

  • AnnotatedType[] getAnnotatedInterfaces():获取实现的接口的完整类型信息,包括泛型参数和注解信息。

参考链接:https://stackoverflow.com/questions/60918216/is-it-possible-to-check-at-runtime-whether-or-not-a-type-use-annotation-is-prese,例子如下:

java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)   // 注意
public @interface MyAnnotation {
    String value();
}
java
interface A<T>{}
interface B{}
interface C{}

public class MyClass
        implements @MyAnnotation("This is A") A<String> ,
         B,
        @MyAnnotation("This is C") C {

}
java
public static void main(String[] args) throws ClassNotFoundException {
    Class<?> clazz = Class.forName("com.lee.reflect.MyClass");
    AnnotatedType[] annotatedInterfaces = clazz.getAnnotatedInterfaces();

    for (AnnotatedType annotatedInterface : annotatedInterfaces) {
        System.out.println("Interface: " + annotatedInterface.getType());

        if (annotatedInterface.isAnnotationPresent(MyAnnotation.class)) {
            MyAnnotation annotation = annotatedInterface.getAnnotation(MyAnnotation.class);
            System.out.println("Annotation: " + annotation);
        }
    }
}
txt
Interface: com.lee.reflect.A<java.lang.String>
Annotation: @com.lee.reflect.MyAnnotation(value=This is A)
Interface: interface com.lee.reflect.B
Interface: interface com.lee.reflect.C
Annotation: @com.lee.reflect.MyAnnotation(value=This is C)

5.3 获取注解

注解可以用Annotation类来表示,在Class类中可以用以下方法获取注解对象:

  • Annotation[] getAnnotations():返回所有注解(包括继承的注解)。
  • Annotation[] getDeclaredAnnotations():返回直接声明的注解(不包括继承的注解)。
  • <A extends Annotation> A getAnnotation(Class<A> annotationClass):返回指定类型的注解(包括继承的注解)。
  • <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass):返回指定类型的注解(不包括继承的注解)。

例如:

java
// 定义一个可继承的注解
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface MyInheritedAnnotation {
    String value();
}

// 定义一个不可继承的注解
@Retention(RetentionPolicy.RUNTIME)
@interface MyNonInheritedAnnotation {
    String value();
}
java
// 父类使用两个注解
@MyInheritedAnnotation("Inherited Annotation Value")
@MyNonInheritedAnnotation("Non-Inherited Annotation Value")
class ParentClass {
}

// 子类不显式使用任何注解
class ChildClass extends ParentClass {
}
java
public static void main(String[] args) {
    // 获取父类的注解
    System.out.println("Parent Class Annotations:");
    Annotation[] parentAnnotations = ParentClass.class.getAnnotations();
    for (Annotation annotation : parentAnnotations) {
        System.out.println(annotation.annotationType().getSimpleName());
    }

    // 获取子类的注解
    System.out.println("\nChild Class Annotations:");
    Annotation[] childAnnotations = ChildClass.class.getAnnotations();
    for (Annotation annotation : childAnnotations) {
        System.out.println(annotation.annotationType().getSimpleName());
    }
}
txt
Parent Class Annotations:
MyInheritedAnnotation
MyNonInheritedAnnotation

Child Class Annotations:
MyInheritedAnnotation

如果要获取成员变量、方法、参数上的注解,可以分别使用FieldMethodParameter(表示方法参数)类中的方法。