理解反射
Class类也是类的一种,与class关键字是不一样的。
手动编写的类被编译后会产生一个Class对象,其表示的是创建的类的类型信息,而且这个Class对象保存在同名.class的字节码文件,每个通过关键字class标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个Class对象。
Class类的对象作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要。
Class的初始化时机
在虚拟机规范严格规定了有且只有5种场景必须对类进行初始化:
1)使用new关键字实例化对象时、读取或者设置一个类的静态字段(不包含编译期常量)以及调用静态方法的时候,必须触发类加载的初始化过程(类加载过程最终阶段)。
2)使用反射包(java.lang.reflect)的方法对类进行反射调用时,如果类还没有被初始化,则需先进行初始化,这点对反射很重要。
3)当初始化一个类的时候,如果其父类还没进行初始化则需先触发其父类的初始化。
4)当Java虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的类),虚拟机会先初始化这个主类
5)当使用JDK 1.7 的动态语言支持时,如果一个java.lang.invoke.MethodHandle 实例最后解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄对应类没有初始化时,必须触发其初始化
Class引用获取
对于一般的对象而言,获取Class对象引用的方式3种:
- 通过实例的getClass方法
- Class类的静态方法forName
- 字面常量的方式”.class”
- 使用类加载其进行加载获得class
例子如下:
class LoadClass{
}
public class LoadClassTest {
@Test
public void test001() throws ClassNotFoundException{
// 直接引用类
Class<?> c1 = LoadClass.class;
// 只用forName加载类
Class<?> c2 = Class.forName("com.zifang.util.zex.bust.chapter10.LoadClass");
// 从对象实例内获得class
Class<?> c3 = new LoadClass().getClass();
// 使用类加载器进行加载
Class<?> c4 = Thread.currentThread().getContextClassLoader().loadClass("com.zifang.util.zex.bust.chapter10.LoadClass");
System.out.println("c1:"+c1.getName());
System.out.println("c2:"+c2.getName());
System.out.println("c3:"+c3.getName());
System.out.println("c4:"+c2.getName());
}
}
加载会涉及到类的初始化阶段,这里需要注意的是,Class.forName得到的类是已经初始化完成的,ClassLoader.loadClass得到的类还未初始化完成,使用字面常量获得的class也是未初始化的。
但是对于基本类型而言就需要注意了,例如你需要获得int的类类型的时候,可以有以下方法:
Class c1 = int.class;
Class c2 = Class.getPrimitiveClass("int");
// c1 == c2
除此之外还需要注意数组的描述,你可以使用class.forName创建出数组类型:
Class c1 = Class.forName("[I");
// c1.getCanonicalName() -> int[]
泛化的Class对象引用
由于Class的引用总数指向某个类的Class对象,利用Class对象可以创建实例类,这也就足以说明Class对象的引用指向的对象确切的类型。在Java SE5引入泛型后,使用我们可以利用泛型来表示Class对象更具体的类型,即使在运行期间会被擦除,但编译期足以确保我们使用正确的对象类型。如下:
public class ClazzDemo {
public static void main(String[] args){
//没有泛型
Class intClass = int.class;
//带泛型的Class对象
Class<Integer> integerClass = int.class;
integerClass = Integer.class;
//没有泛型的约束,可以随意赋值
intClass= double.class;
//编译期错误,无法编译通过
//integerClass = double.class
}
}
从代码可以看出,声明普通的Class对象,在编译器并不会检查Class对象的确切类型是否符合要求,如果存在错误只有在运行时才得以暴露出来。但是通过泛型声明指明类型的Class对象,编译器在编译期将对带泛型的类进行额外的类型检查,确保在编译期就能保证类型的正确性,实际上Integer.class就是一个Class类的对象。面对下述语句,确实可能令人困惑,但该语句确实是无法编译通过的。
Class<Number> numberClass = Integer.class;
我们或许会想Integer不就是Number的子类吗?然而事实并非这般简单,毕竟Integer的Class对象并非Number的Class对象的子类,前面提到过,所有的Class对象都只来源于Class类,看来事实确实如此。当然我们可以利用通配符“?”来解决问题:
Class<?> intClass = int.class;
intClass = double.class;
这样的语句并没有什么问题,毕竟通配符指明所有类型都适用,那么为什么不直接使用Class还要使用Class<?>呢?这样做的好处是告诉编译器,我们是确实是采用任意类型的泛型,而非忘记使用泛型约束,因此Class泛型总是优于直接使用Class,至少前者在编译器检查时不会产生警告信息。当然我们还可以使用extends关键字告诉编译器接收某个类型的子类,如解决前面Number与Integer的问题:
//编译通过!
Class<? extends Number> clazz = Integer.class;
//赋予其他类型
clazz = double.class;
clazz = Number.class;
上述的代码是行得通的,extends关键字的作用是告诉编译器,只要是Number的子类都可以赋值。这点与前面直接使用Class是不一样的。实际上,应该时刻记住向Class引用添加泛型约束仅仅是为了提供编译期类型的检查从而避免将错误延续到运行时期。
反射获取类信息
基本上来说,除了方法体本身之外,所有肉眼可见的代码都可以被反射的机制在运行时捕获。这里先将我们进行测试用的代码拿出来:
interface SuperInterface{
default void defaultFunction(){}
void commonFunction();
}
class Father implements @Annotation("接口注解") SuperInterface{
private static Integer fatherStaticPrivateField;
protected Integer fatherProtectedField;
@Override
public void commonFunction() {}
public void fatherPublicFunction(){}
private void fatherPrivateFunction(){}
protected void fatherProtectedFunction(){}
}
@Annotation("Son类注解")
class Son <@AnnotationTest("类变量类型(泛型)上的注解") T0, T1> extends @Annotation("继承类上的注解") Father{
@Annotation("son.sonStaticPrivateField:类字段")
private static Integer sonStaticPrivateField;
@Annotation("son.sonProtectedField:类字段")
protected Integer sonProtectedField;
@Annotation("son.sonPublicField:类字段")
public Integer sonPublicField;
@AnnotationTest("son.map:类字段")
private Map<@AnnotationTest("成员变量泛型上的注解") String, String> map;
@AnnotationTest("构造函数上的注解")
public Son(){}
public Son(String name){}
private Son(String name,Integer age){}
@AnnotationTest("成员方法上的注解")
public void sonPublicFunction(@Annotation("son#sonPublicFunction:i方法字段") int i,
@Annotation("son#sonPublicFunction:integer方法字段") Integer integer){}
private void sonPrivateFunction(@Annotation("son#sonPrivateFunction:is方法字段") int[] is,
@Annotation("son#sonPrivateFunction:integers方法字段") Integer[] integers){}
public class SonSub{}
public static class SonSubStatic{
}
}
enum Code{
Code1,Code2;
}
@Target({ElementType.TYPE,
ElementType.METHOD,
ElementType.FIELD,
ElementType.TYPE_USE,
ElementType.PARAMETER,
ElementType.CONSTRUCTOR})
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface Annotation{
String value();
}
接下来就按照类的成分一点点来看Class下的API。
类基本信息获取
类声明信息获取
首先对于一个简单类而言,我们可以获得其类声明信息:
@Test
public void test001(){
// 获得类基础信息
Class<?> c = ParserClass.class;
System.out.printf("%s 类包名 : %s%n",c.getName(),c.getPackage().getName());
System.out.printf("%s 类名 : %s%n",c.getName(),c.getName());
System.out.printf("%s 类短名 : %s%n",c.getName(),c.getSimpleName());
}
得到输出是:
类包名:com.zifang.util.zex.bust.chapter10
类名:com.zifang.util.zex.bust.chapter10.ParserClass
类短名:ParserClass
类标记信息获取
之前的代码解决了类名,包名问题,类上的public标记与类的类型实际没有展示,可以靠以下的代码进行抽出:
@Test
public void test002() throws ClassNotFoundException {
System.out.printf("%s 类是否是枚举类型 : %s%n",ParserClass.class.getName(),ParserClass.class.isEnum());
System.out.printf("%s 类是否是枚举类型 : %s%n",Code.class.getName(),Code.class.isEnum());
System.out.println("\n");
System.out.printf("%s 类是否是注解类型 : %s%n",ParserClass.class.getName(),ParserClass.class.isAnnotation());
System.out.printf("%s 类是否是注解类型 : %s%n",Annotation.class.getName(),Annotation.class.isAnnotation());
System.out.println("\n");
System.out.printf("%s 类是否是接口类型 : %s%n",ParserClass.class.getName(),ParserClass.class.isInterface());
System.out.printf("%s 类是否是接口类型 : %s%n",SuperInterface.class.getName(),SuperInterface.class.isInterface());
System.out.println("\n");
System.out.printf("%s 类是否是基本变量类型 : %s%n",ParserClass.class.getName(),ParserClass.class.isPrimitive());
System.out.printf("%s 类是否是基本变量类型 : %s%n",int.class.getName(),int.class.isPrimitive());
System.out.println("\n");
System.out.printf("%s 类是否是数组类型 : %s%n",ParserClass.class.getName(),ParserClass.class.isArray());
System.out.printf("%s 类是否是数组类型 : %s%n",Class.forName("[I").getName(),Class.forName("[I").isArray());
System.out.println("\n");
System.out.printf("%s 类是否继承自 %s: %s%n", ParserClass.class.getName(), Father.class.getName(), ParserClass.class.isAssignableFrom(Father.class));
System.out.printf("%s 类是否继承自 %s: %s%n", Son.class.getName(), Father.class.getName(), Son.class.isAssignableFrom(Father.class));
System.out.println("\n");
System.out.printf("%s 类实例是是匿名类 : %s%n", ParserClass.class.getName(), new ParserClass().getClass().isAnonymousClass());
System.out.printf("%s 类实例是是匿名类 : %s%n", Son.class.getName(), new Runnable() {
@Override
public void run() {
System.out.println();
}
}.getClass().isAnonymousClass());
System.out.println("\n");
class LocalClass{}
System.out.printf("%s 类是否为局部类 : %s%n", LocalClass.class.getName(),LocalClass.class.isLocalClass());
System.out.printf("%s 类是否为局部类 : %s%n", FieldLocalClass.class.getName(),FieldLocalClass.class.isLocalClass());
System.out.printf("%s 类是否为局部类 : %s%n", ParserClass.class.getName(),ParserClass.class.isLocalClass());
System.out.println("\n");
System.out.printf("%s 类是否为成员类 : %s%n", LocalClass.class.getName(),LocalClass.class.isMemberClass());
System.out.printf("%s 类是否为成员类 : %s%n", FieldLocalClass.class.getName(),FieldLocalClass.class.isMemberClass());
System.out.printf("%s 类是否为成员类 : %s%n", ParserClass.class.getName(),ParserClass.class.isMemberClass());
System.out.println("\n");
System.out.printf("%s 是否实现注解%s : %s%n", Father.class.getName(),Annotation.class.getName(), Father.class.isAnnotationPresent(Annotation.class));
System.out.printf("%s 是否实现注解%s : %s%n", Son.class.getName(),Annotation.class.getName(), Son.class.isAnnotationPresent(Annotation.class));
}
得到的输出是:
com.zifang.util.zex.bust.chapter10.ParserClass 类是否是枚举类型 : false
com.zifang.util.zex.bust.chapter10.Code 类是否是枚举类型 : true
com.zifang.util.zex.bust.chapter10.ParserClass 类是否是注解类型 : false
com.zifang.util.zex.bust.chapter10.Annotation 类是否是注解类型 : true
com.zifang.util.zex.bust.chapter10.ParserClass 类是否是接口类型 : false
com.zifang.util.zex.bust.chapter10.SuperInterface 类是否是接口类型 : true
com.zifang.util.zex.bust.chapter10.ParserClass 类是否是基本变量类型 : false
int 类是否是基本变量类型 : true
com.zifang.util.zex.bust.chapter10.ParserClass 类是否是数组类型 : false
[I 类是否是数组类型 : true
com.zifang.util.zex.bust.chapter10.ParserClass 类是否继承自 com.zifang.util.zex.bust.chapter10.Father: false
com.zifang.util.zex.bust.chapter10.Son 类是否继承自 com.zifang.util.zex.bust.chapter10.Father: false
com.zifang.util.zex.bust.chapter10.ParserClass 类实例是是匿名类 : false
com.zifang.util.zex.bust.chapter10.Son 类实例是是匿名类 : true
com.zifang.util.zex.bust.chapter10.ParserClass$1LocalClass 类是否为局部类 : true
com.zifang.util.zex.bust.chapter10.ParserClass$FieldLocalClass 类是否为局部类 : false
com.zifang.util.zex.bust.chapter10.ParserClass 类是否为局部类 : false
com.zifang.util.zex.bust.chapter10.ParserClass$1LocalClass 类是否为成员类 : false
com.zifang.util.zex.bust.chapter10.ParserClass$FieldLocalClass 类是否为成员类 : true
com.zifang.util.zex.bust.chapter10.ParserClass 类是否为成员类 : false
com.zifang.util.zex.bust.chapter10.Father 是否实现注解com.zifang.util.zex.bust.chapter10.Annotation : true
com.zifang.util.zex.bust.chapter10.Son 是否实现注解com.zifang.util.zex.bust.chapter10.Annotation : false
可以看到这里的api有很多针对类的标识符进行判断的方法,实际上有一种通用的判断,任何Type类型都可以取出其modifiers,Modifier中存在大量static方法帮助判断其类标记。
@Test
public void test002_1(){
System.out.println(Modifier.isPublic(ParserClass.class.getModifiers()));
}
数组类信息获取
数组类型与一般的类型不太一样,其信息可以依靠getComponentType获得其数组元素类型
@Test
public void test003(){
Class c = Integer[].class;
System.out.println("是否是一个数组:"+c.isArray());
System.out.println("数组元素的类类型:"+c.getComponentType().getName());
}
类成分信息获取
类内会有三种成分:内部类,字段,方法,任何一种成分可以进一步获取到其信息。信息结构如下:
- 当前类
- 内部类
- 字段
- 字段标记
- 字段声明类型
- 字段名称信息
- 方法
- 方法标记
- 方法返回类型
- 方法名称
- 方法入参列表
- 方法入参类型
- 方法入参形参名
类的内部类
这里将使用demo来描述上述涉及到的api,首先是从当前类内获得其内部声明类
@Test
public void test004(){
Class c = Son.class;
Class[] cs = c.getClasses();
for (Class clazz : cs){
System.out.println(clazz.getName());
}
}
得到输出是:
com.zifang.util.zex.bust.chapter10.Son$SonSubStatic
com.zifang.util.zex.bust.chapter10.Son$SonSub
需要注意的是,在类内定义类,需要使用public修饰,getClasses才能取出。内部类可以定义在类中也可以定义在局部,当定义在类中,可以从内部类上获得到依附的类,但是如果是局部的则需要使用相关方法获得其依附类
@Test
public void test004_1(){
Class c = Son.SonSub.class;
System.out.println("1. 获得内部类的声明类:"+c.getDeclaringClass().getName());
class Inner{
class InnerInner{}
}
Class innerInner = Inner.InnerInner.class;
System.out.println("2. 获得局部内部类的声明类:getDeclaringClass:"+innerInner.getDeclaringClass().getName());
System.out.println("3. 获得局部内部类的声明类:getEnclosingClass:"+innerInner.getEnclosingClass().getName());
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
System.out.println("4. 获得局部内部类的声明类:getDeclaringClass:"+runnable.getClass().getDeclaringClass());
System.out.println("5. 获得局部内部类的声明类:getEnclosingClass:"+ runnable.getClass().getEnclosingClass());
// runnable.getClass().getConstructors() 将返回空数组
System.out.println("6. 获得局部内部类的声明类:getEnclosingConstructor:"+ runnable.getClass().getEnclosingConstructor());
// 返回 public void com.zifang.util.zex.bust.chapter10.ParserClass.test004_1()
System.out.println("7. 获得局部内部类的声明类:getEnclosingMethod:"+ runnable.getClass().getEnclosingMethod());
}
得到输出如下:
1. 获得内部类的声明类:com.zifang.util.zex.bust.chapter10.Son
2. 获得局部内部类的声明类:getDeclaringClass:com.zifang.util.zex.bust.chapter10.ParserClass$1Inner
3. 获得局部内部类的声明类:getEnclosingClass:com.zifang.util.zex.bust.chapter10.ParserClass$1Inner
4. 获得局部内部类的声明类:getDeclaringClass:null
5. 获得局部内部类的声明类:getEnclosingClass:class com.zifang.util.zex.bust.chapter10.ParserClass
6. 获得局部内部类的声明类:getEnclosingConstructor:null
7. 获得局部内部类的声明类:getEnclosingMethod:public void com.zifang.util.zex.bust.chapter10.ParserClass.test004_1()
类字段信息
接下去是字段信息:
@Test
public void test005(){
Class<?> c = Son.class;
System.out.println("getFields-----------");
for(Field field : c.getFields()){
System.out.println("fieldFlag:" + field.getModifiers());
System.out.println("fieldType:" + field.getType().getName());
System.out.println("fieldName:" + field.getName());
System.out.println("");
}
System.out.println("getDeclaredFields-----------");
for(Field field : c.getDeclaredFields()){
System.out.println("fieldFlag:" + field.getModifiers());
System.out.println("fieldType:" + field.getType().getName());
System.out.println("fieldName:" + field.getName());
System.out.println("");
}
}
这里需要注意getFields与getDeclaredFields的区别,getDeclaredFields表达当前类内定义的无论是什么标记都会拿出,但是getFields只会拿到对外部可见的字段列表。如果你直到字段的名称,可以不需要遍历,直接使用以下方法就可以获得到其字段对象:
@Test
public void test006() throws NoSuchFieldException {
Class<?> c = Son.class;
System.out.println("getField(sonPublicField)-----------");
Field field = c.getField("sonPublicField");
System.out.println("fieldFlag:" + field.getModifiers());
System.out.println("fieldType:" + field.getType().getName());
System.out.println("fieldName:" + field.getName());
System.out.println("");
System.out.println("getDeclaredField(sonPublicField)-----------");
Field field2 = c.getDeclaredField("sonStaticPrivateField");
System.out.println("fieldFlag:" + field2.getModifiers());
System.out.println("fieldType:" + field2.getType().getName());
System.out.println("fieldName:" + field2.getName());
System.out.println("");
}
得到输出:
getField(sonPublicField)-----------
fieldFlag:1
fieldType:java.lang.Integer
fieldName:sonPublicField
getDeclaredField(sonPublicField)-----------
fieldFlag:10
fieldType:java.lang.Integer
fieldName:sonStaticPrivateField
类方法信息
同类字段信息一样同样可以通过getMethods与getDeclareMethods方法获得到当前类的方法信息。前者获得当前类可见的方法,后者获得在当前类内定义的方法。
@Test
public void test007(){
Class<?> c = Son.class;
System.out.println("c.getMethods()------");
for(Method method : c.getMethods()){
if(method.getDeclaringClass() == Object.class){
continue;
}
System.out.println("methodFlag:" + method.getModifiers());
System.out.println("methodReturnType:" + method.getReturnType().getName());
System.out.println("methodName:" + method.getName());
List<String> params = new ArrayList<>();
for(Parameter parameter : method.getParameters()){
params.add(String.format("类型:%s 局部形参名:parameterName:%s",parameter.getType().getName(), parameter.getName()));
}
System.out.println("methodParamDesc:" + String.join(",", params));
System.out.println();
}
System.out.println("c.getDeclaredMethods()------");
for(Method method : c.getDeclaredMethods()){
if(method.getDeclaringClass() == Object.class){
continue;
}
System.out.println("methodFlag:" + method.getModifiers());
System.out.println("methodReturnType:" + method.getReturnType().getName());
System.out.println("methodName:" + method.getName());
List<String> params = new ArrayList<>();
for(Parameter parameter : method.getParameters()){
params.add(String.format("类型:%s 局部形参名:parameterName:%s",parameter.getType().getName(), parameter.getName()));
}
System.out.println("methodParamDesc:" + String.join(",", params));
System.out.println();
}
}
获得到输出是:
c.getMethods()------
methodFlag:1
methodReturnType:void
methodName:sonPublicFunction
methodParamDesc:类型:int 局部形参名:parameterName:arg0,类型:java.lang.Integer 局部形参名:parameterName:arg1
methodFlag:1
methodReturnType:void
methodName:commonFunction
methodParamDesc:
methodFlag:1
methodReturnType:void
methodName:fatherPublicFunction
methodParamDesc:
methodFlag:1
methodReturnType:void
methodName:defaultFunction
methodParamDesc:
c.getDeclaredMethods()------
methodFlag:2
methodReturnType:void
methodName:sonPrivateFunction
methodParamDesc:类型:[I 局部形参名:parameterName:arg0,类型:[Ljava.lang.Integer; 局部形参名:parameterName:arg1
methodFlag:1
methodReturnType:void
methodName:sonPublicFunction
methodParamDesc:类型:int 局部形参名:parameterName:arg0,类型:java.lang.Integer 局部形参名:parameterName:arg1
类构造器信息
我们直到类的初始化离不开其构造器,我们可以使用反射的方式捕获到当前类声明的构造器:
@Test
public void test008(){
Class<?> c = Son.class;
System.out.println("c.getConstructors()------");
for (Constructor<?> constructor : c.getConstructors()){
System.out.println("constructorFlag:" + constructor.getModifiers());
List<String> params = new ArrayList<>();
for(Parameter parameter : constructor.getParameters()){
params.add(String.format("类型:%s 局部形参名:parameterName:%s",parameter.getType().getName(), parameter.getName()));
}
System.out.println("constructorParamDesc:" + String.join(",", params));
System.out.println();
}
System.out.println("c.getDeclaredConstructors()------");
for (Constructor<?> constructor : c.getDeclaredConstructors()){
System.out.println("constructorFlag:" + constructor.getModifiers());
List<String> params = new ArrayList<>();
for(Parameter parameter : constructor.getParameters()){
params.add(String.format("类型:%s 局部形参名:parameterName:%s",parameter.getType().getName(), parameter.getName()));
}
System.out.println("constructorParamDesc:" + String.join(",", params));
System.out.println();
}
}
得到的输出是:
c.getConstructors()------
constructorFlag:1
constructorParamDesc:类型:java.lang.String 局部形参名:parameterName:arg0
constructorFlag:1
constructorParamDesc:
c.getDeclaredConstructors()------
constructorFlag:2
constructorParamDesc:类型:java.lang.String 局部形参名:parameterName:arg0,类型:java.lang.Integer 局部形参名:parameterName:arg1
constructorFlag:1
constructorParamDesc:类型:java.lang.String 局部形参名:parameterName:arg0
constructorFlag:1
constructorParamDesc:
可以看到如果构造器是private修饰的场合下,getConstructors方法不会获得该构造器
类继承关系获取
上面解决了一个类内的信息获取,这里就需要处理类与类之间的关系,一般而言会存在父类或者接口,如下:
@Test
public void test009(){
Class<?> c = Son.class;
System.out.println("son的父类:"+c.getSuperclass().getName());
System.out.println("son的父类的接口:"+c.getSuperclass().getInterfaces()[0].getName());
}
获得到的输出如下:
son的父类:com.zifang.util.zex.bust.chapter10.Father
son的父类的接口:com.zifang.util.zex.bust.chapter10.SuperInterface
注解类信息获取
注解信息可以放在很多地方,这里单独拎出来做展示:
@Test
public void test010() throws NoSuchMethodException {
Class<Son> clazz = Son.class;
System.out.println("\n获取类上的注解-----");
java.lang.annotation.Annotation[] annotations = clazz.getAnnotations();
for (java.lang.annotation.Annotation annotation : annotations) {
System.out.println("存在注解:"+annotation.getClass().getName()+":value = "+((Annotation)annotation).value());
}
System.out.println("\n获取类上接口的注解-----");
AnnotatedType[] annotatedInterfaces = Father.class.getAnnotatedInterfaces();
for (AnnotatedType annotatedType : annotatedInterfaces) {
System.out.println("存在注解:"+annotatedType.getClass().getName()+":value = "+(annotatedType.getAnnotation(Annotation.class)).value());
}
System.out.println("\n获取类上继承类的注解-----");
AnnotatedType annotatedSuperclass = Father.class.getAnnotatedSuperclass();
System.out.println("存在注解:"+annotatedSuperclass.getClass().getName()+":value = "+(annotatedSuperclass.getAnnotation(Annotation.class)).value());
System.out.println("\n获取类的类型变量(泛型)的注解-----");
TypeVariable<Class<Son>>[] typeParameters = clazz.getTypeParameters();
for (TypeVariable typeVariable : typeParameters) {
System.out.println("获得泛型占位:"+typeVariable.getName());
java.lang.annotation.Annotation[] annotations1 = typeVariable.getAnnotations();
for (java.lang.annotation.Annotation annotation : annotations1) {
System.out.println("获得泛型占位上的注解:"+annotation);
}
}
System.out.println("\n获取成员变量上的注解-----");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("字段名称:"+field.getName());
for (java.lang.annotation.Annotation annotation : field.getAnnotations()) {
System.out.println("字段注解:"+annotation);
}
}
System.out.println("\n获取成员变量泛型的注解-----");
for (Field field : fields) {
if(field.getAnnotatedType() instanceof AnnotatedParameterizedType){
AnnotatedParameterizedType annotatedType = (AnnotatedParameterizedType) field.getAnnotatedType();
AnnotatedType[] typeArguments = annotatedType.getAnnotatedActualTypeArguments();
for (AnnotatedType typeArgument : typeArguments) {
for (java.lang.annotation.Annotation annotation : typeArgument.getAnnotations()) {
System.out.println("字段名称:"+field.getName());
System.out.println("字段注解:"+annotation);
}
}
}
}
System.out.println("\n获取成员方法上的注解-----");
Method test = clazz.getMethod("sonPublicFunction", int.class, Integer.class);
for (java.lang.annotation.Annotation annotation : test.getAnnotations()) {
System.out.println("方法名称:"+test.getName());
System.out.println(annotation);
}
System.out.println("\n获取成员方法上的注解-----");
for (Parameter parameter : test.getParameters()) {
for (java.lang.annotation.Annotation annotation : parameter.getAnnotations()) {
System.out.println("参数名称:"+parameter.getName());
System.out.println("参数注解:"+annotation);
}
}
System.out.println("\n获取构造函数上的注解-----");
Constructor<?> constructor = clazz.getConstructor();
for (java.lang.annotation.Annotation annotation : constructor.getAnnotations()) {
System.out.println("构造器描述:"+constructor.toString());
System.out.println("构造器注解:"+annotation);
}
}
获得到输出:
获取类上的注解-----
存在注解:com.zifang.util.zex.bust.chapter10.$Proxy4:value = Son类注解
获取类上接口的注解-----
存在注解:sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl:value = 接口注解
获取类上继承类的注解-----
存在注解:sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl:value = 继承类上的注解
获取类的类型变量(泛型)的注解-----
获得泛型占位:T0
获得泛型占位上的注解:@com.zifang.util.zex.bust.chapter9.AnnotationTest(value=类变量类型(泛型)上的注解)
获得泛型占位:T1
获取成员变量上的注解-----
字段名称:sonStaticPrivateField
字段注解:@com.zifang.util.zex.bust.chapter10.Annotation(value=son.sonStaticPrivateField:类字段)
字段名称:sonProtectedField
字段注解:@com.zifang.util.zex.bust.chapter10.Annotation(value=son.sonProtectedField:类字段)
字段名称:sonPublicField
字段注解:@com.zifang.util.zex.bust.chapter10.Annotation(value=son.sonPublicField:类字段)
字段名称:map
字段注解:@com.zifang.util.zex.bust.chapter9.AnnotationTest(value=son.map:类字段)
获取成员变量泛型的注解-----
字段名称:map
字段注解:@com.zifang.util.zex.bust.chapter9.AnnotationTest(value=成员变量泛型上的注解)
获取成员方法上的注解-----
方法名称:sonPublicFunction
@com.zifang.util.zex.bust.chapter9.AnnotationTest(value=成员方法上的注解)
获取成员方法上的注解-----
参数名称:arg0
参数注解:@com.zifang.util.zex.bust.chapter10.Annotation(value=son#sonPublicFunction:i方法字段)
参数名称:arg1
参数注解:@com.zifang.util.zex.bust.chapter10.Annotation(value=son#sonPublicFunction:integer方法字段)
获取构造函数上的注解-----
构造器描述:public com.zifang.util.zex.bust.chapter10.Son()
构造器注解:@com.zifang.util.zex.bust.chapter9.AnnotationTest(value=构造函数上的注解)
泛型化信息获取
虽说泛型信息在编译之后会被丢弃,但是实际在字节码层面还是存在该信息的,因此我们的反射可以捕捉到信息。这里我们构造出了一个新的测试类用来测试泛化信息。
interface GenericInterface<T,R> {
R call(T t);
}
class GenericFather<T,R> implements GenericInterface<T,R>{
public T t;
@Override
public R call(T t) {
return null;
}
public static <T,R> R doCall(T t){
return null;
}
}
class GenericSon extends GenericFather<String,Integer> implements GenericInterface<String,Integer>{
public static Map<String,Integer> doA(List<String> list){
return null;
}
}
GenericSon在编译期就携带上了具体的泛化信息,因此我们可以通过反射捕获到具体类型。
@Test
public void test001() throws NoSuchMethodException {
GenericSon genericSon = new GenericSon();
Class<?> c1 = genericSon.getClass();
System.out.println("\n解析继承的父类泛型信息----");
if(c1.getGenericSuperclass() instanceof ParameterizedType){
ParameterizedType parameterizedType = (ParameterizedType)c1.getGenericSuperclass();
for(Type type : parameterizedType.getActualTypeArguments()){
System.out.println("获得到父类泛型:"+type.getTypeName());
}
}
System.out.println("\n解析实现的接口泛型信息----");
for(Type type : c1.getGenericInterfaces()){
if(type instanceof ParameterizedType){
ParameterizedType parameterizedType = (ParameterizedType)type;
for(Type item : parameterizedType.getActualTypeArguments()){
System.out.println("获得到接口泛型:"+item.getTypeName());
}
}
}
System.out.println("\n解析doA方法返回类型泛型信息----");
Method method = c1.getMethod("doA",List.class);
Type type = method.getGenericReturnType();
if(type instanceof ParameterizedType){
ParameterizedType parameterizedType = (ParameterizedType)type;
for(Type item : parameterizedType.getActualTypeArguments()){
System.out.println("获得到方法返回泛型信息:"+item.getTypeName());
}
}
System.out.println("\n解析doA方法入参类型泛型信息----");
Type[] params = method.getGenericParameterTypes();
for(Type param : params){
if(param instanceof ParameterizedType){
ParameterizedType parameterizedType = (ParameterizedType)type;
for(Type item : parameterizedType.getActualTypeArguments()){
System.out.println("获得到方法入参泛型信息:"+item.getTypeName());
}
}
}
}
这里重点关注ParameterizedType对象,这个类代表了参数化泛型信息,它可以帮助我们获得到编译时携带上的确定的泛型信息。获得到输出是:
解析继承的父类泛型信息----
获得到父类泛型:java.lang.String
获得到父类泛型:java.lang.Integer
解析实现的接口泛型信息----
获得到接口泛型:java.lang.String
获得到接口泛型:java.lang.Integer
解析doA方法返回类型泛型信息----
获得到方法返回泛型信息:java.lang.String
获得到方法返回泛型信息:java.lang.Integer
解析doA方法入参类型泛型信息----
获得到方法入参泛型信息:java.lang.String
获得到方法入参泛型信息:java.lang.Integer
但是对于GenericFather而言,由于他编译时并没有确定具体的参数类型,因此使用上述方法获得的并不是ParameterizedType对象,而是TypeVariableImpl对象:
@Test
public void test002(){
GenericFather<String,Integer> father = new GenericFather<>();
System.out.println("\n解析泛化类型泛型信息----");
Type[] types = father.getClass().getTypeParameters();
for(Type type : types) {
if (type instanceof TypeVariable) {
TypeVariable typeVariable = (TypeVariable) type;
System.out.println("获得到泛化参数名称:" + typeVariable.getName());
}
}
}
获得到输出:
解析泛化类型泛型信息----
获得到泛化参数名称:T
获得到泛化参数名称:R
反射调用
创建实例
我们得到类之后就可以使用类信息进行创建实例而不需要使用new关键字,以下都是通过反射进行创建的方式:
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
class InvokeClass{
public InvokeClass(){
System.out.println("调用默认构造方法");
}
public InvokeClass(String desc){
System.out.println("调用构造方法:"+desc);
}
@Override
public String toString(){
return String.valueOf(this.hashCode());
}
}
public class InvokerTest {
@Test
public void test001() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
System.out.println("\n执行newInstance--");
InvokeClass i1 = InvokeClass.class.newInstance();
System.out.println("\n执行调用有参Constructor");
InvokeClass i2 = InvokeClass.class.getConstructor(String.class).newInstance("初始化");
System.out.println("\n执行调用无参Constructor");
InvokeClass i3 = InvokeClass.class.getConstructor().newInstance();
System.out.println("\n创建对象数组");
InvokeClass[] invokeClasses = (InvokeClass[]) Array.newInstance(InvokeClass.class,3);
invokeClasses[0] = i1;
invokeClasses[1] = i2;
invokeClasses[2] = i3;
System.out.println(Arrays.toString(invokeClasses));
}
}
获得输出:
执行newInstance--
调用默认构造方法
执行调用有参Constructor
调用构造方法:初始化
执行调用无参Constructor
调用默认构造方法
创建对象数组
[474675244, 932583850, 212628335]
修改字段
我们可以使用反射变更类字段的值
class InvokeClass{
public static String staticValue = "staticValue";
public String noStaticValue = "noStaticValue";
public InvokeClass(){
System.out.println("调用默认构造方法");
}
public InvokeClass(String desc){
System.out.println("调用构造方法:"+desc);
}
@Override
public String toString(){
return String.valueOf(this.hashCode());
}
}
public class InvokerTest {
@Test
public void test002() throws NoSuchFieldException, IllegalAccessException {
InvokeClass invokeClass = new InvokeClass();
System.out.println("\n打印两者数据");
System.out.println("invokeClass.noStaticValue: "+invokeClass.noStaticValue);
System.out.println("InvokeClass.staticValue: "+InvokeClass.staticValue);
System.out.println("\n使用反射的方式获得两者数据");
System.out.println("reflect:invokeClass.noStaticValue: "+invokeClass.getClass().getDeclaredField("noStaticValue").get(invokeClass));
System.out.println("reflect:InvokeClass.staticValue: "+InvokeClass.class.getDeclaredField("staticValue").get(null));
System.out.println("\n修改两者数据");
Field field1 = invokeClass.getClass().getDeclaredField("noStaticValue");
field1.setAccessible(true);
field1.set(invokeClass,"noStaticValue-new");
Field field2 = InvokeClass.class.getDeclaredField("staticValue");
field2.setAccessible(true);
field2.set(invokeClass,"staticValue-new");
System.out.println("\n打印修改后两者数据");
System.out.println("invokeClass.noStaticValue: "+invokeClass.noStaticValue);
System.out.println("InvokeClass.staticValue: "+InvokeClass.staticValue);
}
}
需要注意的是,使用field进行set的时候需要先执行setAccessible(true),否则无法操作。当操作static的field的时候,不需要传递对象实例。
调用方法
class InvokeClass{
public static String staticValue = "staticValue";
public String noStaticValue = "noStaticValue";
public InvokeClass(){
System.out.println("调用默认构造方法");
}
public InvokeClass(String desc){
System.out.println("调用构造方法:"+desc);
}
public static void staticHandle(){
System.out.println("staticHandle");
}
public void handle(){
System.out.println("handle");
}
@Override
public String toString(){
return String.valueOf(this.hashCode());
}
}
public class InvokerTest {
@Test
public void test003() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
InvokeClass invokeClass = new InvokeClass();
Method method1 = InvokeClass.class.getMethod("staticHandle");
method1.setAccessible(true);
Method method2 = InvokeClass.class.getMethod("handle");
method2.setAccessible(true);
method1.invoke(null);
method2.invoke(invokeClass);
}
}
类似掉用Field,调用Method同样需要优先使用setAccessible,否则无法调用。其次调用static方法的时候不需要携带实例对象。