18.3.2 从Class中获取信息

18.3. 2从Class中获取信息

Class类提供了大量的实例方法来获取该Class对象所对应类的详细信息, Class类大致包含如下方法,下面每个方法都可能包括多个重载的版本,读者应该查阅API文档来掌握它们。

获取构造器

下面4个方法用于获取Class对应类所包含的构造器

方法 描述
Constructor<T> getConstructor(Class<?>...parameter Types) 返回此Class对象对应类的、带指定形参列表的public构造器。
Constructor<?>[] getConstructors() 返回此 Class对象对应类的所有public构造器。
Constructor<T> getDeclaredConstructor( Class<?>...parameterTypes) 返回此 Class对象对应类的带指定形参列表的构造器,与构造器的访问权限无关。
Constructor<?>[] getDeclaredConstructors() 返回此 Class对象对应类的所有构造器,与构造器的访问权限无关。

获取方法

下面4个方法用于获取Class对应类所包含的方法。

方法 描述
Method getMethod(String name, Classs<?> ... parameterTypes) 返回此Class对象对应类的、带指定形参列表的 public方法。
Method[] getMethods() 返回此Class对象所表示的类的所有 public方法。
Method getDeclaredMethod(String name, Class<?> ... parameter Types) 返回此 Class对象对应类的、带指定形参列表的方法,与方法的访问权限无关。
Method[] getDeclaredMethods() 返回此Class对象对应类的全部方法,与方法的访问权限无关

获取成员变量

如下4个方法用于访问Class对应类所包含的成员变量。

方法 描述
Field getField(String name) 返回此 Class对象对应类的、指定名称的 public成员变量。
Field[] getFields() 返回此Class对象对应类的所有 public成员变量。
Field getDeclaredField(String name) 返回此 Class对象对应类的、指定名称的成员变量,与成员变量的访问权限无关。
Field[] getDeclaredFields() 返回此 Class对象对应类的全部成员变量,与成员变量的访问权限无关

获取Annotation

如下几个方法用于访问Class对应类上所包含的Annotation

