当前位置: 首页 > 科技观察

浅谈Java反射技术

时间:2023-03-17 15:27:40 科技观察

本文转载自微信公众号《Java极客技术》,作者鸭血范唐。转载本文请联系Java极客技术公众号。1、什么是反射?反射(Reflection)是Java的特性之一,它允许一个运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性。Oracle对反射的官方解释是:反射使Java代码能够发现有关加载类的字段、方法和构造函数的信息,并在安全限制范围内使用反射的字段、方法和构造函数对其底层对应项进行操作。API适合需要访问目标对象的公共成员(基于其运行时类)或给定类声明的成员的应用程序。它还允许程序抑制默认反射访问控制。总之,通过反射,我们可以在运行时获取程序或程序集中每个类型的成员和成员信息。程序中一般对象的类型是在编译时确定的,但Java的反射机制可以动态创建对象并调用其属性,而这类对象的类型在编译时是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译时是未知的。二、反射的主要用途很多人认为反射在实际的Java开发应用中应用并不广泛,其实不然。当我们使用IDE(如Eclipse、IDEA)时,当我们进入一个对象或类,想要调用它的属性或方法时,按下那个点,编译器会自动列出它的属性或方法,这里会用到Reflection。反射最重要的用途是开发各种通用框架。很多框架(比如Spring)都是可配置的(比如通过XML文件配置bean)。为了保证框架的通用性,他们可能需要根据配置文件加载不同的对象或类,调用不同的方法。这时候就必须要用到反射,需要加载的对象是在运行时动态加载的。举个例子,在使用Struts2框架的开发中,我们一般会在struts.xml中配置Action,如:/shop/shop-index.jsplogin.jsp配置文件与Action建立映射关系,当View层发送请求时,request会被StrutsPrepareAndExecuteFilter拦截,然后StrutsPrepareAndExecuteFilter会动态创建一个Action实例。比如我们请求login.action,那么StrutsPrepareAndExecuteFilter会解析struts.xml文件,在action中获取名为login的Action,并根据class属性创建一个SimpleLoginAction实例,使用invoke方法调用执行方法。这个过程离不开反思。对于框架开发人员来说,反射虽小但功能强大,它是各种容器实现的核心。对于普通开发者来说,如果不深入框架的开发,会用到较少的反射,但是了解框架的底层机制,有助于丰富自己的编程思路,也是大有裨益的。3.反射的基本应用3.1.通过反射获取类对象通过反射获取对象的方式有3种。);returnforName0(className,true,ClassLoader.getClassLoader(caller),caller);}例如JDBC开发中常用此方法加载数据库驱动Class.forName("包名.类名");3.1.2、类名.class的获取例如:ClassintClass=int.class;类integerClass=Integer.TYPE;#RelfectEntity类是本文的一个例子ClassrelfectEntity2=RelfectEntity.class;3.1.3、对象getClass()获取StringBuilderstr=newStringBuilder("123");ClassstrClass=str.getClass();这三种方法都可以用来获取类对象。在框架开发中,一般第一种方法用得比较多。3.2.获取类的构造函数信息获取类构造函数的用法主要是通过Class类的getConstructor方法获取Constructor类的实例,而Constructor类有一个newInstance方法来创建对象实例:publicTnewInstance(Object...initargs)3.3。通过反射获取类的实例生成对象主要有两种方式。使用Class对象的newInstance()方法创建Class对象对应的类的实例。Classc=String.class;Objectstr=c.newInstance();首先通过Class对象获取指定的Constructor对象,然后调用Constructor对象的newInstance()方法创建实例。//获取String类对应的Class对象c=String.class;//获取带String参数的String类的构造函数Constructorconstructor=c.getConstructor(String.class);//创建实例Objectobj根据构造函数=constructor.newInstance("23333");System.out.println(obj);该方法可以使用指定的构造函数来构造类的实例。3.4.获取类的变量实体类:/***baseclass*/publicclassBaseClass{publicStringpublicBaseVar1;publicStringpublicBaseVar2;}/***subclass*/publicclassChildClassextendsBaseClass{publicStringpublicOneVar1;publicStringpublicOneVar2;privateStringprivateOneVar1;privateStringprivateOneVar2;publicStringprivateOneVar2;publicStringprintOneVar2}retMsgTest(Test:publicclass{publicstaticvoidmain(String[]args){//1.获取并输出类名ClassmClass=ChildClass.class;System.out.println("Classname:"+mClass.getName());System.out.println("--获取所有具有公共访问权限的变量(包括本类声明的和从父类继承的变量)----");//2.获取所有具有公共访问权限的变量(包括本类声明的和继承的)fromparentclass)Field[]fields=mClass.getFields();//遍历变量并输出变量信息for(Fieldfield:fields){//获取访问权限并输出intmodifiers=field.getModifiers();System.out.print(Modifier.toString(modifiers)+"");//输出变量类型和变量名System.out.println(field.getType().getName()+""+field.getName());}System.out.println("----获取本类声明的所有变量----");//3.获取该类中声明的所有变量Field[]allFields=mClass.getDeclaredFields();for(Fieldfield:allFieldelds){//获取访问并输出intmodifiers=field.getModifiers();System.out.print(Modifier.toString(modifiers)+"");//输出变量类型和变量名System.out.println(field.getType().getName()+""+field.getName());}}}输出结果:类名:com.example.java.reflect.ChildClass----获取所有公共访问变量(包括那些由本类声明并继承自父类的)----publicjava.lang.StringpublicOneVar1publicjava.lang.StringpublicOneVar2publicjava.lang.StringpublicBaseVar1publicjava.lang.StringpublicBaseVar2----获取本类声明的所有变量----publicjava。lang.StringpublicOneVar1publicjava.lang.StringpublicOneVar2privatejava.lang.StringprivateOneVar1privatejava.lang.StringprivateOneVar23.5、修改类的变量修改子类/***子类*/publicclassChildClassextendsBaseClass{publicStringpublicOneVar1;publicStringpublicOneVar2;privateStringprivateOneVar1;privateStringprivateOneVar2;publicStringprintOneMsg(){returnprivateOneVar1;}}测试:publicclassVarModfiyTest{publicstaticvoidmain(String[]args)throwsException{//1。获取并输出类名ClassmClass=ChildClass.class;System.out.println("类名:"+mClass.getName());System.out.println("----获取ChildClass类中的privateOneVar1私有变量----");//2.获取ChildClass类中的privateOneVar1私有变量FieldprivateField=mClass.getDeclaredField("privateOneVar1");//3.操作私有变量if(privateField!=null){//获取私有变量的访问权限privateField.setAccessible(true);//实例化对象ChildClassobj=(ChildClass)mClass.newInstance();//修改私有变量并输出测试变量的值//privateField是获取到的私有变量//obj要操作的对象//"helloworld"是要修改的值privateField.set(obj,"helloworld");System.out.println("privateOneVar1variable,modifiedValue:"+obj.printOneMsg());}}}输出结果:类名:com.example.java.reflect.ChildClass----获取th中的privateOneVar1私有变量eChildClass类----privateOneVar1变量,修改前一个值:nullprivateOneVar1变量,修改值:helloworld3.6,获取类的所有方法修改实体类/***基类*/publicclassBaseClass{publicStringpublicBaseVar1;publicStringpublicBaseVar2;privatevoidprivatePrintBaseMsg(Stringvar){System.out.println("基类-私有方法、变量:"+var);}publicvoidpublicPrintBaseMsg(Stringvar){System.out.println("基类-公共方法,变量:"+var);}}/***子类*/publicclassChildClassextendsBaseClass{publicStringpublicOneVar1;publicStringpublicOneVar2;privateStringprivateOneVar1;privateStringprivateOneVar2;publicStringprintOneMsg(){returnprivateOneVar1;}privatevoidprivatePrintOneMsg(Stringvar){System.out.println("子类-私有方法,变量:"+var);}publicvoidpublicPrintOneMsg(Stringvar){System.out.println("子类-Publicmethod,variable:"+var);}}Test:publicclassMethodTest{publicstaticvoidmain(String[]args){//1.获取并输出类名ClassmClass=ChildClass.class;System.out.println("Thenameoftheclass:"+mClass.getName());System.out.println("----获取所有公共访问权限的方法,包括自声明和从父类继承---");//2获取所有publicMethods的访问权限,包括自声明和继承自父类Method[]mMethods=mClass.getMethods();for(Methodmethod:mMethods){//获取和输出方法访问权限(Modifiers:修饰符)intmodifiers=method.getModifiers();System.out.print(Modifier.toString(modifiers)+"");//获取并输出方法的返回值类型ClassreturnType=method.getReturnType();System.out.print(returnType.getName()+""+method.getName()+"(");//获取并输出方法的所有参数Parameter[]parameters=method.getParameters();for(Parameterparameter:parameters){System.out.print(parameter.getType().getName()+""+parameter.getName()+","));}//获取并输出方法throw异常类[]exceptionTypes=method.getExceptionTypes();if(exceptionTypes.length==0){System.out.println(")");}else{for(Classc:exceptionTypes){System.out.println(")throws"+c.getName());}}}System.out.println("----获取该类的所有方法---");//3.获取本类的所有方法Method[]allMethods=mClass.getDeclaredMethods();for(Methodmethod:allMethods){//获取并输出方法访问权限(Modifiers:修饰符)intmodifiers=method.getModifiers();System.out.print(Modifier.toString(modifiers)+"");//获取并输出方法的返回值类型ClassreturnType=method.getReturnType();System.out.print(returnType.getName()+""+method.getName()+"(");//获取并输出方法的所有参数Parameter[]parameters=method.getParameters();for(Parameterparameter:parameters){System.out.print(parameter.getType().getName()+""+parameter.getName()+",",");}//获取和输出方法抛异常Class[]exceptionTypes=method.getExceptionTypes();if(exceptionTypes.length==0){System.out.println(")");}else{for(Classc:exceptionTypes){System.out.println(")throws"+c.getName());}}}}}输出:类名:com.example.java.reflect.ChildClass----获取所有公共访问权限的方法,包括自我声明和从父级---publicjava.lang.StringprintOneMsg()publicvoidpublicPrintOneMsg(java.lang.Stringarg0,)publicvoidpublicPrintBaseMsg(java.lang.Stringarg0,)publicfinalvoidwait(longarg0,intarg1,)throwsjava.lang.InterruptedExceptionpublicfinalnativevoidjava0,longargrow.InterruptedExceptionpublicfinalvoidwait()抛出java.lang。中断的异常publicbooleanequals(java.lang.Objectarg0,)publicjava.郎。StringtoString()publicnativeinthashCode()publicfinalnativejava.inalnativevoidnotifyAll()----获取这个类的所有方法---publicjava.lang.StringprintOneMsg()privatevoidprivatePrintOneMsg(java.lang.Stringarg0,)publicvoidpublicPrintOneMsg(java.lang.Stringarg0,)为什么输出那么多?因为所有的类都默认继承了object类,打开object类就会发现一些public方法,所以一起打印出来!3.7、调用方法publicclassMethodInvokeTest{publicstaticvoidmain(String[]args)throwsException{//1。获取并输出类名ClassmClass=ChildClass.class;System.out.println("类名:"+mClass.getName());System.out.println("----获取ChildClass类的私有方法privatePrintOneMsg---");//2.获取对应的私有方法//第一个参数是要获取的私有方法名//第二个是要获取方法的参数类型,参数是Class...,如果没有参数,就是null//方法参数也可以这样写:newClass[]{String.class}MethodprivateMethod=mClass.getDeclaredMethod("privatePrintOneMsg",String.class);//3.启动运行方法if(privateMethod!=null){//获取私有方法的访问权限//只是获取访问权限,不修改实际权限privateMethod.setAccessible(true);//实例化对象ChildClassobj=(ChildClass)mClass.newInstance();//使用invoke反射调用私有方法//obj要操作的对象//后一个参数传递实参privateMethod.invoke(obj,"helloworld");}}}输出结果:类名:com.example.java.reflect.ChildClass----获取ChildClass类的私有方法privatePrintOneMsg---子类-私有方法,变量:helloworld4.总结因为反射会额外消耗系统资源,如果不需要创建对象动态的,那就没必要用反射了。另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装,造成安全问题。5、参考文章sczyh30:深入剖析Java反射Bert:Java反射由浅入深