Java反射机制 蔚落 2022-05-31 09:05 217阅读 0赞 百科: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。 JAVA反射(放射)机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为[动态语言][Link 1]”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C\#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。 我的理解: 说到这个机制先要理解一下加载。加载可以分为动态加载和静态加载,所谓静态加载就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应[动态链接库][Link 2](.so或.lib)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。也就是在还没有生成class文件之前就能发现程序中的错误,例如ide编译器中还没点build之前就下面有波浪线报错提示,像打错了一些参数或者格式不对。 动态加载就是在运行时的加载,那么静态加载和动态加载有什么不同呢?举个例子,我们在编程序的时候是不是一定要全部调对程序才可以生成字节码文件再运行呢。比如你写了两个语句,第一个没错,但是第二个有问题,静态加载的时候你就过不了,生成不了字节码文件,也就无法执行第一个语句。那么动态加载就相当于绕过了这个过程,直接生成字节码文件,然后再运行出错时停止,也就是会执行第一个语句,运行到第二个语句时才会抛异常,这是不是就方便调试了呢。 还有一个很重要的用途,就是当一个项目很大的时候,不可能把全部程序写好再生成字节码文件再运行吧,效率太低,而且每个人都有分工,如果只是静态加载就相当于会发生“一锅老鼠屎毁了一锅粥”的感觉。这个时候就需要动态加载了,还有就是当对工程代码进行修改时,静态加载需要重新生成class文件才能运行,但是动态加载就没必要这么做,这就大大提高了效率。 那么完成动态加载的一个关键点就是Class类。 再百科一波: 众所周知Java有个Object 类,是所有Java 类的继承根源,其内声明了数个应该在所有Java 类中被改写的方法:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class 对象。 Class 类十分特殊。它和一般类一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。如果您想借由“修改Java标准库源码”来观察Class 对象的实际生成时机(例如在Class的constructor内添加一个println()),这样是行不通的!因为Class并没有public constructor。 Class是Reflection故事起源。针对任何您想探勘的类,唯有先为它产生一个Class 对象,接下来才能经由后者唤起为数十多个的Reflection APIs。 用中国话说: 就是在面向对象的java语言中,万物都是对象,接口,函数,甚至是基本数据类型,void其实都是叫Class类的实例。 那么如何获得这个对象呢: 有3种方法:这里拿Object obj作为示范。 1.Class c1=Object.class;//也就是类名+.class来获得,每个类都有一个隐含的静态成员 2.Class c2=obj.getClass();//实例对象的getClass()函数获得 3.Class c3=null; c3=Class.forName(java.lang.object);//通过Class类的静态函数获得,注意带上包名 通过class获得对象的方法:Object obj=(Object)c1.newInstance();//需要有无参数的构造方法 通过这个Class类我们不仅可以完成动态编译,也可以完成对于原码各种信息的获取。 笔记: 1、静态加载类,是编译时刻加载;动态加载类,是运行时刻加载 2、new创建对象:是静态加载类,在编译时刻就需要加载所有的【可能使用到的类】。有一个类有问题(如不存在),都不能通过编译,会报错。 3、Class.forName()通过动态加载类,可以用到一个类时,才进行加载。 【功能性的类尽量使用动态加载,并对新添的类实现功能性接口(标准),这样就不用重新编译】 java.lang.reflect.Field封装关于成员变量的操作 java.lang.reflect.Method封装关于方法的操作 java.lang.Constructor封装构造函数信息 获取方法信息: 一、基本的数据类型,void关键字等都存在类类型 Class c = 基类.class (int,String,double,void等) 二、Class类的基本API操作的 1、c.getName()//可以获取类的名称 2、c.getSimpleName();//不包含包名的类的名称 3、c.getMethods()//方法获取是该类的所有public方法,包括从父类继承的方法; ***注意【所有方法都是Method类的对象】 4、c.getDeclaredMethods()//获取的是该类自行声明的所有方法,不论访问权限 三、Method类提供了一些操作方法的方法 1、method.getReturnType()//得到该方法的返回值类型的类类型(class),如int.class String.class 2、method.getName()//得到方法的名称 3、method.getParameterTypes()//获得参数列表类型的类类型,如参数为(int,int)则得到(int.class ,int class) eg. Class c1 = int.class;// int的类类型Class c2 = String.class;// String类的类类型 String类字节码Class c3 = double.class; //double这个数据类类型的字节码表示方式Class c4 = Double.class; //Double这个类的类类型字节码表示方式Class c5 = void.class; //表达了void这个类的类类型getName为这个类的类类型的具体名称 c1.getName ---> intc2.getName ---> java.lang.String 类的全称c2.getSimpleName ---> String 不包含包名的类的名称 获取成员变量&构造函数: 一、成员变量是java.lang.reflect.Field的对象 1、Field类封装了关于成员变量的操作 2、Field[] fs = c.getFields()方法获取所有public的成员变量Field[]信息 3、c.getDeclaredFields获取的是该类自己声明的成员变量信息 4、field.getType()获得成员类型的类类型 5、field.getName()获得成员的名称 二、构造函数是java.lang.Constructor类的对象 1、通过Class.getConstructor()获得Constructor[]所有公有构造方法信息 2、建议getDeclaredConstructors()获取自己声明的构造方法 3、Constructor.getName():String 4、Constructor.getParameterTypes():Class[] 成员变量也是对象,是java.lang.reflect.Field的对象; 如果想动态加载一个方法,可以先获得类类型,也就是Class,然后再通过getMethod函数,第一个参数是方法名,后面的都是方法所需的参数的类类型,如果没有也可不写,然后通过.invoke(操作对象,方法参数..)可以调用函数,效果和直接使用函数是一样的。 方法的反射: 例子: public Class A{ public void print(){}; public void Print(Sting a,String b){} public void Print(int a,int b){}; } public Class B{ public static void main(String[] args){ A a1 = new A(); Class c= a1.getclass; Method getMet=c.getMethod("print",String.class,String.class);//忘了加引号 Object obj=getMet.invoke(a1,"df","df"); } } 1.获取A类中的print(int,int)方法: ①要获取一个方法就是获取类的信息,获取类的信息首先要获取类的类类型 A a1=new A(); Class c= a1.getClass(); ②获取方法 由名称和参数列表来决定,getMethod获取的是public方法,getDelcaredMethod获取自己声明的方法 Method m =c.getMethod(methodName,paramtypes);//paramtypes可以用数组的形式 表示new Class[]{int.class,int.class},也可以直接列举类类型 2.方法的反射操作:是用m对象来进行方法调用,和a1.print(10,20)调用的方法相同 m.invoke(a1,new Object[]{10,20}) Object o=m.invoke(对象名,参数);//方法如果没有返回值返回null,如果有返回值返回具体值,参数可用数组的方式表示,也可以直接列举,没有参数就不写 通过反射了解集合泛型的本质: 1:反射的操作都是编译之后的操作;就是运行阶段 2:java中集合的泛型是防止错误输入的;只在编译阶段有效,只要绕过编译就无效啦 我们可以通过方法的反射来操作,绕过编译 eg: ArrayList list1=new ArrayList(); ArrayList<String> list2=new ArrayList<String>(); Class c1=list1.getClass(); Class c2=list2.getClass(); System.out.print(c1==c2);//true Method m=c2.getMethod("add",Object.class); m.invoke(list2,20);//向list2集合中添加一个int 型的值;绕过编译 //当然是不能直接foreach list2集合的,会报类型转换错误 [Link 1]: https://baike.baidu.com/item/%E5%8A%A8%E6%80%81%E8%AF%AD%E8%A8%80 [Link 2]: https://baike.baidu.com/item/%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E5%BA%93/100352
还没有评论,来说两句吧...