本文记录是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的值为valueAnnotatedType 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修饰的字符串或者是基本类型的变量,是无法对它做出修改的,只能读取。 - 我们的异常类的声明的时候是无法使用泛型的。
