likes
comments
collection
share

Java Reflection 反射使用 完全指南

作者站长头像
站长
· 阅读数 21

前言

Java 中的反射大家都不陌生,有很多很好的文章都进行了讲解,但是很难找到一篇文章能完全解释反射的所有用法,特别是反射获取这个对象的注解的信息和泛型信息,往往都停留在了获取类的函数、方法,构造上。所以这篇文章将全面讲解 Java 反射中的所有用法,特别是对于类中的注解的获取上。另外,本文章不会对反射的原理进行讲解,看完本文你应该只能学会怎么用反射,至于其原理,本文不会涉及。

首先我们自定义两个类和一个注解,用于后续我们来演示反射的用法。简单来说,就是 Student 类继承自 Person 类,并在 Student 类上使用了 Work 注解。Person 类比较简单,我就不贴出来了,另外两个 Java 文件如下,我们后续将通过反射获取这个类对象的所有信息。

Work.java

package lic.reflect;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Documented()
@Inherited()
public @interface Work {
    String value();
    int category() default 0;
    Class<?> workClass();
}

Student.java

package lic.reflect;

@Work(value = "college", workClass = Person.class)
public class Student extends Person implements Cloneable {

 private volatile int level;

    private Student(String name) {
        super(name);
    }

    public Student(String name, int age) {
        super(name, age);
    }

    @Work(value = "math", workClass = HashMap.class)
    public List<String> study(Map<String, Integer> map) {
        System.out.println(this + " study:\n");
        List<String> list = new ArrayList();
        for (Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " : " + entry.getValue());
            list.add(entry.getKey());
         }
        return list;
    }

    @Override
    public void printOccupation() {
        System.out.println("occupation student.");
    }
}

Java Reflection 反射使用 完全指南

获取 Class 对象的四种方式

java.lang.Class 类是反射机制的基础,存放着对应类型对象的运行时信息。在 Java 程序运行时,Java虚拟机为所有类型维护一个 java.lang.Class 对象,即每种类型的 Class 对象只有一个。 在使用反射做任何事情之前,我们必须获得 Class 对象,可以说,Class 对象是我们进行反射操作的入口。那么怎么获取这个 Class 对象呢?主要有四种方式:

