【JAVA基础】反射机制 比眉伴天荒 2022-06-03 09:05 148阅读 0赞 Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制 ### 一、什么是java反射 ### 反射使程序代码能够接入装载到JVM中的类的内部信息,允许在编写与执行时,而不是源代码中选定的类协作的代码,是以开发效率换运行效率的一种手段。这使反射称为构建灵活应用的主要工具。 反射的作用: 1、调用一些私有方法,实现黑科技。比如双卡短信发送、设置状态栏颜色、自动挂电话等。 2、实现序列化与反序列化,比如PO的ORM,Json解析等 3、实现跨平台兼容,比如jdk中的SocketImpl的实现 4、通过xml或注解,实现依赖注入,注解处理,动态代理,单元测试等功能。比如Retrofit、Spring或者Dagger ### 二、class类 ### 1、class是一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Field,描述构造类的Constructor等属性 2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。 3、对于每个类而言。jre都为其保留一个不变的class类型的对象。一个class对象包含了特定某个类的有关信息 4、class对象只能由系统建立对象 5、一个类在jvm中只会有一个class实例 ##### **反射机制获取类的三种方法** ##### 1、通过类名 2、通过getClass() 3、通过全类名获取 /** * 反射机制获取类有三种方法 */ @Test public void testGetClass() throws ClassNotFoundException { Class clazz = null; //1 直接通过类名.Class的方式得到 clazz = Person.class; System.out.println("通过类名: " + clazz); //2 通过对象的getClass()方法获取,这个使用的少(一般是传的是Object,不知道是什么类型的时候才用) Object obj = new Person(); clazz = obj.getClass(); System.out.println("通过getClass(): " + clazz); //3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常 clazz = Class.forName("com.java.reflection.Person"); System.out.println("通过全类名获取: " + clazz); } ##### **利用newInstance创建对象:调用的类必须有无参的构造器** ##### 无参构造器:Person\{name=’null’,age=0\} /** * Class类的newInstance()方法,创建类的一个对象。 */ @Test public void testNewInstance() throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class clazz = Class.forName("com.java.reflection.Person"); //使用Class类的newInstance()方法创建类的一个对象 //实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的) //一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器 Object obj = clazz.newInstance(); System.out.println(obj); } #### 三、ClassLoader类加载器 #### 类加载器是用来把类装载进jvm的。jvm规范定义了两种类型的类装载器:启动类家载器和用户自定义加载器。jvm在运行时会产生3个类加载器组成的初始化加载器层次结构,如下图所示: ![类加载器][SouthEast] 类加载器详解:[http://blog.csdn.net/ochangwen/article/details/51473120][http_blog.csdn.net_ochangwen_article_details_51473120] /** * ClassLoader类装载器 */ @Test public void testClassLoader1() throws ClassNotFoundException, IOException { //1、获取一个系统的类加载器 ClassLoader classLoader = ClassLoader.getSystemClassLoader(); System.out.println("系统的类加载器-->" + classLoader); //2、获取系统类加载器的父类加载器(扩展类加载器(extensions classLoader)) classLoader = classLoader.getParent(); System.out.println("扩展类加载器-->" + classLoader); //3、获取扩展类加载器的父类加载器 //输出为Null,无法被Java程序直接引用 classLoader = classLoader.getParent(); System.out.println("启动类加载器-->" + classLoader); // //4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器 classLoader = Class.forName("com.java.reflection.Person").getClassLoader(); System.out.println("当前类由哪个类加载器进行加载-->"+classLoader); //5、测试JDK提供的Object类由哪个类加载器负责加载的 classLoader = Class.forName("java.lang.Object").getClassLoader(); System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader); } #### 四、反射在native的实现 #### 反射在java中可以直接调用,不过最终调用的认识native方法,以下为主流反射操作的实现 **1、Class.forName** Class.forName可以通过寻找Class对象,比如Class.forName(“java.lang.String”)。 在JDK的源码实现中,可以发现最终调用的是native方法forName0(),它在JVM中调用的实际是findClassFromClassLoader(),原理与ClassLoader的流程一样。 **2、getDeclareFields的实现** 在jdk源码中,可以知道class.getDeclaredFields()方法实际调用的是native方法getDeclaredFields0(),它在JVM主要实现步骤如下: (1)根据class结构体信息,获取field\_count与fields\[\]字段,这个字段早已在load过程中被放入了 (2)根据field\_count的大小分配内存、创建数组 (3)将数组进行forEach循环,通过fields\[\]中的信息依次创建Object对象 (4)返回数组指针 **3、Method.invoke的实现** 以下为无同步、无异常的情况下调用的步骤: (1)创建Frame (2)如果对象flag为native,交给native\_handler进行处理 (3)在frame中执行java代码 (4)弹出Frame (5)返回执行结果的指针 **4、class.newInstance的实现** (1)检测权限、预分配空间大小等参数 (2)创建Object对象,并分配空间 (3)通过Method.invoke调用构造函数(()) (4)返回Object指针 [SouthEast]: /images/20220603/70a7846b7ced4d89b03980695311b127.png [http_blog.csdn.net_ochangwen_article_details_51473120]: http://blog.csdn.net/ochangwen/article/details/51473120
还没有评论,来说两句吧...