方法 描述
<A extends Annotation> A getAnnotation(Class<A> annotationClass 尝试获取该 Class象对应类上存在的、指定类型的 Annotation;如果该类型的注解不存在,则返回null
Annotation[] getAnnotations() 返回修饰该 Class对象对应类上存在的所有 Annotation
<A extends annotation> A getDeclaredAnnotation(Class<A> annotationClass) 这是Java 8新增的方法,该方法尝试获取直接修饰该Class对象对应类的、指定类型的Annotation;如果该类型的注解不存在,则返回null
Annotation[] getDeclaredAnnotations() 返回直接修饰该Class对应类的所有 Annotation
<A extends Annotation> A getAnnotationsByType(Class<A> annotationClass) 该方法的功能与前面介绍的getAnnotation()方法基本相似。但由于Java 8增加了重复注解功能,因此需要使用该方法获取修饰该类的、指定类型的多个Annotation

获取包含的内部类

如下方法用于访问该Class对象对应类包含的内部类。

方法 描述
Class<?> getClasses() 返回该Class对象对应类里包含的全部内部类。

获取所在的外部类

如下方法用于访问该Class对象对应类所在的外部类。

方法 描述
Class<?> getDeclaringClass() 返回该Class对象对应类所在的外部类

访问该Class对象对应类所实现的接口

如下方法用于访问该Class对象对应类所实现的接口

方法 描述
Classs<?>[] getInterfaces() 返回该Class对象对应类所实现的全部接口。

获取所继承的父类

如下几个方法用于访问该Class对象对应类所继承的父类。

方法 描述
Class<? super T> getSuperClass() 返回该Class对象对应类的超类的Class对象。

获取修饰符 所在包 类名等基本信息

如下方法用于获取Class对象对应类的修饰符、所在包、类名等基本信息。

方法 描述
int getModifiers() 返回此类或接口的所有修饰符。修饰符由 publicprotectedprivatefinalstaticabstract等对应的常量组成,返回的整数应使用 Modifier工具类的方法来解码,才可以获取真实的修饰符。
Package getPackage() 获取此类的包。
String getName() 以字符串形式返回此Class象所表示的类的名称。
String getSimpleName() 以字符串形式返回此Class对象所表示的类的简称。

判断该类是否为接口 枚举 注解类型

除此之外, Class对象还可调用如下几个判断方法来判断该类是否为接口、枚举、注解类型等。

方法 描述
boolean isAnnotation() 返回此Class对象是否表示一个注解类型(由@interface定义)
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 判断此Class对象是否使用了Annotation修饰
boolean isAnonymousClass() 返回此Class对象是否是一个匿名类。
boolean isArray() 返回此Class对象是否表示一个数组类。
boolean isEnum() 返回此Class象是否表示一个枚举(由enum关键字定义)。
boolean isInterface() 返回此 Class对象是否表示一个接口(使用 interface定义)。
boolean isInstance(Object object) 判断object是否是此Class对象的实例,该方法可以完全代替instanceof操作符。

如何通过反射获取一个方法

上面的多个getMethod()方法和getConstructor()方法中,都需要传入多个类型为Classs<?>的参数,用于获取指定的方法或指定的构造器。关于这个参数的作用,假设某个类内包含如下三个info方法签名。

  • public void info()
  • public void info(String str)
  • public void info(String str,Integer num)

这三个同名方法属于重载,它们的方法名相同,但参数列表不同。在Java语言中如果需要确定一个方法,则应该由方法名和形参列表来确定,但形参名没有任何实际意义,所以只能由形参类型来确定。例如想指定第二个info方法,则必须指定:

  • 方法名为info,
  • 形参列表为String.class

因此在程序中获取该方法使用如下代码:

  • clazz.getMethod("info", String.class)
    • 前一个参数指定方法名,后面的个数可变的Class参数指定形参类型列表。

如果需要获取第三个info方法,则使用如下代码:

  • clazz.getMethod("info", String.class, Integer.class)
    • 前一个参数指定方法名,后面的个数可变的Class参数指定形参类型列表。

如何通过反射获取一个构造器

获取构造器时无须传入构造器名—同一个类的所有构造器的名字都是相同的,所以要确定一个构造器只要指定形参列表即可

程序示例

下面程序示范了如何通过该Class对象来获取对应类的详细信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import java.util.*;
import java.lang.reflect.*;
import java.lang.annotation.*;

// 定义可重复注解
@Repeatable(Annos.class)
@interface Anno {}
@Retention(value=RetentionPolicy.RUNTIME)
@interface Annos {
Anno[] value();
}
// 使用4个注解修饰该类
@SuppressWarnings(value="unchecked")
@Deprecated
// 使用重复注解修饰该类
@Anno
@Anno
public class ClassTest
{
// 为该类定义一个私有的构造器
private ClassTest()
{
}
// 定义一个有参数的构造器
public ClassTest(String name)
{
System.out.println("执行有参数的构造器");
}
// 定义一个无参数的info方法
public void info()
{
System.out.println("执行无参数的info方法");
}
// 定义一个有参数的info方法
public void info(String str)
{
System.out.println("执行有参数的info方法"
+ ",其str参数值:" + str);
}
// 定义一个测试用的内部类
class Inner
{
}
public static void main(String[] args)
throws Exception
{
// 下面代码可以获取ClassTest对应的Class
// 获取该Class对象所对应类的全部构造器
Class<ClassTest> clazz = ClassTest.class;
Constructor[] ctors = clazz.getDeclaredConstructors();
System.out.println("ClassTest的全部构造器如下:");
for (Constructor c : ctors)
{
System.out.println(c);
}
// 获取该Class对象所对应类的全部public构造器
Constructor[] publicCtors = clazz.getConstructors();
System.out.println("ClassTest的全部public构造器如下:");
for (Constructor c : publicCtors)
{
System.out.println(c);
}
// 获取该Class对象所对应类的全部public方法
Method[] mtds = clazz.getMethods();
System.out.println("ClassTest的全部public方法如下:");
for (Method md : mtds)
{
System.out.println(md);
}
// 获取该Class对象所对应类的指定方法
System.out.println("ClassTest里带一个字符串参数的info()方法为:"
// 获取该Class对象所对应类的上的全部注解
+ clazz.getMethod("info" , String.class));
Annotation[] anns = clazz.getAnnotations();
System.out.println("ClassTest的全部Annotation如下:");
for (Annotation an : anns)
{
System.out.println(an);
}
System.out.println("该Class元素上的@SuppressWarnings注解为:"
+ Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class)));
System.out.println("该Class元素上的@Anno注解为:"
// 获取该Class对象所对应类的全部内部类
+ Arrays.toString(clazz.getAnnotationsByType(Anno.class)));
Class<?>[] inners = clazz.getDeclaredClasses();
System.out.println("ClassTest的全部内部类如下:");
for (Class c : inners)
{
System.out.println(c);
}
// 使用Class.forName方法加载ClassTest的Inner内部类
// 通过getDeclaringClass()访问该类所在的外部类
Class inClazz = Class.forName("ClassTest$Inner");
System.out.println("inClazz对应类的外部类为:" +
inClazz.getDeclaringClass());
System.out.println("ClassTest的包为:" + clazz.getPackage());
System.out.println("ClassTest的父类为:" + clazz.getSuperclass());
}
}

