反射的基本概念:Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内 部信息,并能直接操作任意对象的内部属性及方法。加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Cl ass对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构 ,所以,我们形象的称之为:反射。如果正常情况下,要使用一个类,则必须按照骤操作,厦门北大青鸟的老师教你怎么操作:使用import导 入类所在的包,(类:java.lang.Class)明确的使用类名称或接口名称定义对象通过关键字new进行类对象实例化(构造方法: java.lang.reflect.Constructor)产生对象可以使用“对象.属性”进行类中的属性调用,(属性:java.l ang.reflect.Field)通过“对象.方法()”调用类中方法(方法:java.lang.reflect.Method)而 反射的过程呢?不需要有明确类型的对象,所有的对象使用Object表示:可以直接利用Object与反射机制的混合调用类中的方法Jav a反射机制提供的功能?在运行时判断任意一个对象所属的类?在运行时构造任意一个类的对象?在运行时判断任意一个类所具有的成员变量和方法 ?在运行时获取泛型信息?在运行时调用任意一个对象的成员变量和方法?在运行时处理注解?生成动态代理反射相关的主要API?java. lang.Class:代表一个类?java.lang.reflect.Method:代表类的方法?java.lang.re flect.Field:代表类的成员变量?java.lang.reflect.Constructor:代表类的构造器?… …1.Class类class类是整个反射操作源头,而这个类的定义如下:【参考JDK文档】但是如果想要使用Class类进行操作,那 么就必须首先产生Class的实例化对象,而有类的实例化对象,有如下三种方式可以获得Class类的实例化对象Object类提供了一个 返回Class类对象的方法,PublicClass>getClass():利用“类.class”获得,日后见得最后的就是在 hibernate上利用Class类的static方法取得publicstaticClass>forName(Strin gclassName)throwsClassNotFoundException如果是程序设计的人员,使用最多的方法一定是for Name()方法,但是如果是使用者,一定会使用“类.class”,通过之前的分析可以知道,工厂设计模式最好利用反射解决耦合问题2. 利用反射实例化对象Class类如果使用了forName()方法之后,就可以使用Class类定义的newInstance()方法默 认去调用类之中的无参构造方法进行操作:publicTnewInstance()throwsInstantiationExce ption,IllegalAccessException【创建由此类对象表示的类的新实例。该类被实例化为一个具有空参数列表的ne w表达式。如果类尚未初始化,则初始化该类。】这个时候可以非常清楚的发现,在整个程序编写,即使不知道类的结构,不导入包.类,也可以 进行类的实例化操作!3.操作构造方法但是如果使用反射实例化对象,必须要求类中存在无参构造方法,因为默认使用class类的newIn stance()方法,只能够找到无参!这个时候只能够取得类的构造方法,传递所需要的参数后,才可以执行!在Class类里面定义了可以 取得一个类之中构造方法的操作【看API】第一种:取得类之中的制定参数的构造方法public?Constructor?getC onstructor(类>...?parameterTypes)throwsNoSuchMethodException, SecurityException返回一个Constructor对象,该对象反映Constructor对象表示的类的指定的公共类函 数。parameterTypes参数是以声明顺序标识构造函数的形式参数类型的类对象的数组。如果此类对象表示在非静态上下文中声明 的内部类,则形式参数类型将显式包围实例作为第一个参数。反映的构造是这样表示的类的公共构造类对象,其形式参数类型匹配那些由指定的p arameterTypes。第二种:取得类之中的全部构造方法public?Constructor>[]?getConstru ctors()throwsSecurityException返回一个包含Constructor对象的数组,反映由此Constr uctor对象表示的类的所有公共类函数。如果类没有公共构造函数,或者类是数组类,或类反映原始类型或void时,返回长度为0的数组 。请注意,虽然此方法返回一个Constructor对象(这是Constructor构造函数数组)的数组,但此方法的返 回类型为Constructor>[],而不是Constructor[]。这种信息较少的返回类型是必要的,因为从该 方法返回后,可以修改数组以容纳不同类的Constructor对象,这将违反Constructor[]的类型Construct or[]。结果代表这个类的公共构造函数的Constructor对象的数组异常SecurityException -如果安全管理器,S存在,并且调用者的类加载器是不一样或类加载器的当前类和调用的祖先s.checkPackageAccess( )拒绝访问包这个类的。例如获取String类中的全部构造方法所以如果现在想要进行指定构造方法的使用,就必须将我们的关注点放在Co nstructor类中【看api的Constructor】,在此类中定义了一个实例化对象的方法正是因为通过构造方法实例化对象的规则 不同意,所以在进行简单java类操作的时候就明确给出了必须要给出无参构造方法调用类中方法取得了一个类的实例化对象之后,下面主要的任 务就是要调用类之中可以使用的操作方法,对于一个类之中的方法【两种】第一种是:取得父类继承而来的方法;方法【取得指定方法】getMe thod(String?name,类>...?parameterTypes)返回一个方法对象,它反映此表示的类或接口的指 定公共成员方法类对象。方法[]【取得全部方法】getMethods()返回包含一个数组方法对象反射由此表示的类或接口的所有 公共方法类对象,包括那些由类或接口和那些从超类和超接口继承的声明。第二种是取得本类定义的方法!方法【取得指定方法】getDecl aredMethod(String?name,类>...?parameterTypes)返回一个方法对象,它反映此表示的 类或接口的指定声明的方法类对象。方法[]【取得全部方法】getDeclaredMethods()返回包含一个数组方法对象反 射的类或接口的所有声明的方法,通过此表示类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。以上两种操作在定义 上区别不大,因为方法大部分都是public,所以两种方式取得的结果是没有区别的!案例:现在的程序是直接调用了Method类之中的t oString()方法实现的输出!如果用户有需要,用户可以自己整理方法输出,需要使用到Method类中的方法intgetModif iers()返回由此方法对象表示的方法的修饰符。注意:程序之中找的不是public,static等,而是关键字所代表的的数字,但 是在程序之中我们不许更换我们可以读懂的信息,可以借助于我们的Modifier类,此类中可以直接利用方法将数字变为修饰符字符串sta ticStringModifiertoString(int?mod)返回描述指定修饰符中的访问修饰符标志的字符串。Stri nggetName()返回由此方法对象表示的方法的名称,作为String。类>getReturnType()返回一个 类对象,它表示由该表示的方法的正式返回类型方法对象。TypeVariable<方法>[]getTypeParameters() 返回一个TypeVariable对象的数组,它们以声明顺序表示由此GenericDeclaration对象表示的通用声明声明的 类型变量。案例:publicclassReflectDemo{publicstaticvoidmain(Strin g[]args)throwsException{Classcls=Class.forName("cn.kgc.re flect.Student");Method[]methods=cls.getMethods();for(Method method:methods){//获取修饰符System.out.print(Modifier.toString(m ethod.getModifiers())+"");//取得返回值类型System.out.print(method.get ReturnType().getSimpleName()+"");//取得方法名称System.out.print(meth od.getName()+"(");//取得方法参数Class>[]parameterTypes=method.ge tParameterTypes();if(parameterTypes.length>0){//有参数for(inti= 0;iypes[i].getSimpleName()+"args-"+i);if(i){//还有参数输出,System.out.print(",");}}}System.out.print(")");/ /取得所有抛出的异常Class>[]exceptionTypes=method.getExceptionTypes() ;if(exceptionTypes.length>0){System.out.print("throws");for (inti=0;iceptionTypes[i].getSimpleName());if(iSystem.out.print(",");}}}System.out.println();}}}interface Message{publicvoidget();}classStudentimplementsMessage{pu blicStringfun(){return"";}publicvoidprint(inta){}@Overr idepublicvoidget(){}}注意:此类代码一般只会在编写开发工具的时候出现,而所谓的随笔提示功能就是依据以 上代码实现的!但是与开发者密切最紧密的一定是调用类中的方法,而且在method类中提供一个重要的方法public?Object?i nvoke(Object?obj,Object...?args)throwsIllegalAccessException, IllegalArgumentException,InvocationTargetException在具有指定参数的方法对象上调 用此方法对象表示的基础方法。个别参数自动解包以匹配原始形式参数,原始参考参数和参考参数都需要进行方法调用转换。如果底层方法是静 态的,则指定的obj参数将被忽略。它可能为null。如果底层方法所需的形式参数的数量为0,则提供的args数组的长度为0或为空 。如果底层方法是一个实例方法,它将使用动态方法查找来调用,如“Java语言规范”第二版,第15.12.4.4节所述;特别是将会 发生基于目标对象的运行时类型的覆盖。如果底层方法是静态的,则如果尚未初始化该方法,那么声明该方法的类将被初始化。如果方法正常完 成,则返回的值将返回给调用者;如果值具有原始类型,则首先将其适当地包装在对象中。但是,如果该值具有基本类型的数组的类型,则该数 组的元素不会包含在对象中;换句话说,返回一个原始类型的数组。如果底层方法返回类型为void,则调用返回null。1.调用指定方 法通过反射,调用类中的方法,通过Method类完成。步骤:1.通过Class类的getMethod(Stringname,Cla ss…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。2.之后使用Objecti nvoke(Objectobj,Object[]args)进行调用,并向方法中传递要设置的obj对象的参数信息。Object invoke(Objectobj,Object…args)说明:1.Object对应原方法的返回值,若原方法无返回值, 此时返回null2.若原方法若为静态方法,此时形参Objectobj可为null3.若原方法形参列表为空,则Object[]a rgs为null4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessibl e(true)方法,将可访问private的方法。案例:反射调用类中的方法正是因为如此,所以我才会在开发中强调有getter/se tter方法必须严格按照要求书写!调用类中的属性关于类之中的属性也可以直接利用反射进行操作,而支持的方法有两类取得所有继承而来的 属性FieldgetField(String?name)取得指定属性返回一个Field对象,它反映此表示的类或接口的指定 公共成员字段类对象。Field[]getFields()取得全部属性返回包含一个数组Field对象反射由此表示的类或接口的 所有可访问的公共字段类对象。取得本类定义的属性FieldgetDeclaredField(String?name)取得指定 属性返回一个Field对象,它反映此表示的类或接口的指定已声明字段类对象。Field[]getDeclaredFields( )取得全部属性返回的数组Field对象反映此表示的类或接口声明的所有字段类对象在field类里面还定义有进行属性调用的方法设 置属性内容:voidset(Object?obj,Object?value)将指定对象参数上的此Field对象表示的字段设置 为指定的新值获取属性内容:Objectget(Object?obj)返回该所表示的字段的值Field,指定的对象上。范例:取 得一个类之中的全部属性在ContructorMethodField三个类上有一个共同的父类AccessibleObject,在 这个类里面定义了可以取消封装的操作staticvoidsetAccessible(AccessibleObject[]?arra y,boolean?flag)方便的方法来设置accessible标志的一系列对象的安全检查(为了效率)。publicc lassDemo{publicstaticvoidmain(String[]args)throwsException{Class>aClass=Class.forName("cn.kgc.reflect.Person");Objecto=aClass.newInstance();FielddeclaredFieldName=aClass.getDeclaredField("name");declaredFieldName.setAccessible(true);declaredFieldName.set(o,"jillion");System.out.println(declaredFieldName.get(o));}}classPerson{privateStringname;}在开发之中,只需要灵活使用Class,Constructor,Method,Field就可以使用反射进行一系列的反射操作了!下一次厦门北大青鸟老师教你Web对于反射的支持,记得关注拿干货!华大职业教育 |
|