//方式一:Object.getClass()
Class studentClass = student.getClass();
//方式二:T.class
Class studentClass = Student.class;
//方式三:static Class<?> Class.forName
try {
    Class studentClass_3 = Class.forName("lic.reflect.Student");
    System.out.println("Class.forName = " + studentClass);
} catch (ClassNotFoundException exception) {                //此方式会抛出 ClassNotFoundException 异常
    exception.printStackTrace(System.err);
}
//方式四:通过 ClassLoader
try {    
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();    
    Class<?> c = classLoader.loadClass("java.lang.String");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

无论通过哪种方式,其获取的 Class 对象都是一样的。

Class 对象中,我们一般会获取的有个三类信息:构造方法 Constructor、成员方法 Method、成员变量 Field。而后续作用域对象的操作都必须先过去到其对应的上述三类信息,下面我们一一道来。

构造方法 Constructor

Class 类中用于获取构造方法的方法:

  • Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组(只能获取 public 的构造方法)
  • Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组(所有构造方法,包含 private)
  • Constructor<T> getConsturctor(Class<?>... parameterTypes) 返回单个指定的公共构造方法
  • Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回单个指定的构造方法

注意这上面,带 declared 的方法和不带 declared 的方法的区别:

  • 不带 Declared的方法支持取出包括继承、公有(Public) 但不包括有(Private)的构造函数
  • Declared的方法是支持取出包括公共(Public)、保护(Protected)、默认(包)访问和私有(Private)的构造方法,但不包括继承的构造函数

这个规则不仅适用于获取 Constructor、还适用于获取 MethodField 对象。 获取到 Constructor 对象之后,即可用其创建对象,相关方法如下:

  • T newInstance(Object... initargs) 根据指定的构造方法创建对象
  • setAccessible(boolean flag) 设置是否取消访问检查,设置为 true 表示取消访问检查,可以提高反射效率

下面我们试一下上面的几个方式获取构造器:

Constructor[] constructors = studentClass.getConstructors();
for(Constructor constructor: constructors) {
    System.out.println(constructor);
}
//输出:
//public lic.reflect.Student(java.lang.String,int)

constructors = studentClass.getDeclaredConstructors();
for(Constructor constructor: constructors) {
    System.out.println(constructor);
}
//输出:
//private lic.reflect.Student(java.lang.String)
//public lic.reflect.Student(java.lang.String,int)

try {
    Constructor constructor = studentClass.getDeclaredConstructor(String.class, Integer.TYPE);
    System.out.println(constructor);
} catch (NoSuchMethodException|SecurityException e) {
    e.printStackTrace();
} 
//输出:
//public lic.reflect.Student(java.lang.String,int)

当获取了 Constructor 之后,即可以使用这个对象来创建 Student 类对象:

constructor.setAccessible(true);    //取消访问检查
Student student = (Student)constructor.newInstance("Alan", 30);    //创建对象
System.out.println(student);

成员变量 Field

Class 类中用于获取成员变量的方法:

  • Field[] getFields() 返回此类和父类中所有public的成员变量
  • Field[] getDeclaredFields() 返回此类中所有的成员变量,不包括父类
  • FIeld getField(String name) 从此类和父类中查找对应的public成员变量并返回
  • Field getDeclaredField(String name) 在此类中查找指定的成员变量并返回

Field 类中用于设置和获取成员变量的方法:

  • void set(Object obj, Object value) 设置 obj 对象中的此成员变量设置为 value
  • Object get(Object obj) 获取 obj 对象中的此成员变量
  • String getName() 获取此成员变量的名称
  • Class<?> getType() 获取此成员变量的类型
  • int getModifiers() 获取此成员变量的修饰符(即 private volatile之类的),通过 Modifier 类方便查询

下面是 Field 这个类的使用示例:

Field[] fields = studentClass.getFields();
for(Field field: fields) 
    System.out.println(field);
//输出:父类的 public name 成员变量
//public java.lang.String lic.reflect.Person.name
        
Field[] fields = studentClass.getDeclaredFields();
for(Field field: fields) 
System.out.println(field);
//输出:本类的 private level 成员变量
//private int lic.reflect.Student.level

try {
    Field field = studentClass.getDeclaredField("level");
    String fieldName = field.getName();
    System.out.println(fieldName);            //输出 level
    Class fieldType = field.getType();
    System.out.println(fieldType);            //输出 int
    int modifier = field.getModifiers();
    System.out.println(Modifier.isPrivate(modifier));    //输出 true
    System.out.println(Modifier.isVolatile(modifier));    //输出 true
} catch (NoSuchFieldException | SecurityException exception) {
    exception.printStackTrace();
}

成员方法 Method

Class 类中用于获取成员变量的方法:

  • Method[] getMethdos() 返回所有 public 成员方法的对象,包括继承的
  • Method[] getDeclaredMethods() 返回此类中所有成员方法对象的数组
  • Method getMethod(String name, Class<?>... parameterTypes) 查找此类及父类中指定的 public 成员方法
  • Method getDeclaredMethod(String name, Class<?>... parameterTypes) 查找并返回单个成员方法

Method 类中的常用方法:

  • Object invoke(Object obj, Object... args) 调用 obj 对象上的这个方法
  • Class getReturnType() 返回这个方法的返回值类型
  • Type getGenericReturnType() 返回这个方法的泛型返回值类型
  • Class[] getParameterTypes() 返回这个方法的参数类型
  • Type[] getGenericParameterTypes() 返回这个方法的参数的泛型信息
  • Annotation[] getAnnotations() 返回这个方法上的注解

下面是 Method 类的使用示例:

Method[] methods = studentClass.getMethods();
for(Method method: methods)
    System.out.println(method);
//输出:可见大部分都是从 Object 类继承而来的方法
//public void lic.reflect.Student.study(java.util.Map)
//public void lic.reflect.Student.printOccupation()
//public java.lang.String lic.reflect.Person.getName()
//public void lic.reflect.Person.setName(java.lang.String)
//public int lic.reflect.Person.getAge()
//public void lic.reflect.Person.setAge(int)
//public static void lic.reflect.Person.staticFunc()
//public boolean java.lang.Object.equals(java.lang.Object)
//public java.lang.String java.lang.Object.toString()
//public native int java.lang.Object.hashCode()
//public final native java.lang.Class java.lang.Object.getClass()
//public final native void java.lang.Object.notify()
//public final native void java.lang.Object.notifyAll()
//public final void java.lang.Object.wait(long) throws java.lang.InterruptedException
//public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
//public final void java.lang.Object.wait() throws java.lang.InterruptedException

Method[] methods = studentClass.getDeclaredMethods();
for(Method method: methods)
    System.out.println(method);
//输出:
//public void lic.reflect.Student.study(java.util.Map)
//public void lic.reflect.Student.printOccupation()

try {
    Method method = studentClass.getMethod("study", Map.class);
    System.out.println(method);
    Map<String, Integer> methodArgs = new HashMap<String, Integer>();
    methodArgs.put("math", 10);
    method.invoke(student, methodArgs);        //study 方法被正常调用
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException e) {
    e.printStackTrace();
}

Class returnType = method.getReturnType();
//interface java.util.List
Type type = method.getGenericReturnType();
//java.util.List<java.lang.String>

Class[] parameterClass = method.getParameterTypes();
//interface java.util.Map
Type[] types = method.getGenericParameterTypes();
//java.util.Map<java.lang.String, java.lang.Integer>
Annotation[] annotations = method.getAnnotations();
//@lic.reflect.Work(category=0, value="math", workClass=java.util.HashMap.class)

对注解进行解析

上面的代码中,我们通过 method.getAnnotations() 方法获取了 study 方法上的注解,下面将讲解如何解析注解的一些信息。注解是由 Annotation 这个接口来表示,为了获取注解上的信息,我们首先就要获取这个注解的 Class 对象:

  • Class<? extends Annotation> annotationType() 获取这个注解的 Class 对象

有了这个 Class 对象再来看注解的解析。实际上跟上述获取类的成员方法并调用是一样的:

Method[] methods = annoClass.getDeclaredMethods();
//输出
//public abstract java.lang.String lic.reflect.Work.value()
//public abstract int lic.reflect.Work.category()
//public abstract java.lang.Class lic.reflect.Work.workClass()

如果我们要获取定义在 study() 方法上的定义的注解 @Work(value = "math", workClass = HashMap.class),只需要进行如下的调用即可:

String value = (String) methods[0].invoke(annotation);
System.out.println(value);        //math
int category = (int) methods[1].invoke(annotation);
System.out.println(category);        //0
Class workClass = (Class) methods[2].invoke(annotation);
System.out.println(workClass);        //class java.util.HashMap

与反射相关的三个类

通过上面的解读,我们肯定对反射有了基本的认识和运用,在使用反射中,我们往往会碰到三个类或接口,其中另一个就是上面说的 Class。而另外两个,一个是 Type 接口,一个是 AnnotatedType 接口。下面就对这两个接口简单解释一下。

Type 接口

Type 是一个顶层接口,表示 Java 语言中的所有类型,它的几个子接口和实现类如下:

  • Class<?>:表示类或接口类型
  • ParameterizedType:表示参数化类型(例如,List<String>
  • GenericArrayType:表示泛型数组类型(例如,T[]
  • TypeVariable<D extends GenericDeclaration>:表示类型变量(例如,T
  • WildcardType:表示通配符类型(例如,? extends Number

Type 接口主要用来在反射中表示和处理各种类型。通过这个接口,可以获取类型的详细信息并执行相应的操作。一般来说,当我们获取泛型类型的参数、确定数组类型时,会用到这个接口。例如:

public class Example {
    private List<String> list;

    public static void main(String[] args) throws NoSuchFieldException {
        Field field = Example.class.getDeclaredField("list");
        Type type = field.getGenericType();
        System.out.println("Type: " + type); // 输出:Type: java.util.List<java.lang.String>
    }
}

AnnotatedType 接口

AnnotatedType 接口是 AnnotatedElement 的一个子接口,表示带有注解的类型,允许访问和操作类型上的注解。但我们需要获取注解信息时,就必须获取使用这个接口。例如:

@Retention(RetentionPolicy.RUNTIME)
@interface ExampleAnnotation {
}

public class Example {
    private @ExampleAnnotation List<String> list;
    public static void main(String[] args) throws NoSuchFieldException {
            Field field = Example.class.getDeclaredField("list");        
            AnnotatedType annotatedType = field.getAnnotatedType();        
            System.out.println("Annotations: " + annotatedType.getAnnotations().length); // 输出:Annotations: 1    
    }
}

Class 类

Class 对象是 Java 反射机制中的核心概念之一。它是 Type 接口的一个实现类,代表了运行时的类或接口。Class 对象不仅仅是类型信息的载体,还提供了大量的方法来检查类的结构、创建实例、访问成员等。 这个类上面说了一大堆的内容,这里就不赘述了。

总结

Java 反射机制是 Java 语言的重要特性之一,如果你开发框架或库时,掌握反射的使用是必须的。例如大名鼎鼎的 Retrofit、Gson、Room 等,都大量使用了反射。所以作为 Android 开发者,这个反射咱们还真是得好好掌握。 Java Reflection 反射使用 完全指南

转载自:https://juejin.cn/post/7380296428142870528
评论
请登录