已阅读5页,还剩5页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
详解Java反射各种应用Java除了给我们提供在编译期得到类的各种信息之外,还通过反射让我们可以在运行期间得到类的各种信息。通过反射获取类的信息,得到类的信息之后,就可以获取以下相关内容:Class对象构造器变量方法私有变量与私有方法注解泛型数组本文也将从上面几个方面来介绍Java反射。本文涉及的所有代码均在反射代码首先放出一个Java类作为反射的研究对象,类的内容如下:public abstract class FatherObject implements Runnable public void doSomething() System.out.println(做事情.); public class ExampleObject extends FatherObject public int age = 30; public String name = byhieg; private Integer score = 60; public void printName() System.out.println(name); public int getAge() return age; public void setAge(int age) this.age = age; public String getName() return name; public void setName(String name) = name; public Integer getScore() return score; public void setScore(Integer score) this.score = score; public ExampleObject() public ExampleObject(String name) public ExampleObject(int age,Integer score) Override public void doSomething() super.doSomething(); Override public void run() System.out.println(run.); Class对象我们应用会用到反射这个知识点,肯定是想要在运行时得到类的信息,根据类的那些信息去做一些特定的操作。那么,首先无疑就是得到类的信息,在JDK中提供了Class对象来保存类的信息。所以,反射的第一步就是得到Class对象。在JDK中提供了两种方式得到Class对象。第一种,如果编写代码的时候,就知道Class的名字,可以直接用如下方式得到Class对象:Class exampleObjectClass = ExampleObject.class;第二种,如果在编写代码的时候,不知道类的名字,但是在运行时的时候,可以得到一个类名的字符串,可以用如下的方式获取Class对象:Class exampleObjectClass = Class.forName(cn.byhieg.reflectiontutorial.ExampleObject);注意,此方法需要有2个条件,第一,forName中的字符串必须是全限定名,第二,这个Class类必须在classpath的路径下面,因为该方法会抛出ClassNotFoundException的异常。获取到这个Class对象之后,就可以得到类的各种信息,开头已经提及了一些信息,下面,说几个没提到的类的信息。得到类的名字类的名字有两种方式得到,一种是getName(),一种是getSimpleName()。第一种得到的是全限定名,第二种得到的是这个类的名字,不带包名。看下面的例子:Class对象,已经通过上面的代码得到了。 String fullClassName = exampleObjectClass.getName(); String simpleClassName = exampleObjectClass.getSimpleName(); System.out.println(fullClassName); System.out.println(simpleClassName);结果如下:cn.byhieg.reflectiontutorial.ExampleObjectExampleObject得到类的包名、父类和实现的接口类的包名和父类,可以通过如下代码得到。 /得到包信息 Package aPackage = exampleObjectClass.getPackage(); System.out.println(aPackage); /得到父类 Class superClass = exampleObjectClass.getSuperclass(); System.out.println(superClass.getSimpleName();结果如下:package cn.byhieg.reflectiontutorialFatherObject很显然,得到父类的返回值也是一个Class对象,那么可以利用这个对象得到父类的一些信息,比如判断父类是不是抽象类 System.out.println(父类是不是抽象类 + Modifier.isAbstract(superClass.getModifiers();getModifiers可以得到类的修饰符,从而得到类的修饰符,当然,这个getModifiers不仅仅Class对象可以调用,Method对象可以调用。可以使用java.lang.reflect.Modifier类中的方法来检查修饰符的类型:Modifier.isAbstract(int modifiers);Modifier.isFinal(int modifiers);Modifier.isInterface(int modifiers);Modifier.isNative(int modifiers);Modifier.isPrivate(int modifiers);Modifier.isProtected(int modifiers);Modifier.isPublic(int modifiers);Modifier.isStatic(int modifiers);Modifier.isStrict(int modifiers);Modifier.isSynchronized(int modifiers);Modifier.isTransient(int modifiers);Modifier.isVolatile(int modifiers);此外,我们还可以得到父类实现的接口 /得到接口 Class classes = superClass.getInterfaces(); System.out.println(父类的接口 + classes0);因为Java类可以实现很多接口,所以用的数组,但在实际使用的时候,需要先判断数组的长度。下面,重点讲解上述列出来的内容。构造器利用Java反射可以得到一个类的构造器,并根据构造器,在运行时动态的创建一个对象。首先,Java通过以下方式获取构造器的实例: /构造器 Constructor constructors = exampleObjectClass.getConstructors(); for (Constructor constructor : constructors) System.out.println(constructor.toString(); 结果如下:public cn.byhieg.reflectiontutorial.ExampleObject(int,java.lang.Integer)public cn.byhieg.reflectiontutorial.ExampleObject(java.lang.String)public cn.byhieg.reflectiontutorial.ExampleObject()如果,事先知道要访问的构造方法的参数类型,可以利用如下方法获取指定的构造方法,例子如下: Constructor constructor = exampleObjectClass.getConstructor(String.class); System.out.println(constructor.toString();结果显然是:public cn.byhieg.reflectiontutorial.ExampleObject(java.lang.String)还可以用如下方式得到另一个构造器 Constructor constructor = exampleObjectClass.getConstructor(int.class,Integer.class); System.out.println(constructor.toString();此外,如果我们不知道构造器的参数,只能得到所有的构造器对象,那么可以用如下方式得到每一个构造器对想的参数: Constructor constructors = exampleObjectClass.getConstructors(); for (Constructor constructor : constructors) Class parameterTypes = constructor.getParameterTypes(); System.out.println(构造器参数如下=); for (Class clz : parameterTypes) System.out.println(参数类型 + clz.toString(); 结果如下:构造器参数如下=参数类型 class java.lang.String构造器参数如下=参数类型 int参数类型 class java.lang.Integer这里,可以看出无参构造方法,是不打印出结果的。基本类型的Class对象和引用类型的Class对象toString()方法是不一样的。现在,可以根据构造器的各种信息,动态创建一个对象。 Object object = constructor.newInstance(1,100); System.out.println(object.toString();这个创建对象的方式有2个条件,第一是通过有参构造器创建的,第二,构造器对象必须通过传入参数信息的getConstructor得到。第一个条件,对于无参构造方法就可以创建的对象,不需要得到构造器对象,直接Class对象调用newInstance()方法就直接创建对象。第二个条件,构造器对象必须通过exampleObjectClass.getConstructor(String.class);这种形式得到。如果通过getConstructors得到构造器数组,然后调用指定的构造器对象去创建对象在JDK1.8是会错的。但是JDK1.6是正常的。变量利用Java反射可以在运行时得到一个类的变量信息,并且可以根据上面讲的方式,创建一个对象,设置他的变量值。首先,通过如下方法,得到所有public的变量: Field fields = exampleObjectClass.getFields(); for (Field field : fields) System.out.println(变量为: + field.toString(); 结果如下:变量为: public int cn.byhieg.reflectiontutorial.ExampleObject.age变量为: public java.lang.String cn.byhieg.reflectiontutorial.ExampleO很显然,得到的都是public的变量,上述的private的变量score,并没有得到。和构造器一样的得到方式一样,我们可以指定一个参数名,然后得到指定的变量: Field field = exampleObjectClass.getField(age); System.out.println(变量为: + field.toString();上述的变量的toString方法得到的名字太长,Java对Field类提供了getName的方法,返回类中写的变量名字,上面的代码就可以改成field.getName()。反射不仅提供了得到变量的方法,还提供了设置变量值的方式。通过如下方法可以对一个动态生成的类,改变其变量值: ExampleObject object = (ExampleObject) constructor1.newInstance(byhieg); System.out.println(原先的age是 + object.age); field.set(object,10); System.out.println(更改之后的age是 + object.age);结果如下:原先的age是 30更改之后的age是10根据上面的代码,得到名字为age的Field对象,然后调用该对象的set方法,传入一个对象与要更改的值,就可以改变该对象的值了。注意,此方法不仅仅对成员变量有用,对静态变量也可以。当然,如果是静态变量,传入null,不用传对象,也是可以的。方法Java反射给我们除了给我们提供类的变量信息之外,当然也给我们提供了方法的信息,反射可以让我们得到方法名,方法的参数,方法的返回类型,以及调用方法等功能。首先,通过如下代码得到方法: /输出类的public方法 Method methods = exampleObjectClass.getMethods(); for (Method method : methods) System.out.println(method = + method.getName(); 和获取变量一样似曾相识的代码,这里直接调用了getName,来得到类中写的方法名。写到这里,大家应该自然想到,Java同样提供了根据参数,得到具体的方法。 Method method = exampleObjectClass.getMethod(setAge,int.class); System.out.println(method.getName();这里与得到变量不同的是,getMethod方法还需要传入参数的类型信息,反射提供获取方法参数以及返回类型的方法,得到方法参数的例子如下: Method method = exampleObjectClass.getMethod(setAge,int.class); System.out.println(method.getName(); for (Class clz : method.getParameterTypes() System.out.println(方法的参数 + clz.getName(); 结果如下:setAge方法的参数int得到方法返回类型的例子如下:System.out.println(method.getReturnType().getName();结果如下:void此外,Java反射支持通过invoke调用得到的方法。例子如下:method.invoke(exampleObjectClass.newInstance(),1);invoke第一个参数是这个对象,第二个参数是变长数组,传入该方法的参数。和Field对象同样,对于静态方法同样,可以传入null,调用静态方法。私有变量与私有方法上面的方法只能得到public方法和变量,无法得到非public修饰的方法和变量,Java提供了额外的方法来得到非public变量与方法。即通过getDeclaredFields与getDeclaredMethods方法得到私有的变量与方法,同样也支持用getDeclaredField(变量名)与getDeclaredMethod(方法名)的形式得到指定的变量名与方法名。但是这样得到的Field对象与Method对象无法直接运用,必须让这些对象调用setAccessible(true),才能正常运用。之后的方式就可上面讲的一样了。注解先写一个包含注解的类:MyAnnotation(name=byhieg,value = hello world)public class AnnotationObject MyAnnotation(name=field,value = 变量) public String field; MyAnnotation(name=method,value = 方法) public void doSomeThing() System.out.println(做一些事情); public void doOtherThing(MyAnnotation(name=param,value = 参数) String param) Retention(RetentionPolicy.RUNTIME)public interface MyAnnotation public String name(); public String value();Java给我们提供了在运行时获取类的注解信息,可以得到类注解,方法注解,参数注解,变量注解。与上面获取方式一样,Java提供了2种获取方式,一种是获取全部的注解,返回一个数组,第二种是指定得到指定的注解。我们以一个类注解为例,讲解以下这两种获取方式。 Class clz = AnnotationObject.class; Annotation annotations = clz.getAnnotations(); Annotation annotation = clz.getAnnotation(AnnotationObject.class);然后,就可以根据得到的注解进行后续的处理,下面是一个处理的例子: for (Annotation annotation : annotations) if (annotation instanceof MyAnnotation) MyAnnotation myAnnotation = (MyAnnotation)annotation; System.out.println(name: + myA(); System.out.println(value: + myAnnotation.value(); 上面的类注解使用Class对象调用getAnnotations得到的,方法注解和变量注解是一样的,分别用method对象与field对象调用getDeclaredAnnotations得到注解,没什么多说的。例子看反射代码参数注解是比较麻烦的一项,获取方式比较得到,第一步,先取得method对象,调用getParameterAnnotations,但是这个返回值是一个二维数组,因为method对象有很多参数,每个参数有可能有很多注解。例子如下: Method method1 = clz.getMethod(doOtherThing,String.class); Annotation annotationInParam = method1.getParameterAnnotations(); Class params = method1.getParameterTypes(); int i = 0; for (Annotation annotations: annotationInParam) Class para = paramsi+; for (Annotation annotation : annotations) if(annotation instanceof MyAnnotation) MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println(param: + para.getName(); System.out.println(name : + myA(); System.out.println(value : + myAnnotation.value(); 泛型因为Java泛型是通过擦除来实现的,很难直接得到泛型具体的参数化类型的信息,但是我们可以通过一种间接的形式利用反射得到泛型信息。比如下面这个类:public class GenericObject public List lists; public List getLists() return lists; public void setLists(List lists) this.lists = lists; 如果一个方法返回一个泛型类,我们可以通过method对象去调用getGenericReturnType来得到这个泛型类具体的参数化类型是什么。看下面的代码: Class clz = GenericObject.class; Method method = clz.getMethod(getLists); Type genericType = method.getGenericReturnType(); if(genericType instanceof PeterizedType) ParameterizedType parameterizedType = (ParameterizedType) genericType); Type types = parameterizedType.getActualTypeArguments(); for (Type type : types) Class actualClz = (Class) type); System.out.println(参数化类型为 : + actualClz); 结果如下:参数化类型为 : class java.lang.String步骤有点繁琐,下面一步步解释:反射得到返回类型为泛型类的方法调用getGenericReturnType得到方法返回类型中的参数化类型判断该type对象能不能向下转型为ParameterizedType转型成功,调用getActualTypeArguments得到参数化类型的数组,因为有的泛型类,不只只有一个参数化类型如Map取出数组中的每一个的值,转型为Class对象输出。看结果确实得到了泛型的具体的信息。如果没有一个方法返回泛型类型,那么我们也可以通过方法的参数为泛型类,来得到泛型的参数化类型,如上面类中的setLists方法。例子如下: Method setMethod = clz.getMethod(setLists, List.class); Type genericParameterTypes = setMethod.getGenericParameterTypes(); for (Type genericParameterType: genericParameterTypes) System.out.println(GenericParameterTypes为 : + genericParameterType.getTypeName(); if(genericParameterType instanceof PeterizedType) ParameterizedType parameterizedType = (ParameterizedType) genericParameterType); System.out.println(ParameterizedType为 : + parameterizedType.getTypeName(); Type types = parameterizedType.getActualTypeArguments(); for (Type type : types) System.out.println(参数化类型为 : + (Class) type).getName(); 执行的结果如下:GenericParameterTypes为 : java.util.ListParameterizedType为 :java.util.List参数化类型为 : java.lang.String因为方法的参数为泛型类型的可能不止一个,所以通过getGenericParameterTypes得到是一个数组,我们需要确定每一个元素,是否是具有参数化类型。后续的步骤与上面类似,就不多说了。如果连方法参数都不带泛型类,那么只剩下最后一种情况,通过变量类型,即用Field类。例子如下: Field field = clz.getField(lists); Type type = field.getGenericType(
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 塔吊人工租赁合同范本
- 买苗木采购合同范本
- 合股经营合同4篇
- 贴片加工转让合同范本
- 门面租赁合同范本税票
- 品牌传媒公司合同范本
- 增城电梯保养合同范本
- 个人游戏交易合同范本
- 维修整治合同范本
- 2026年家庭育儿嫂服务合同
- 2025宁夏回族自治区大学生乡村医生专项计划招聘工作人员13人考试笔试模拟试题及答案解析
- 学校食堂满意度测评及管理方案
- 2025安徽清水街道招聘就业专干6人笔试考试参考试题附答案解析
- 小学语文教师素养大赛知识素养试题
- 北京市海淀区2025-2026学年高三上学期期中地理试题 含解析
- 施工现场安全事故应急预案
- 2025版疾病控制护理护士培训大纲
- 2025年中级消防设施操作员《理论知识》题库必做200题(含答案)
- JCT2112-2012 塑料防护排水板
- 110kV线路运维方案
- 北京第十三中学分校2023-2024学年九年级上学期期中物理试卷
评论
0/150
提交评论