反射(-)

反射作为 Java 高级特性之一,在框架开发中使用很多。

Class

Class 类用于表示已被 JVM 加载的类型(为避免歧义,文中用 “Class 实例” 代指类型),它的实例是 Java 基础数据类型、已被 Java 加载的类和实例。枚举是特殊的类,注解是特殊的接口,数组的类型实际也是类。

获取 Class 对象

Class 类没有公共的构造方法,所有 Class 实例都是 JVM 的类加载器的 defineClass 方法构建的。我们可以通过以下方法获取已存在的 Class 实例。

Object#getClass 方法

Object#getClass 方法用于获取一个对象实例对应的 Class 实例,如果是一个父类引用指向子类实例,返回结果仍然是实际的子类对应的 Class 实例。

String name = "cncsl";
String[] infos = {"cncsl", "male", "developer"};

System.out.println(name.getClass());//class java.lang.String
System.out.println(infos.getClass());//class [Ljava.lang.String;

List<Integer> numbers = new ArrayList<>();
System.out.println(numbers.getClass());//class java.util.ArrayList

注意调用第二次调用输出语句时控制台的输出是 class [Ljava.lang.String;,与普通的 String 对象相比,多了 [L;

.class.TYPE

所有的类和基本数据类型都有 class 属性(严格意义上来说基本数据类型不是对象,不能用“属性”二字来描述),可在没有可用实例时直接通过类名或基本数据类型关键字获取 Class 实例。此外每个基本数据类包装类都有 TYPE 属性,可用于获取对应基本数据类型的 Class 实例。

System.out.println(String.class);//class java.lang.String
System.out.println(String[].class);//class [Ljava.lang.String;

System.out.println(int.class);//int
System.out.println(Integer.class);//class java.lang.Integer
System.out.println(Integer.TYPE);//int

注意上方基本数据类型关键字的 .class、包装类的 .class.TYPE 的对应关系, int.class == Integer.TYPE 为真、int.class == Integer.class 为假。

Class#forName 方法

有两个不同类型重载的 Class#forName 方法,通常用于显式的类加载,并返回 Class 实例

  • public static Class<?> forName(String className):根据 className 参数指定的全限定名找到并加载相关类型,如果找不到抛出 ClassNotFoundException 异常。
  • public static Class<?> forName(String name, boolean initialize, ClassLoader loader):与另一重载类似,参数 initializa 指定是否立即初始化,loader 指定类加载器。
public class Main {

public static void main(String[] args) throws ClassNotFoundException {
Class.forName("io.github.cncsl.Foo");//Foo loaded
Class.forName("io.github.cncsl.Bar", false, Main.class.getClassLoader());//无输出
}

}

class Foo {
static {
System.out.println("Foo loaded");
}
}

class Bar {
static {
System.out.println("Bar loaded");
}
}

这两个函数底层都是调用私有、静态的本地方法 Class#forName0 实现类加载的。

Examining Class Modifiers and Types

Member

一个类声明由字段、方法和构造器共同构成,可以统称为类的成员,反射 API 提供了三个类分别表示这三种成员,Class 类提供了相关方法用于获取这些类的对象。利用反射进行编程的过程实际就是通过 Class 对象获取到想要的成员对象,然后通过成员对象提供的功能进行操作。

下方表中,”继承“指在当前类的父类(以及父类的父类)中定义的 public 修饰的变量,”私有“意为被 private 修饰的。

  • Field:常翻译为字段或成员变量,对应 java.lang.reflect.Field

    Class 实例方法 列举变量 可获取继承变量 可获取私有变量
    getDeclaredField()
    getField()
    getDeclaredFields()
    getFields()
  • Method:常翻译为方法或成员函数,对应 java.lang.reflect.Method

    Class 实例方法 列举函数 可获取继承函数 可获取私有函数
    getDeclaredMethod()
    getMethod()
    getDeclaredMethods()
    getMethods()
  • Constructor:常翻译为构造器或构造函数,对应 java.lang.reflect.Constructor

    Class 实例方法 列举构造器 可获取私有构造器
    getDeclaredConstructor()
    getConstructor()
    getDeclaredConstructors()
    getConstructors()

Field

Field 对象实例封装了一个字段的名称、类型、修饰符和注解,可用于动态的访问和修改字段的值。常用方法有:

  • 基本信息
    • String getName()
    • Class<?> getType()
    • Type getGenericType()
  • 获取:参数 obj 指定具体的类
    • 用于基本数据类型的 getXxx(Object obj)
    • 用于对象的 get(Object obj)
  • 修改:参数 obj 指定具体的类
    • 用于基本数据类型的 setXxx(Object obj, Xxx value)
    • 用于对象的 set(Object obj, Object value)

Method

Method 对象实例封装了一个函数的名称、返回值类型、形参列表、异常列表等,可用于动态的调用。常用方法有:

  • 基本信息:

    • String getName()
    • Class<?> getReturnType()
    • Class<?>[] getParameterTypes()
    • Class<?>[] getExceptionTypes()
  • 调用:Object invoke(Object obj, Object... args) 第一个参数用于指定调用类,后面的变长参数是传给函数的参数。