本文记录是Java反射的基础知识和相关的API。
其中,大概的相关类的继承关系如下:
AnnotatedElement类
先说下AnnotatedElement
这个接口,因为他是处于整个类关系的顶层,是一个通用接口,提供关于获取Annotation
的相关方法,我们在后续的Class
,Method
和Field
等的类也会有它的相关方法,都是继承自AnnotatedElement
,具体可以看上面的图。
boolean isAnnotationPresent(@RecentlyNonNull Class<? extends Annotation> annotationClass)
当前的对象是否被annotationClass
注解注解了Annotation[] getAnnotations()
,获取所有的注解该类的注解Class,包括继承而来的注解(使用了@Inherited
注解的注解会被继承)<T extends Annotation> T getAnnotation(@RecentlyNonNull Class<T> var1)
,获取对象上的指定注解(包括继承)<T extends Annotation> T[] getAnnotationsByType(@RecentlyNonNull Class<T> annotationClass)
,获取指定类型的注解在该类上Annotation数组,包括继承的<T extends Annotation> T getDeclaredAnnotation(@RecentlyNonNull Class<T> annotationClass)
, 用于获取对象上的指定注解,不包括继承<T extends Annotation> T[] getDeclaredAnnotationsByType(@RecentlyNonNull Class<T> annotationClass)
,获取指定类型的注解在该类上Annotation数组,不包括继承的Annotation[] getDeclaredAnnotations()
,获取自己声明的所有注解,不包括继承
AccessibleObject
其中,我们的Field
,Method
,Constructor
都是继承了该类的,这个类我们主要用到的就是一个方法setAccessible(boolean flag)
,在需要反射时调用并传入true
,就可以提高反射速度,即使private
修饰的也可以访问,原因则是true
跳过了访问检查。
Member
其中,我们的Field
,Method
,Constructor
都是实现了该接口,他的主要作用是抽象了几个实现类的通用点,具体的方法如下:
Class<?> getDeclaringClass()
获取定义的所在类,如果自身与父类都存在对应的方法/属性/构造函数,则返回自身的String getName()
获取名称int getModifiers()
获取总体的修饰符的|
值
GenericDeclaration
其中,我们的Class
,Method
,Constructor
,他只有一个方法TypeVariable<?>[] getTypeParameters()
,获取修饰的泛型数组,返回的TypeVariable
可以获取泛型的相关信息,假如该类的泛型是非具体的则返回非具体的泛型名称,具体的则是返回具体的泛型类型。比如:
1 | List<String> list = new ArrayList<>(); |
Executable
我们的Method
,Constructor
都是继承了这个抽象类,他主要是定义了对于方法的一些抽象处理,因为对于Method
,Constructor
都是属于方法,有着较多的共性。在该类定义的方法,然后在Method
,Constructor
实现,他们的意思都是差不多的。
Class<?>[] getParameterTypes()
获取参数列表的Class
对象int getParameterCount()
获取参数个数Type[] getGenericParameterTypes()
假如参数中有泛型则返回泛型类型的数组,否则返回getParameterTypes()
Parameter[] getParameters()
,返回类型为Parameter
的参数列表数组,关于Parameter
后续会说Class<?>[] getExceptionTypes()
获取通过throw
抛出的异常的class列表Type[] getGenericExceptionTypes()
,假如throw
抛出的异常有泛型,则返回一个带有泛型的数组,否则就是返回getExceptionTypes()
boolean isVarArgs()
参数是否是可变参数Annotation[][] getParameterAnnotations()
,获取参数的注解数组,因为每个参数都可以被多个注解注解,所以是二位数组AnnotatedType getAnnotatedReturnType()
获取返回值中包含了注解信息AnnotatedType类型的对象,通过该对象可以得到AnnotatedType getAnnotatedReceiverType()
返回一个AnnotatedType
对象AnnotatedType[] getAnnotatedParameterTypes()
表示该方法/构造函数的接收者类型(自己也搞不太明白这个方法) 获取参数的注解的AnnotatedType对象数组,多个参数所以是一个数组AnnotatedType[] getAnnotatedExceptionTypes()
,获取throws
抛出的异常的AnnotatedType
数组
下面是一些例子,简单是就不用例子了
1 |
|
Type
我们的Class
是实现了该接口,他提供了一个方法String getTypeName()
用于获取类型名称,不同实现不同。
Class
Class对象是反射的基础,我们获取Class对象的方式有三种:
public class Person {
}
// 获取Class对象的方式
// 能引用到该类路径的时候,可以使用这种方式
Class<Person> clazz = Person.class;
//无法引用到类路径,但是知道他的类路径可以使用这种,但是他会抛出异常,因为路径可能是错误的
Class<?> clazz2 = Class.forName("com.github.sample.Person");
Person p =new Person();
// 有改类的对象的时候,可以通过getClass得到Class对象
Class<?> clazz3 = p.getClass();
即:
- 通过类名得到
- 通过
Class.forName()
得到 - 通过类对象得到
Class的常用方法
getName()
获取类的完整名称,返回虚拟机里面的Class的标识,但是对于内部类(无论是静态还是非静态),他是带有$$
符号的,对于数组,他是以L[
开头的getCanonicalName()
,一般情况下他与上面的一般是一样的,对于内部类(无论是静态还是非静态),他是以.
切割的,对于数组array
,他说以标准类名+[]
结尾getSimpleName()
获取类名称,非全路径,内部类(无论是静态还是非静态),都只是获取类名称,而不是带有$$
形式的。
1 | package com.github.io; |
getPackageName()
获取包名,内部类也只是获取包名,不包括他的外部类名称,获取数组的时候会带有[]
getFields()
获取public
的属性,包括父类的公共属性,假如二者名称一样,则会获取多个。getField(String name)
,获取特定public
的属性,如果自身与父类都有这个public
的,则获取自身,从下往上依次查找,不存在则抛出异常getDeclaredFields()
,获取自身声明的所有属性,无法获取父类的getDeclaredField(String name)
,获取自身声明的特定属性,不存在则抛出异常getMethods()
,获取自身的public
方法,包括父类的,比如Object
类的相关方法getMethod(String name)
,获取public
方法,与getField(String name)
差不多getDeclaredMethods()
,获取自身声明的所有方法,无法获取父类的getDeclaredMethod(String name)
,获取自身声明的特定方法与getDeclaredField(String name)
差不多getConstructors()
,获取public
的构造方法,非public
类而且没有声明显式构造函数的时候返回的是空数组,public
类而且没有声明返回默认构造函数,数组长度为1(即默认构造函数)。有声明则返回自身的public
构造函数,无public
的时候返回空数组getConstructor(Class<?>... parameterTypes)
,返回与parameterTypes参数类型一一对应类型的构造方法getDeclaredConstructors()
,返回自身声明的构造函数getDeclaredConstructor(Class<?>... parameterTypes)
,返回自身声明的而且与parameterTypes
参数一一对应的构造函数newInstance()
,java9废弃,他会使用class的空构造函数创建对象,类没有的话会抛出异常。
Class的类型判断方法
类型比较的三个常用方法
instanceof
,他说一个运算符,格式是obj instanceof Class
,即obj
的类型是否是Class
的本身或者是他的子类,他表达的意思是obj
是否可以转为Class
类对象。注意点是obj
不能是原始类型(Primitive),它可以是包装类,其中null instanceof Class
返回的是false
。该操作符是从对象的角度。isInstance(Object obj)
,他是class下的方法,标识当前的class
对象是否是obj
类相同或者是它的父类,是的话则返回true,即obj
类是调用者自身或者子类,它与instanceof
关键字不一样是,它的参数可以是原始类型的。isAssignableFrom(Class<?> cls)
,当前对象是不是B的父类或者是接口,true即B类型的对象可以转化为A类型,该方法是从类的角度。子类实例 instanceof 父类类型
父类.class.isInstance(子类实例)
父类.class.isAssignableFrom(子类Class)
其他常用的判断方法:
boolean isInterface()
,是否是接口boolean isArray()
,是否是数组对象boolean isPrimitive()
,是否是基本类型boolean isAnnotation()
,是否是注解boolean isAnonymousClass()
,是否是匿名类boolean isEnum()
,是否是枚举boolean isAnnotationPresent(@RecentlyNonNull Class<? extends Annotation> annotationClass)
,当前类型是否有被annotationClass类型注解,即声明当前类型的类上是否有annotationClass的注解。T cast(@RecentlyNullable Object obj)
,把obj对象为T对象,假如当前对象不是obj的父类或者自身,则抛出ClassCastException
异常,使用较少。
Class的父类,接口
String getSuperclass()
,获取父类Class对象String getPackage()
,获取该类的存放路径,基本类型或者是未经过类加载器加载的放回null,不常用String getPackageName()
,用于获取此实体的包名称,可以是数组,接口,基本类型等,假如是数组,则返回数组元素的最顶层的Class,比如有一个数组为Object[]
,则返回java.lang
,假如是基本类型的Class,则是返回java.lang
。java9之后有该方法。Class<?>[] getInterfaces()
,获取所有实现的接口数组int getModifiers()
,返回该类的修饰符,比如是public
或者是public final
等,他可以通过与(|)
操作指导是否被某个修饰符修饰。<U> Class<? extends U> asSubclass(Class<U> clazz)
,如果传入的clazz
是当前Class<u> clazz
的父类或者自身,则把自身强制转换为Class<u>
对象,否则抛出ClassCastException
异常Class<?> getComponentType()
,该方法是用于数组中,假如是非数组类型的Class对象调用,则返回的是null。我们可以通过该返回获取到数组的ComponentType,然后通过Array
类中的静态方法Object newInstance(Class<?> componentType, int length)
方法创建一个数组对象,其中传入的componentType可以是基本类型的,也可以是引用类型的。
Method
获取Method对象,有下面四种方式,他们的区别上面就有说过了,我们看下Method的一些常用方法,父类的方法不再赘述。
1 | Method[] getMethods() |
Class<?> getReturnType()
,获取方法的返回类型Type getGenericReturnType()
返回一个Type对象,该对象表示方法对象的形式返回类型,假如该方法的返回是泛型,则返回的是泛型的名称。Object invoke(Object obj, Object... args)
,调用对象obj上的该方法,参数是argsboolean isDefault()
,是否是接口的默认方法boolean isBridge()
是否是桥接方法Object getDefaultValue()
,用户获取注解类的方法的默认值。
Constructor
获取Constructor
的方式
1 | Constructor<T> getConstructor(Class<?>... parameterTypes) // 获取public的特定参数的构造方法 |
方法:
T newInstance(Object ... initargs)
实例化一个对象,initargs
为传入的参数,需要与想使用的构造函数的参数一致,包括类型。
Parameter
参数类,实现了AnnotatedElement
接口,构造/普通方法可以通过Parameter[] getParameters()
获取到Parameter
对象数组,父类的方法不再赘述,他的常用方法如下
int getModifiers()
获取修饰符String getName()
获取名称Type getParameterizedType()
假如是泛型,则返回泛型名称, 否则返回getType()
的值Class<?> getType()
获取声明的类型AnnotatedType getAnnotatedType()
获取AnnotatedType
对象,进而可以获取注解boolean isVarArgs()
,是否是可变参数
Field
获取Method对象,有下面四种方式,他们的区别上面就有说过了,我们看下Method的一些常用方法
1 | Field[] getFields() |
boolean isEnumConstant()
是否是枚举元素Class<?> getType()
获取变量声明类型ClassType getGenericType()
,获取变量声明的类型,假如是泛型则返回泛型符号Object get(Object obj)
获取obj
对象的对应的该field值,其他的get方法类似set(Object obj, Object value)
,无论该set方法还是其他的set方法,都是设置obj
对象上的field的值为value
AnnotatedType getAnnotatedType()
返回一个AnnotatedType
对象,该对象表示此Field表示的字段的声明类型,可以获取相关的注解信息。
Modifier
该类是用来提供计算某个类/方法/构造函数/数学等是否被某个限定修饰符修饰了,通过他们自身的getModifiers()
得到一个修饰符总和,之后他的返回参数作为Modifier类的相关方法传入参数:
isPublic(int mod)
是否是public方法isPrivate(int mod)
是否是private方法isProtected(int mod)
isStatic(int mod)
isFinal(int mod)
isSynchronized(int mod)
isVolatile(int mod)
isTransient(int mod)
isNative(int mod)
是否是本地方法isInterface(int mod)
是否被interface修饰isAbstract(int mod)
是否是抽象isStrict(int mod)
还提供了一些列获取修饰符的值的方法,比如private
修饰符的值等
int interfaceModifiers()
可以修饰Interface的修饰符的|
值int constructorModifiers()
可以修饰Constructor的修饰符的|
值int methodModifiers()
可以修饰方法的修饰符的|
值int fieldModifiers()
,可以修饰filed的修饰符的|
值parameterModifiers()
,可以修饰参数的修饰符
注意点
- 我们使用反射的时候,被
static final
修饰的字符串或者是基本类型的变量,是无法对它做出修改的,只能读取。 - 我们的异常类的声明的时候是无法使用泛型的。