• JAVA 反射学习


    Java反射

    参考《Java安全漫谈》

    概念

    反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行状态的时候才动态加载类,对于任意一个类都能够知道这个类所有的属性和方法,并且对于任意一个对象,都能够调用它的方法/访问属性。这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。通过使用反射我们不仅可以获取到任何类的成员方法(Methods)、成员变量(Fields)、构造方法(Constructors)等信息,还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等。
     

    java反射机制组成需要重点注意一下的类:

    • java.lang.Class  :类对象
    • java.lang.reflect.Constructor 类构造器对象
    • java.lang.reflect.Field:类的属性对象;

    • java.lang.reflect.Method   类方法对象

    Class类

    在程序运行期间,Java运行时系统始终为所有对象维护一个运行时类型标识。这个信息会跟踪每个对象所属的类。虚拟机利用运行时类型信息选择要执行的正确方法。

    可以使用一个特殊的Java类访问这些信息。保存这些信息的类名为Class

    能得到Class类,也就是 java.lang.class对象 的三种方法: 

    •  .class 在加载了某个类的时候,可以直接获取他的属性,如:
      System.out.println(cla.class);
    • obj.getClass() 如果存在了某个 obj实例,那么可以通过getClass()方法得到这个类对应的class例如:
      System.out.println(cla.getClass());
    • Class.forName ,也是最常用的,知道了哪个类的完整包名或类名后,可以作为参数:
      Class cla = Class.forName("a.cat"); //包名下的cat类
      System.out.println(Class.forName("a.cat"));

    一般使用第三种(Class.forName) 方法去动态加载类 ,且使用forName就不需要导入其他类,可以加载我们任意的类。 而使用 .class属性,则需要导入类的包,依赖性强,且容易抛出编译错误。

    而使用实例化对象的 getClass() 方法,需要本身创建一个对象,是静态的,就体现不出反射机制意义了,所以我们在获取class对象中, 一般都会使用 Class.forName去获取

    拿到了类之后,就可以通过反射来获取该类的成员变量,成员方法,构造函数,实例化类,并且利用invoke 调用方法    实操如下:

    获取成员变量Field

    1. //返回 字段对象,该对象反映此 类对象表示的类或接口的指定声明字段。
    2. getDeclaredField(String name)
    3. //返回 字段对象的数组, 字段对象反映由此 类对象表示的类或接口声明的所有字段。
    4. getDeclaredFields()
    5. //返回 字段对象,该对象反映此 类对象表示的类或接口的指定公共成员字段。
    6. getField(String name)
    7. //返回一个包含 字段对象的数组, 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
    8. getFields()

    例如:

    1. Class cls = Class.forName("com.person");
    2. System.out.println(cls.getName());
    3. Field[] field = cls.getFields();
    4. for (Field field1 : field) {
    5. System.out.println(" 属性: "+field1.getName()); //只能获取public 的属性
    6. }
    7. System.out.println("========");
    8. Field[] declaredFields = cls.getDeclaredFields(); //可以获取类全部属性
    9. for (Field all : declaredFields) {
    10. System.out.println("属性" + all.getName());
    11. }

    大致就是 getDeclaredFields() 和getDeclaredField() 可以获取所有的成员变量,下面两个得到的只有 public变量,第一 第三可以传入一个名字 (字符串 ) 来 得到指定的成员变量,而第二第四只能得到一个数组,没法得到指定的 (但是可以遍历 数组,取出所有的遍历)

    获取方法Method

    Class类 获取 Method 方法;

    1. //返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
    2. getDeclaredMethod(String name, 类... parameterTypes);
    3. //返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。
    4. getDeclaredMethods(); //数组
    5. //返回 方法对象,该对象反映此 类对象表示的类或接口的指定公共成员方法。
    6. getMethod(String name, 类... parameterTypes);
    7. //返回一个包含 方法对象的数组, 方法对象反映此 类对象所表示的类或接口的所有公共方法,包括由类或接口声明的那些以及从超类和超接口继承的那些。
    8. getMethods();

    用法 跟Field 类似。

    但是要注意后面的  String name, 类... parameterTypes 

    如:

    1. Class clazz = Class.forName("java.lang.Runtime");
    2. clazz.getMethod("exec", String.class);

    Runtime 类中的 exec 实现了方法的重载, 因此需要使用forMethod就要带上第二个参数,才能准确的找到指定方法的对象。

    获取构函数

    1. //返回一个 构造器对象,该对象反映此 类对象所表示的类或接口的指定构造函数。
    2. getDeclaredConstructor(类... parameterTypes)
    3. //返回 构造器对象的数组, 构造器对象反映由此 类对象表示的类声明的所有构造函数。
    4. getDeclaredConstructors()
    5. //返回一个 构造器对象,该对象反映此 类对象所表示的类的指定公共构造函数。
    6. getConstructor(类... parameterTypes)
    7. //返回一个包含 构造器对象的数组, 构造器对象反映了此 类对象所表示的类的所有公共构造函数。
    8. getConstructors()

    用法几乎差不多,可以使用 getConstructors 再进行遍历取出

    实例化类

    得到地址和类名后,使用newInstance 实例化这个类:

    1. Class> cls = Class.forName("com.person");
    2. Object o = cls.newInstance();
    3. System.out.println(o);

    但是这样实例化了这个类呢,会自动调用无参构造器,就需要用到之前得到的Constructor 了。

    1. Constructor> cons = cls.getDeclaredConstructor(String.class, double.class);
    2. Object snowy = cons.newInstance("snowy", 1000000000);
    3. System.out.println(snowy);

    调用方法

    反射中的调用,是用方法来调用对象,如:

    方法.invoke(对象)

    例子, 比如要调用 person 类中的 hi() 方法,首先要得到 hi() 方法的 Method对象,其次 还需要得到 person类的对象 作为invoke的参数

    1. Class> cls = Class.forName("com.person");//加载类
    2. Constructor> cons = cls.getDeclaredConstructor(String.class, double.class); //获取构造器
    3. Object snowy = cons.newInstance("snowy", 123);//实例化对象
    4. Method m2 = cls.getMethod("m2");//得到Method 对象
    5. Object name = m2.invoke(snowy); // 调用方法
    6. //System.out.println(name);

    如果调用的是普通方法,第一个参数就是类对象;

    如果调用的这个方法是静态方法,第一个参数是类,或者可以护忽略,设置为null

    权限访问

    如果得到的Field 、Method、Constructor 是私有属性的话,受到权限影响,不能直接调用或者读取,需要设置 setAccessible(true) ; 

    setAccessible方法AccessibleObejct类的一个方法,它是FieldMethodConstructor的公共超类。这个特性是为调式、持久存储和类似机制提供的。

    例如,Runtime类的构造函数是私有的,可以这样获取对象

    1. Class> cls = Class.forName("java.lang.Runtime");
    2. Constructor> m = cls.getDeclaredConstructor();
    3. m.setAccessible(true);
    4. cls.getMethod("exec", String.class).invoke(m.newInstance(),"calc.exe");

    参考:

    JAVA安全基础(一)--类加载器(ClassLoader) - 先知社区 (aliyun.com)

    JAVA安全基础(二)-- 反射机制 - 先知社区 (aliyun.com)

    《Java安全漫谈》

    Java反射学习_bfengj的博客-CSDN博客_java反射学习

  • 相关阅读:
    Apollo7.0系统概述
    机器学习笔记之EM算法(二)EM算法公式推导过程
    浅谈旧项目如何添加新依赖
    ​k8s常用命令 ​
    【MySQL】基础SQL语句——表的操作
    Interference Signal Recognition Based on Multi-Modal Deep Learning
    通过Element开发基础增删改查页面——Vue项目实战(三)
    Python 全栈系列211 自建容器仓库
    Bean作用域和生命周期-------JavaEE(Spring)
    校验算法总结
  • 原文地址:https://blog.csdn.net/snowlyzz/article/details/127591020