反射 比眉伴天荒 2022-04-14 04:14 201阅读 0赞 **知识点:** 1. 类对象概念 2. Class类的使用:创建类对象 3. 动态加载类 4. 获取方法信息 5. 获取成员变量、构造函数信息 6. 方法反射的基本操作 7. 通过反射了解集合泛型的本质 8. 关于Java 类加载器内容的详解 9. 应用 10. new 和 newInstance 的区别 ### 一.类对象概念 ### 在理解类对象之前,先说我们熟悉的对象之间的区别:garen和teemo都是Hero对象,他们的区别在于,各自有不同的名称,血量,伤害值。 然后说说类之间的区别:Hero和Item都是类,他们的区别在于有不同的方法,不同的属性。 类对象,就是用于描述这种类,都有什么属性,什么方法的 ### 二.Class类的使用 ### 1.创建对象:有三种方法表示 任何一个类都是Class类的实例对象,这个实例对象有 三种表示方式 :(我们新建一个Student类) 1.Class c1 = Student.class;//实际告诉我们任何一个类都有一个隐含的静态成员变量class(知道类名时用) 2.Class c2 = stu.getClass();//已知该类的对象通过getClass方法(知道对象时用) 3.Class c3 = Class.forName("类的全名");//会有一个ClassNotFoundException异常 System.out.println(c1==c2);//true System.out.println(c2==c3);//true 准确的讲是一个ClassLoader下,一种类,只会有一个类对象存在。 官网解释说:c1,c2表示了Student类的类类型(class type),万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们成为该类的类对象 。 这里有一点值得注意 ,当我们执行System.out.println(c1==c2);语句,结果返回的是 true ,这是为什么呢? 原因是不管c1还是c2都代表了Student类的类类型,一个类可能是Class类的一个实例对象。 我们完全可以通过类的类类型创建该类的对象实例,即通过c1或c2创建Student的实例。 Student stu = (Student)c1.newInstance(); //前提是必须要有无参的构造方法,因为该语句会去调用其无参构造方法。该语句会抛出异常。 ### 三.动态加载 ### 1. 编译时加载类是静态加载类 new 创建对象是静态加载类,在编译时刻就需要加载所有可用使用到的类,如果有一个用不了,那么整个文件都无法通过编译 2. 运行时加载类是动态加载类 Class c = Class.forName("类的全名"),不仅表示了类的类型,还表示了动态加载类,编译不会报错,在运行时才会加载,使用接口标准能更方便动态加载类的实现。功能性的类尽量使用动态加载,而不用静态加载。 很多软件比如QQ,360的在线升级,并不需要重新编译文件,只是动态的加载新的 ## ## ### 四.获取方法信息 ### **1、基本的数据类型,void关键字都存在类类型** Class c1 =int.class;//int的类类型 Class c2 =String.class;//String类的类类型,可以理解为编译生成的那个String.class字节码文件,当然,这并不是官方的说法 Class c3 =double.class; Class c4 =Double.class; Class c5 =void.class; **2**.**Class类的基本API操作**** ** 要获取类的信息,首先要获取类的类类型 1.获取类的名称 2..获取类的方法 Method类,方法的对象 一个成员方法就是一个Method对象 getMethods()方法获取的是所有的public的函数,包括父类继承而来的 getDeclaredMethods()获取的是多有该类自己声明的方法,不问访问权限 2.1得到方法的名称 2.2获取类方法的返回值类型的类类型 2.3获取的参数类型--->得到的是参数列表的类型的类类型 实例: Class c = obj.getClass();//传递的是哪个子类的对象,c就是该子类的类类型 //1.获取类的名称 System.out.println("类的名称是:"+c.getName()); //2.获取类方法 Method[] ms = c.getMethods();//c.getDeclaredMethods(); //2.1 得到方法的名称 System.out.print(ms[i].getName()+"("); //2.2 得到方法的返回值类型的类类型 for(int i =0; i < ms.length; i++){ Class retrunType = ms[i].getReturnType(); System.out.print(retrunType.getName()+" "); }; //2.3 获取的参数类型--->得到的是参数列表的类型的类类型 Class[] paraTypes = ms[i].getParameterTypes(); for(Class class1 : paraTypes){ System.out.print(class1.getName()+","); }; **Class的API中还有很多其他的方法,可以得到interface、Package、Annotation等很多信息,具体使用请参考帮助手册,本文就不在详细讲解。特别注意的一点是,如果你想得到一个类的信息,首先就要获取该类的类类型。** ### 五.获取成员变量、构造函数信息 ### **getField和getDeclaredField的区别:** 1. 这两个方法都是用于获取字段 2. getField 只能获取public的,包括从父类继承来的字段。 3. getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true)) /** * 成员变量也是对象,是java.lang.reflect.Field这个类的的对象 * Field类封装了关于成员变量的操作 * getFields()方法获取的是所有public的成员变量的信息 * getDeclareFields()方法获取的是该类自己声明的成员变量的信息 */ //1.获取所有属性 Field[] fs = c.getDeclaredFields(); for(Field field : fs){ //得到成员变量的类型的类类型 Class fieldType = field.getType(); String typeName = fieldType.getName(); //得到成员变量的名称 String fieldName = field.getName(); System.out.print(typeName+" "+fieldName); } //2.获取某个属性 Hero h1=new Hero(); h1.setName("德玛");//传统方法给属性赋值 Class c1=h1.getClass(); Field f1=c1.getDeclaredField("name");//会抛出异常,要进行异常处理 f1.set(h1,"石头人");//反射的方法给属性赋值 System.out.println(h1.name);//石头人 /** * 构造函数也是对象 * java.lang.Constructor中封装了构造函数的信息 * getConstructor()方法获取所有的public的构造函数 * getDeclaredConstructors得到所有的构造函数 */ Constructor[] cs = c.getDeclaredConstructors(); for(Constructor constructor : cs){ System.out.print(constructor.getName()+"("); //获取构造函数的参数列表---》得到的是参数雷彪的类类型 Class[] paramTypes = constructor.getParameterTypes(); for(Class class1 : paramTypes){ System.out.print(class1.getName()+","); } System.out.println(")"); } ### 六.方法反射的基本操作 ### 1. 如何获取某个方法 方法的名称和方法的参数列表才能唯一决定某个方法 Method m = c.getDeclaredMethod("方法名",可变参数列表(参数类型.class)) 2. 方法的反射操作 m.invoke(对象,参数列表) 方法如果没有返回值,返回null,如果有返回值返回Object类型,然后再强制类型转换为原函数的返回值类型 ### 七. 通过反射了解集合泛型的本质 ### ArrayList list1 =newArrayList(); ArrayList<String> list2 =newArrayList<String>(); Class c1 = list1.getClass(); Class c2 = list2.getClass(); System.out.println(c1==c2);//结果为true,为什么?? 结果分析:因为反射的操作都是编译之后的操作,也就是运行时的操作,c1==c2返回true,说明编译之后集合的泛型是去泛型化的。 那么我们就可以理解为,Java集合中的泛型,是用于防止错误类型元素输入的,比如在list2中我们add一个int,add(10)就会编译报错,那么这个泛型就可以只在编译阶段有效,通过了编译阶段,泛型就不存在了。可以验证,我们绕过编译,用反射动态的在list2中add一个int是可以成功的,只是这时因为list2中存储了多个不同类型的数据(String型,和int型),就不能用for-each来遍历了,会抛出类型转换错误异常ClassCastException ### 八.关于Java 类加载器内容的详解 ### 1. 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化 1. 加载:就是指将class文件读入内存,并为之创建一个Class对象,任何类被使用时系统都会建立一个Class对象 2. 连接: 验证:确保被加载类的正确性 准备:负责为类的静态成员分配内存,并设置默认初始化值 解析:将类中的符号引用替换为直接引用 3. 初始化: 局部变量保存在栈区:必须手动初始化 new 的对象保存在堆区:虚拟机会进行默认初始化,基本数据类型初始化值为0,引用类型初始化值为null 2. 类加载的时机(只加载一次) 以下时机仅表示第一次的时候 1. 创建类的实例的时候 2. 访问类的静态变量的时候 3. 调用类的静态方法的时候 4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象 5. 初始化某个类的子类的时候 6. 直接使用java.exe命令来运行某个主类 3. 类加载器 负责将.class文件加载到内存中,并为之生成对应的Class对象 虽然我们在开发过程中不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行 4. 类加载器的组成: 1. Bootstrap ClassLoader 根类加载器 也被称为引导类加载器,负责Java核心类的加载,比如System类,在JDK中JRE的lib目录下rt.jar文件中的类 2. Extension ClassLoader 扩展类加载器 负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录 3. System ClassLoader 系统类加载器 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径,主要是我们开发者自己写的类 更多内容请参考《深入理解JVM虚拟机》 ### 九.应用 ### Spring框架的核心就是使用Java反射实现的,而且对做一些Java底层的操作会很有帮助。 可参考:[http://how2j.cn/k/reflection/reflection-reflection/107.html\#nowhere][http_how2j.cn_k_reflection_reflection-reflection_107.html_nowhere] ### 十.new 和 newInstance 的区别 ### **初始化一个类,生成一个实例的时候;newInstance() 和 new 有什么区别?** 用newInstance与用new是区别的,区别在于创建对象的方式不一样,前者是使用类加载机制,那么为什么会有两种创建对象方式?这个就要从可伸缩、可扩展,可重用等软件思想上解释了。 [JAVA][]中工厂模式经常使用newInstance来创建对象,因此从为什么要使用工厂模式上也可以找到具体答案。 例如: Class c = Class.forName(“A”);factory = (AInterface)c.newInstance(); 其中AInterface是A的接口,如果下面这样写,你可能会理解: String className = “A”;Class c = Class.forName(className);factory = (AInterface)c.newInstance(); **进一步,如果下面写,你可能会理解:** String className = readfromXMlConfig;//从xml 配置文件中获得字符串Class c = Class.forName(className);factory = (AInterface)c.newInstance(); 上面代码就消灭了A类名称,优点:无论A类怎么变化,上述代码不变,甚至可以更换A的兄弟类B , C , D….等,只要他们继承Ainterface就可以。 从jvm的角度看,我们使用new的时候,这个要new的类可以没有加载; 但是使用newInstance时候,就必须保证:1、这个类已经加载;2、这个类已经连接了。而完成上面两个步骤的正是class的静态方法forName()方法,这个静态方法调用了启动类加载器(就是加载javaAPI的那个加载器)。 有了上面jvm上的理解,那么我们可以这样说,newInstance实际上是把new这个方式分解为两步,即,首先调用class的加载方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了我们降耦的手段。 **\[补充:\]** newInstance: 弱类型。低效率。只能调用无参构造。 new: 强类型。相对高效。能调用任何public构造。 newInstance()是实现IOC、反射、面对接口编程 和 依赖倒置 等技术方法的必然选择,new 只能实现具体类的实例化,不适合于接口编程。 里面就是通过这个类的默认构造函数构建了一个对象,如果没有默认构造函数就抛出InstantiationException, 如果没有访问默认构造函数的权限就抛出IllegalAccessException **1、代码运用:** ![ä½ä¸ºJava<a href=http://www.solves.com.cn/it/cxkf/cxy/ target=\_blank class=infotextkey>ç¨åºå</a>ï¼ä½ ç¥é new å newInstance çåºå«åï¼][Java_a href_http_www.solves.com.cn_it_cxkf_cxy_ target_blank class_infotextkey_a_ new _ newInstance] ![ä½ä¸ºJavaç¨åºåï¼ä½ ç¥é new å newInstance çåºå«åï¼][Java_ new _ newInstance] **可以看出,new 和 newInstance 具有相同的作用。** [http_how2j.cn_k_reflection_reflection-reflection_107.html_nowhere]: http://how2j.cn/k/reflection/reflection-reflection/107.html#nowhere [JAVA]: http://www.solves.com.cn/it/cxkf/yy/JAVA/ [Java_a href_http_www.solves.com.cn_it_cxkf_cxy_ target_blank class_infotextkey_a_ new _ newInstance]: /images/20220414/7a1155b99c484e72bd362a6443b6c5eb.png [Java_ new _ newInstance]: /images/20220414/13ba844e6ec646c08b11d7b2fdeb645e.png
相关 Java反射-反射 API 转自:https://www.jianshu.com/p/e55770dd48d3 涉及这些类 ![watermark_type_ZmFuZ3poZW5naGVpdGk_s 深碍√TFBOYSˉ_/ 2023年01月17日 07:40/ 0 赞/ 196 阅读
相关 反射_反射概述 反射 JAVA反射机制是在运行状态中,对于任意一个 类,都能够知道这个类的所有属性和方法;对 于任意一个对象,都能够调用它的任意一个方 清疚/ 2022年06月17日 02:22/ 0 赞/ 247 阅读
相关 反射 知识点: 1. 类对象概念 2. Class类的使用:创建类对象 3. 动态加载类 4. 获取方法信息 5. 获取成员变量、构造函数信息 6. 方法反射的基本操作 比眉伴天荒/ 2022年04月14日 04:14/ 0 赞/ 202 阅读
相关 反射 // 1.定义一个标准的JavaBean,名叫Person,包含属性name、age。 // 使用反射的方式创建一个实例、调用构造函数初始化name、age,使用反射方式调 Dear 丶/ 2022年04月03日 08:16/ 0 赞/ 207 阅读
相关 反射 > JAVA反射机制是在运行状态中 > > 对于任意一个类,都能够知道这个类的所有属性和方法; > > 对于任意一个对象,都能够调用它的任意一个方法和属性; > > 这种 柔光的暖阳◎/ 2022年02月16日 13:24/ 0 赞/ 154 阅读
相关 反射 反射的概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态 喜欢ヅ旅行/ 2022年01月25日 19:23/ 0 赞/ 108 阅读
相关 反射 反射 类加载器的概述 当程序要使用某个类时,如果该类还未被加载到内存中, 则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。 加载 就是指将c 怼烎@/ 2022年01月17日 12:13/ 0 赞/ 232 阅读
相关 【反射】 JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法 比眉伴天荒/ 2021年11月09日 20:40/ 0 赞/ 492 阅读
相关 反射 反射机制简述 静态编译:在编译时确定类型,绑定对象,即通过。 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的 小咪咪/ 2021年09月27日 14:00/ 0 赞/ 376 阅读
相关 反射 RTTI -------------------- 运行时类型识别(RTTI, Run-Time Type Identification)是Java中的机制,在Jav 不念不忘少年蓝@/ 2021年09月23日 00:44/ 0 赞/ 309 阅读
还没有评论,来说两句吧...