Java基础 -- 反射机制 雨点打透心脏的1/2处 2023-06-27 03:52 56阅读 0赞 ## Java基础 – 反射机制 ## 前言概述 Java 反射机制是 Java 语言的一个重要特性,它在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法。此外,在 ORM 中间件的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。 Java 反射机制主要提供了以下功能,这些功能都位于 **java.lang.reflect** 包。 * 在运行时判断任意一个对象所属的类。 * 在运行时构造任意一个类的对象。 * 在运行时判断任意一个类所具有的成员变量和方法。 * 在运行时调用任意一个对象的方法。 生成动态代理。 什么是反射? 答:分析类的能力就叫反射 为什么不直接通过new操作,而要多此一举使用反射? 答:反射机制在当前可能看不到作用,但是在以后学习spring框架的时候,我们会发现spring框架是事先就写好的框架,他内部的处理并不知道用户要写哪些类,应为那是以后由用他的人来定的,这时候你还能在spring内部去new吗?我们拿到的可能只有一个class文件,所以我们必须得有一些未卜先知的能力,那么这个能力就是反射 ## 反射机制 ## 众所周知,所有 Java 类均继承了 Object 类,在 Object 类中定义了一个 getClass() 方法,该方法返回同一个类型为 Class 的对象。例如,下面的示例代码: Class Test= Test.getClass(); 常用的object反射方法。 ![Alt][] 如表所示,在调用 getFields() 和 getMethods() 方法时将会依次获取权限为 public 的字段和变量,然后将包含从超类中继承到的成员变量和方法。而通过 getDeclareFields() 和 getDeclareMethod() 只是获取在本类中定义的成员变量和方法。 为了能够动态获取对象构造方法的信息,首先需要通过下列方法之一创建一个 **Constructor** 类型的对象或者数组。 * getConstructors() * getConstructor(Class<?>…parameterTypes) * getDeclaredConstructors() * getDeclaredConstructor(Class<?>…parameterTypes) * 如果是访问指定的构造方法,需要根据该构造方法的入口参数的类型来访问。例如,访问一个入口参数类型依次为 int 和 String 类型的构造方法,下面的两种方式均可以实现。 objectClass.getDeclaredConstructor(int.class,String.class); objectClass.getDeclaredConstructor(new Class[]{ int.class,String.class}); 创建的每个 Constructor 对象表示一个构造方法,然后利用 Constructor 对象的方法操作构造方法。Constructor 类的常用方法如下表所示。 居中的图片: ![Alt][Alt 1] 通过 java.lang.reflect.Modifier 类可以解析出 getMocMers() 方法的返回值所表示的修饰符信息。在该类中提供了一系列用来解析的静态方法,既可以查看是否被指定的修饰符修饰,还可以字符串的形式获得所有修饰符。下表 列出了 Modifier 类的常用静态方法。 ![Alt][Alt 2] 例如,下列代码判断对象 con 所代表的构造方法是否被 public 修饰,以及以字符串形式获取该构造方法的所有修饰符。 int modifiers = con.getModifiers(); // 获取构造方法的修饰符整数 boolean isPublic = Modifier.isPublic(modifiers); // 判断修饰符整数是否为public string allModifiers = Modifier.toString(modifiers); -------------------- ## 案例 ## 1)首先创建一个 Book 类表示图书信息。在该类中声明一个 String 型变量表示图书名称,两个 int 型变量分别表示图书编号和价格,并提供 3 个构造方法。 Book 类的最终代码如下: public class Book { String name; // 图书名称 int id, price; // 图书编号和价格 // 空的构造方法 private Book() { } // 带两个参数的构造方法 protected Book(String _name, int _id) { this.name = _name; this.id = _id; } // 带可变参数的构造方法 public Book(String... strings) throws NumberFormatException { if (0 < strings.length) id = Integer.valueOf(strings[0]); if (1 < strings.length) price = Integer.valueOf(strings[1]); } // 输出图书信息 public void print() { System.out.println("name=" + name); System.out.println("id=" + id); System.out.println("price=" + price); } } 编写测试类 Test01,在该类的 main() 方法中通过反射访问 Book 类中的所有构造方法,并将该构造方法是否带可变类型参数、入口参数类型和可能拋出的异常类型信息输出到控制台。 Test01 类的代码如下: public class Test01 { public static void main(String[] args) { // 获取动态类Book Class book = Book.class; // 获取Book类的所有构造方法 Constructor[] declaredContructors = book.getDeclaredConstructors(); // 遍历所有构造方法 for (int i = 0; i < declaredContructors.length; i++) { Constructor con = declaredContructors[i]; // 判断构造方法的参数是否可变 System.out.println("查看是否允许带可变数量的参数:" + con.isVarArgs()); System.out.println("该构造方法的入口参数类型依次为:"); // 获取所有参数类型 Class[] parameterTypes = con.getParameterTypes(); for (int j = 0; j < parameterTypes.length; j++) { System.out.println(" " + parameterTypes[j]); } System.out.println("该构造方法可能拋出的异常类型为:"); // 获取所有可能拋出的异常类型 Class[] exceptionTypes = con.getExceptionTypes(); for (int j = 0; j < exceptionTypes.length; j++) { System.out.println(" " + parameterTypes[j]); } // 创建一个未实例化的Book类实例 Book book1 = null; while (book1 == null) { try { // 如果该成员变量的访问权限为private,则拋出异常 if (i == 1) { // 通过执行带两个参数的构造方法实例化book1 book1 = (Book) con.newInstance("Java 教程", 10); } else if (i == 2) { // 通过执行默认构造方法实例化book1 book1 = (Book) con.newInstance(); } else { // 通过执行可变数量参数的构造方法实例化book1 Object[] parameters = new Object[] { new String[] { "100", "200" } }; book1 = (Book) con.newInstance(parameters); } } catch (Exception e) { System.out.println("在创建对象时拋出异常,下面执行 setAccessible() 方法"); con.setAccessible(true); // 设置允许访问 private 成员 } } book1.print(); System.out.println("=============================\n"); } } } 运行数据如下 : 查看是否允许带可变数量的参数:false 该构造方法的入口参数类型依次为: 该构造方法可能抛出的异常类型为: 在创建对象时抛出异常,下面执行setAccessible()方法 name = null id = 0 price = 0 ============================= 当通过反射访问两个参数的构造方法 Book(String\_name,int\_id) 时,将看到如下所示的输出。 查看是否允许带可变数量的参数:false 该构造方法的入口参数类型依次为: class java.lang.String int 该构造方法可能抛出的异常类型为: 在创建对象时抛出异常,下面执行setAccessible()方法 name = null id = 0 price = 0 ============================= 当通过反射访问可变参数数量的构造方法 Book(String…strings) 时,将看到如下所示的输出。 查看是否允许带可变数量的参数:true 该构造方法的入口参数类型依次为: class java.lang.String; 该构造方法可能抛出的异常类型为: class java.lang.String; 在创建对象时抛出异常,下面执行setAccessible()方法 name = null id = 0 price = 0 ============================= -------------------- ## 获取对象方法 ## 要动态获取一个对象方法的信息,首先需要通过下列方法之一创建一个 Method 类型的对象或者数组。 * getMethods() * getMethods(String name,Class<?> …parameterTypes) * getDeclaredMethods() * getDeclaredMethods(String name,Class<?>…parameterTypes) objectClass.getDeclaredConstructor("max",int.class,String.class); objectClass.getDeclaredConstructor("max",new Class[]{ int.class,String.class}); ![Alt][Alt 3] -------------------- ## 访问成员变量 ## 通过下列任意一个方法访问成员变量时将返回 Field 类型的对象或数组。 * getFields() * getField(String name) * getDeclaredFields() * getDeclaredField(String name) object.getDeciaredField("price"); ![Alt][Alt 4] public class Book2 { String name; public int id; private float price; protected boolean isLoan; } public class Test03 { public static void main(String[] args) { Book2 book = new Book2(); // 获取动态类Book2 Class class1 = book.getClass(); // 获取Book2类的所有成员 Field[] declaredFields = class1.getDeclaredFields(); // 遍历所有的成员 for(int i = 0;i < declaredFields.length;i++) { // 获取类中的成员变量 Field field = declaredFields[i]; System.out.println("成员名称为:" + field.getName()); Class fieldType = field.getType(); System.out.println("成员类型为:" + fieldType); boolean isTurn = true; while(isTurn) { try { // 如果该成员变量的访问权限为private,则抛出异常 isTurn = false; System.out.println("修改前成员的值为:" + field.get(book)); // 判断成员类型是否为int if(fieldType.equals(int.class)) { System.out.println("利用setInt()方法修改成员的值"); field.setInt(book, 100); } else if(fieldType.equals(float.class)) { // 判断成员变量类型是否为float System.out.println("利用setFloat()方法修改成员的值"); field.setFloat(book, 29.815f); } else if(fieldType.equals(boolean.class)) { // 判断成员变量是否为boolean System.out.println("利用setBoolean()方法修改成员的值"); field.setBoolean(book, true); } else { System.out.println("利用set()方法修改成员的值"); field.set(book, "Java编程"); } System.out.println("修改后成员的值为:" + field.get(book)); } catch (Exception e) { System.out.println("在设置成员变量值时抛出异常,下面执行setAccessible()方法"); field.setAccessible(true); isTurn = true; } } System.out.println("=============================\n"); } } } 成员名称为:name 成员类型为:class java.lang.String 修改前成员的值为:null 利用set()方法修改成员的值 修改后成员的值为:Java编程 ============================= 访问 id 成员的运行效果如下所示: 成员名称为:id 成员类型为:int 修改前成员的值为:0 利用setInt()方法修改成员的值 修改后成员的值为:100 ============================= 访问 price 成员的运行效果如下所示: 成员名称为:price 成员类型为:float 在设置成员变量值时抛出异常,下面执行setAccessible()方法 修改前成员的值为:0.0 利用setFloat()方法修改成员的值 修改后成员的值为:29.815 ============================= 访问 isLoan 成员的运行效果如下所示: 成员名称为:isLoan 成员类型为:boolean 修改前成员的值为:false 利用setBoolean()方法修改成员的值 修改后成员的值为:true ============================= ## 总结 ## -------------------- 反射jdbc的driver就是明显的例子。由于后面会使用框架,很多东西的都是封装的,所以从上文中可以把反射理解为,可以任意对一个类进行直接剖析的工具 [Alt]: https://img-blog.csdnimg.cn/20200103100714927.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU1MDQ5MA==,size_16,color_FFFFFF,t_70 [Alt 1]: https://img-blog.csdnimg.cn/20200103103146206.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU1MDQ5MA==,size_16,color_FFFFFF,t_70 [Alt 2]: https://img-blog.csdnimg.cn/20200103103345526.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU1MDQ5MA==,size_16,color_FFFFFF,t_70 [Alt 3]: https://img-blog.csdnimg.cn/20200103112953669.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU1MDQ5MA==,size_16,color_FFFFFF,t_70 [Alt 4]: https://img-blog.csdnimg.cn/2020010311320693.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU1MDQ5MA==,size_16,color_FFFFFF,t_70
还没有评论,来说两句吧...