• Java 反射机制:(十二)调用运行时类中指定的结构


    一、调用构造器

      1、调用运行时类中的指定的构造器

         ① 调用 getDeclaredConstructor() 方法根据参数获取指定的构造器

        ② 保证此构造器是可以访问的(暴力反射,针对非 public 修饰)

        ③ 调用 newInstance(Object obj, initargs) 方法创建对象

     1     @Test
     2     public void testConstructor() throws Exception {
     3         Class clazz = Person.class;
     4 
     5         //private Person(String name)
     6         /*
     7         1.获取指定的构造器
     8         getDeclaredConstructor():参数:指明构造器的参数列表
     9          */
    10 
    11         Constructor constructor = clazz.getDeclaredConstructor(String.class);
    12 
    13         //2.保证此构造器是可访问的
    14         constructor.setAccessible(true);
    15 
    16         //3.调用此构造器创建运行时类的对象
    17         Person per = (Person) constructor.newInstance("Tom");
    18         System.out.println(per);
    19 
    20     }

      2、

    二、调用属性

      1、调用指定属性

        在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()get()方法就可以完成设置和取得属性内容的操作

    public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
    
    public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。
    

          在 Field 中:

    public Object get(Object obj) 取得指定对象obj上此Field的属性内容
    
    public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容
    

      

      2、操作运行时类的指定的 public 的属性

        ① 获取运行时的类;

        ② 获取指定的属性:要求运行时类中属性声明为public

        ③ 设置、获取当前属性的值

     1      @Test
     2      public void testField() throws Exception {
     3         Class clazz = Person.class;
     4 
     5         //创建运行时类的对象
     6         Person p = (Person) clazz.newInstance();
     7 
     8 
     9         //获取指定的属性:要求运行时类中属性声明为public
    10         //通常不采用此方法
    11         Field id = clazz.getField("id");
    12 
    13         /*
    14         设置当前属性的值
    15 
    16         set():参数1:指明设置哪个对象的属性   参数2:将此属性值设置为多少
    17          */
    18 
    19         id.set(p,1001);
    20 
    21         /*
    22         获取当前属性的值
    23         get():参数1:获取哪个对象的当前属性值
    24          */
    25         int pId = (int) id.get(p);
    26         System.out.println(pId);
    27 
    28 
    29     }

      3、操作运行时类中的指定的非public修饰属性

        ① 获取运行时的类;

        ② 获取运行时类中指定变量名的属性;

        ③ 保证当前属性是可访问的(暴力反射,防止非 public 修饰的)

    setAccessible(true):暴力反射          // 忽略访问权限修饰符的安全检查

        ④ 获取、设置指定对象的此属性值

     1     @Test
     2     public void testField2() throws Exception {
     3         Class clazz = Person.class;
     4 
     5         //创建运行时类的对象
     6         Person p = (Person) clazz.newInstance();
     7 
     8         //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
     9         Field name = clazz.getDeclaredField("name");
    10 
    11         //2.保证当前属性是可访问的
    12         name.setAccessible(true); //暴力反射
    13         //3.获取、设置指定对象的此属性值
    14         name.set(p,"Tom");
    15 
    16         System.out.println(name.get(p));
    17     }

    三、调用方法

      1、操作运行时类中的指定的方法

        

    Object invoke(Object obj, Object … args) 执行方法
    

         说明:

          ① Object 对应原方法的返回值,若原方法无返回值,此时返回null;

          ② 若原方法若为静态方法,此时形参Object obj可为null;

          ③ 若原方法形参列表为空,则Object[] argsnull;

          ④ 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

        操作步骤:

        ① 根据运行时类对象获取指定的某个方法

        ② 保证当前方法是可访问的(暴力反射,防止非 public 修饰)

        ③ 调用invoke() 方法

     1     @Test
     2     public void testMethod() throws Exception {
     3 
     4         Class clazz = Person.class;
     5 
     6         //创建运行时类的对象
     7         Person p = (Person) clazz.newInstance();
     8 
     9         /*
    10         1.获取指定的某个方法
    11         getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
    12          */
    13         Method show = clazz.getDeclaredMethod("show", String.class);
    14         //2.保证当前方法是可访问的
    15         show.setAccessible(true);
    16 
    17         /*
    18         3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
    19         invoke()的返回值即为对应类中调用的方法的返回值。
    20          */
    21         Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
    22         System.out.println(returnValue);
    23 
    24         System.out.println("*************如何调用静态方法*****************");
    25 
    26         // private static void showDesc()
    27 
    28         Method showDesc = clazz.getDeclaredMethod("showDesc");
    29         showDesc.setAccessible(true);
    30         //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
    31         //Object returnVal = showDesc.invoke(null);
    32         Object returnVal = showDesc.invoke(Person.class);
    33         System.out.println(returnVal);//null
    34 
    35     }

      2、

     

    四、关于 setAccessible 方法

      1、MethodFieldConstructor对象都有setAccessible()方法。

      2、setAccessible启动和禁用访问安全检查的开关。

      3、参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。

        ① 提高反射的效率。 如果代码中必须用反射, 而该句代码需要频繁的被调用, 那么请设置为true

        ② 使得原本无法访问的私有成员也可以访问;

      4、参数值为 false 则指示反射的对象应该实施Java语言访问检查。

    五、操作数组

      在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array类来动态的创建数组,操作数组元素等。

      Array 类提供了如下几个方法:

    public static Object newInstance(Class<?> componentType, int... dimensions):创建一个具有指定的组件类型和维度的新数组
    public static void setXxx(Object array,int index,xxx value):将array数组中[index]元素的值修改为value。
    public static xxx getXxx(Object array,int index,xxx value):将array数组中[index]元素的值返回。
    

      此处的 Xxx 对应 8 种基本数据类型,如果该属性的类型是引用数据类型,则直接使用 set(object arr,int index, Object value)方法或get(Object array, int index) 方法。

      Demo:

     1 import java.lang.reflect.Array;
     2 
     3 public class TestArray {
     4     public static void main(String[] args) {
     5         Object arr = Array.newInstance(String.class, 5);
     6         Array.set(arr, 0, "Hello");
     7         Array.set(arr, 1, "World");
     8         System.out.println(Array.get(arr, 0));
     9         System.out.println(Array.get(arr, 1));
    10         System.out.println(Array.get(arr, 2));
    11     }
    12 }

    五、案例

      需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法

      实现步骤:

      实现技术
        ① 配置文件
        ② 反射
      实现步骤:
        ① 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
        ② 在程序中加载读取配置文件
        ③ 使用反射技术来加载类文件进内存
        ④ 创建对象
        ⑤ 执行方法

       代码实现:

    1 // 定义的配置文件 pro.properties
    2 className=cn.java.domain.Student
    3 methodName=sleep

      程序:

     1 // 框架类
     2 public class ReflectTest {
     3      public static void main(String[] args) throws Exception {
     4          //可以创建任意类的对象,可以执行任意方法
     5  
     6          /*
     7              前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
     8           */
     9  
    10          //1.加载配置文件
    11          //1.1创建Properties对象
    12          Properties pro = new Properties();
    13          //1.2加载配置文件,转换为一个集合
    14          //1.2.1获取class目录下的配置文件
    15          ClassLoader classLoader = ReflectTest.class.getClassLoader();
    16          InputStream is = classLoader.getResourceAsStream("pro.properties");
    17          pro.load(is);
    18  
    19          //2.获取配置文件中定义的数据
    20          String className = pro.getProperty("className");
    21          String methodName = pro.getProperty("methodName");
    22  
    23  
    24          //3.加载该类进内存
    25          Class cls = Class.forName(className);
    26          //4.创建对象
    27          Object obj = cls.newInstance();
    28          //5.获取方法对象
    29          Method method = cls.getMethod(methodName);
    30          //6.执行方法
    31          method.invoke(obj);
    32  
    33  
    34      }
    35 }
  • 相关阅读:
    (一)Python入门-2编程基本概念:18字符串-驻留机制-内存分析-字符串同一判断-值相等判断
    (一)Python入门-2编程基本概念:07内置数据类型-基本算数运算符
    (一)Python入门-2编程基本概念:08整数-不同进制-其他类型转换成整数
    (一)Python入门-2编程基本概念:09浮点数-自动转换-强制转换-增强赋值运算符
    (一)Python入门-2编程基本概念:10时间表示-unix时间点-毫秒和微妙-time模块
    (一)Python入门-2编程基本概念:11布尔值-比较运算符-逻辑运算符及短路问题
    (一)Python入门-2编程基本概念:12同一运算符-整数缓存问题
    (一)Python入门-2编程基本概念:01程序的构成
    (一)Python入门-2编程基本概念:02对象的基本组成和内存示意图
    有关位运算的基础知识总结
  • 原文地址:https://www.cnblogs.com/niujifei/p/14902690.html
Copyright © 2020-2023  润新知