• Java反射机制


    Java反射机制

    Java反射(Reflection)是Java非常重要的动态特性,通过使用反射我们不仅可以获取到任何类的成员方法(Methods)、成员变量(Fields)、构造方法(Constructors)等信息,还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等。Java反射机制是Java语言的动态性的重要体现,也是Java的各种框架底层实现的灵魂。

    获取Class对象

    Java反射操作的对象是java.lang.class 对象,那么我们首先需要得到一个类的对象,通常有以下就中获取类对象的方法:

    1. Class clazz1 = 类名.class,如:Person.class。
    2. Class clazz2 = Class.forName("reflectTest.Person")。
    3. classLoader.loadClass("reflectTest.Person");
    4. Person p1 = new Person();
    Class clazz = p1.getClass();

      

    获取数组类型的class对象会有一点特殊,需要使用java类型大的描述符:

    Class<?> doubleArray = Class.forName("[D");//相当于double[].class
    Class<?> cStringArray = Class.forName("[[Ljava.lang.String;");// 相当于String[][].class

    举例:获取Runtime类的class对象代码:

    String className     = "java.lang.Runtime";
    Class  runtimeClass1 = Class.forName(className);
    Class  runtimeClass2 = java.lang.Runtime.class;
    Class  runtimeClass3 = ClassLoader.getSystemClassLoader().loadClass(className);
    

    通过上面的三种方法都可以获得一个java.lang.Runtime类的Class对象,反射调用内部类的时候需要使用$来代替.,如JavaSec.Test类有一个叫做Hello的内部类,那么调用的时候就应该将类名写成:JavaSec.Test$Hello

    反射调用类方法

    获取类中的所有成员方法:

    Method[] methods = clazz.getDeclaredMethods()
    

    获取当前类指定的成员方法:

    Method method = clazz.getDeclaredMethod("方法名");
    Method method = clazz.getDeclaredMethod("方法名", 参数类型如String.class,多个参数用","号隔开);
    

      getMethodgetDeclaredMethod都能够获取到类成员方法,区别在于getMethod只能获取到当前类和父类的所有有权限的方法(如:public),而getDeclaredMethod能获取到当前类的所有成员方法(不包含父类)。

    调用类方法代码:

    method.invoke(方法实例对象, 方法参数值,多个参数值用","隔开);
    

    method.invoke的第一个参数必须是类实例对象,如果调用的是static方法那么第一个参数值可以传null,因为在java中调用静态方法是不需要有类实例的,因为可以直接类名.方法名(参数)的方式调用。

    method.invoke的第二个参数不是必须的,如果当前调用的方法没有参数,那么第二个参数可以不传,如果有参数那么就必须严格的依次传入对应的参数类型

    反射调用成员属性

    Java反射不但可以获取类所有的成员变量名称,还可以无视权限修饰符实现修改对应的值。

    获取当前类的所有成员变量:

    Field fields = clazz.getDeclaredFields();

    获取当前类的指定成员变量:

    Field field  = clazz.getDeclaredField("变量名");

    获取成员变量值:

    Object obj = field.get(类实例对象);
    

    修改成员变量值:

    field.set(类实例对象, 修改后的值);
    

      当我们没有修改的成员变量权限时可以使用: field.setAccessible(true)的方式修改为访问成员变量访问权限。

    反射java.lang.Runtime

    java.lang.Runtime因为有一个exec方法可以执行本地命令,所以在很多的payload中我们都能看到反射调用Runtime类来执行本地系统命令,通过学习如何反射Runtime类也能让我们理解反射的一些基础用法。

    // 获取Runtime类对象
    Class runtimeClass1 = Class.forName("java.lang.Runtime");
    
    // 获取构造方法
    Constructor constructor = runtimeClass1.getDeclaredConstructor();
    constructor.setAccessible(true);
    
    // 创建Runtime类示例,等价于 Runtime rt = new Runtime();
    Object runtimeInstance = constructor.newInstance();
    
    // 获取Runtime的exec(String cmd)方法
    Method runtimeMethod = runtimeClass1.getMethod("exec", String.class);
    
    // 调用exec方法,等价于 rt.exec(cmd);
    Process process = (Process) runtimeMethod.invoke(runtimeInstance, cmd);
    
    // 获取命令执行结果
    InputStream in = process.getInputStream();
    
    // 输出命令执行结果
    System.out.println(IOUtils.toString(in, "UTF-8"));

    反射调用Runtime实现本地命令执行的流程如下:

    1. 反射获取Runtime类对象(Class.forName("java.lang.Runtime"))。
    2. 使用Runtime类的Class对象获取Runtime类的无参数构造方法(getDeclaredConstructor()),因为Runtime的构造方法是private的我们无法直接调用,所以我们需要通过反射去修改方法的访问权限(constructor.setAccessible(true))。
    3. 获取Runtime类的exec(String)方法(runtimeClass1.getMethod("exec", String.class);)。
    4. 调用exec(String)方法(runtimeMethod.invoke(runtimeInstance, cmd))。
    
    
  • 相关阅读:
    Linux环境下安装redis
    设计模式-工厂模式
    Java操作.csv格式文件导入
    SQL server数据迁移至达梦(DM)数据库
    spring boot+mybatis整合达梦数据库
    canal实时同步mysql数据到redis或ElasticSearch
    SpringBoot按日期和文件大小生成日志文件到对应日期文件夹
    vue项目改造nuxt 利于seo
    解决vue 项目服务器打包报错 ERROR in ./node_modules/_babel-loader@7.1.5@babel-loader/lib!./node_modules/_vue-loader@13.7.3@vue-loader/lib/selector.js?type=script&index=0!./src/views/pastReviewDetail.vue
    gulp自动化构建
  • 原文地址:https://www.cnblogs.com/s1awwhy/p/13725493.html
Copyright © 2020-2023  润新知