上面程序获取了ClassTest类对应的Class对象后,通过调用该Class对象的不同方法来得到该Class对象的详细信息。运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ClassTest的全部构造器如下:
private ClassTest()
public ClassTest(java.lang.String)
ClassTest的全部public构造器如下:
public ClassTest(java.lang.String)
ClassTest的全部public方法如下:
public static void ClassTest.main(java.lang.String[]) throws java.lang.Exception
public void ClassTest.info(java.lang.String)
public void ClassTest.info()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
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()
ClassTest里带一个字符串参数的info()方法为:public void ClassTest.info(java.lang.String)
ClassTest的全部Annotation如下:
@java.lang.Deprecated()
@Annos(value=[@Anno(), @Anno()])
该Class元素上的@SuppressWarnings注解为:[]
该Class元素上的@Anno注解为:[@Anno(), @Anno()]
ClassTest的全部内部类如下:
class ClassTest$Inner
inClazz对应类的外部类为:class ClassTest
ClassTest的包为:null
ClassTest的父类为:class java.lang.Object

从上面运行结果来看, Class提供的功能非常丰富,它可以获取该类里包含的构造器方法内部类注解等信息,也可以获取该类所包括的成员变量(Field)信息。

Class无法获取值显示在源代码上的注解

值得指出的是,虽然定义ClassTest类时使用了 @SuppressWarnings注解,但程序运行时无法分析出该类里包含的该注解,这是因为@SuppressWarnings使用了@Retention(value= SOURCE)修饰,这表明@SuppressWarnings只能保存在源代码级别上,而通过ClassTest.class取该类的运行时Class对象,所以程序无法访问到@SuppressWarnings注解。

对于只能在源代码上保留的注解,使用运行时获得的Class对象无法访问到该注解对象。

通过Class对象可以得到大量的MethodConstructorField等对象,这些对象分别代表该类所包括的方法、构造器和成员变量等,程序还可以通过这些对象来执行实际的功能,例如调用方法、创建实例。

本文重点

通过Class类,

  • 可以获取对应类的构造器
  • 可以获取对应类中方法
  • 获取对应类的成员变量
  • 获取对应类的Annotation
  • 获取对应类包含的内部类
  • 获取对应类所在的外部类
  • 获取对应类所实现的接口
  • 获取对应类所继承的父类
  • 获取对应类的修饰符 所在包 类名等基本信息
  • 可以判断对应类是否为接口 枚举 注解类型
  • 在获取方法时需要传入方法名和参数列表,
  • 在获取构造器时只需要传入参数列表。

Class无法获取值显示在源代码上的注解,例如@SuppressWarnings注解无法通过反射获取