文章目录

  • 写在前面
  • 什么是java的反射机制?
  • 通过Singleton获取实例
    • Runtime类
    • 利用反射机制获取类对象
    • 利用反射机制获取函数
    • 执行方法
    • 小结
  • 通过Constructor获取实例
  • 私有方法的调用
  • 更灵活的调用方式
  • 小结
  • Reference

写在前面

本文是学习了 啦啦菌NODE 大神的文章 Java反射机制 后根据自己的理解进行的记录和整理,希望拜读原文的请大家移步大神的博客。

什么是java的反射机制?

在Java中的反射机制是指在运行状态中,**任意一个类都,能知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法。这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。**简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。1

通过Singleton获取实例

Runtime类

在每一个JVM进程里面都会存在有一个Runtime类的对象,这个类的主要功能是取得一些与运行时有关的环境属性,或者创建新的进程.

直接调用的方式为Runtime.getRuntime().exec("open -a Calculator"),这条语句调用本机的计算器程序,并创建进程。

验证poc的时候通常会调起一个系统计算器进程,open -a Calculator是mac在terminal中打开计算器的命令,windows中通常为calc.exe

上述语句,如果采用反射机制根据类的名字和方法的名字实现,应该怎样编写呢?

  • 利用反射机制获取Runtime类;
  • 利用反射机制获取exec这个方法
  • 传入参数并执行exec方法

首先我们来解决第一个问题

利用反射机制获取类对象

获取java.lang.class类对象的三种方法

  1. obj.getClass():如果上下文中存在某个类的实例obj,那么我们可以直接通过obj.getClass()获取它的类。

    • obj是实例对象:通过obj.getClass()可以获取该实例的类对象。(如Runtime.getRuntime().getClass()的结果是class java.lang.Runtime类)
    • obj是类对象:通过obj.getClass()可以获取java.lang.class
  2. obj.class:当obj是一个已经加载的类时,直接通过.class参数获取它的java.lang.Class对象。

  3. Class.forName:如果知道类的名字,可以直接使用forname来获取java.lang.Class初始化对象。

     * A call to {@code forName("X")} causes the class named* {@code X} to be initialized.
    

很明显,我们是要根据类的名字获取类对象,因此这里要用到Class.forName()的方式。

Class.forName有两种调用方式

  • Class<?> forName(String className)
  • Class<?> forName(String name, boolean initialize, ClassLoader loader)

前一种调用其实是后面的封装, 下面这两种调用是等价的

Class.forName("Foo")Class.forName("Foo", true, this.getClass().getClassLoader()

第一个问题到这里就有答案了,我们可以通过

Class<?> runtimeCls = Class.forName("java.lang.Runtime");

获取Runtime类

利用反射机制获取函数

getMethod(String name, Class<?>... parameterTypes)是通过反射获取某个类的public方法。

    /*** Returns a {@code Method} object that reflects the specified public* member method of the class or interface represented by this* {@code Class} object. The {@code name} parameter is a* {@code String} specifying the simple name of the desired method. The* {@code parameterTypes} parameter is an array of {@code Class}* objects that identify the method's formal parameter types, in declared* order. If {@code parameterTypes} is {@code null}, it is* treated as if it were an empty array.*/

因此在获取类对象后,可以进一步使用getMethod方法来获取类对象的函数。这里我们使用getMethod来获取Runtime的exec方法。exec的参数应为字符类型,即String.class,因此可以以如下方式获取exec方法

Method exec = runtimeCls.getMethod("exec", String.class);

执行方法

getMethod返回的是一个Method对象,而Method类下存在方法invoke(),它的作用是传入参数,执行方法。

从invoke的注释中,可以了解到以下信息。

public Object invoke(Object obj, Object... args)

  • @param obj: 执行方法的类对象实例。

    • 如果要执行的方法时普通方法,传入调用这个方法的类对象实例
    • 如果要执行的方法是静态方法,那么obj可以忽略,传入Null即可。
    • 如果要执行的方法是静态方法,且类对象还未初始化,那么在调用invoke的时候会执行初始化。
  • @param args: 执行方法所需的参数。如果没有参数的话,args可以为长度0的Array或者Null。

这里我们要执行的exec是一个普通方法。因此第一个参数需要传入执行exec的类象实例,即Runtime实例。根据前面所学,我们可以用Class.forName("java.lang.Runtime")获取类对象,但是需要注意的是这里仅仅是获取了类对象,而不是实例。如果以这种方式构造成后面介绍的完整语句并且调用,会报错object is not an instance of declaring class。因此获取类对象后要继续获取类实例。Runtime可以用静态方法getRuntime()获取Runtime的实例,这个函数也不需要参数,因此我们可以用getmethod配合invoke来获取Runtime的实例, 也就是

Object runtimeObj = Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null);

第二个参数传入exec要执行的命令,即open -a Calculator

String execParam = "open -a Calculator";

小结

综上,我们可以以如下方式利用反射机制使用runtime的exec方法启动一个进程

// 获取exec方法
Class<?> runtimeCls = Class.forName("java.lang.Runtime");
Method exec = runtimeCls.getMethod("exec", String.class);
// 利用getmethod获取runtime实例
Method getMethod = runtimeCls.getMethod("getRuntime");
Object runtimeObj = getMethod.invoke(null);
// exec参数
String execParam = "open -a Calculator";
// 执行exec
exec.invoke(runtimeObj, execParam);

如果写成一行语句的话,

Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),"open -a Calculator");

以上我们完成了通过类内置的静态方法获取类的实例,进一步调用一个public方法。但是假如一个类没有无参构造方法(即不能class.newInstance()),也没有单例模式(只存在一个实例)的静态方法(即不能像getRuntime一样获取实例),那我们该如何实例化这个类呢?

通过Constructor获取实例

ProcessBuilder用来创建操作系统的进程。可以通过以下方式调用一个进程

  List<String> paramList = new ArrayList<>();paramList.add("open");paramList.add("-a");paramList.add("Calculator");ProcessBuilder pb = new ProcessBuilder(paramList);pb.start();

Constructor<T> getConstructor(Class<?>... parameterTypes)函数反射类的Constructor对象,然后利用Constructor.newInstance(Object ... initargs)方法获取实例化的对象。

newInstance()返回的是T对象,因此要执行ProcessBuilder的方法还需要进行一次强制类型转化。

((ProcessBuilder) Class.forName("java.lang.ProcessBuilder").getConstructor(List.class).newInstance(Arrays.asList("open","-a","Calculator"))).start();

进一步利用上面getMethod的方式调用start方法,

Class.forName("java.lang.ProcessBuilder").getMethod("start").invoke(Class.forName("java.lang.ProcessBuilder").getConstructor(List.class).newInstance(Arrays.asList("calc.exe")));

成功调起计算器进程。

ProcessBuilder除了上述的Constructor外,还有另外一个Constructor

ProcessBuilder(String... command)

以这种方式调用进程的方式是

ProcessBuilder pb = new ProcessBuilder("open", "-a", "Calculator");
pb.start();

传入的参数类型不是List<String>而是String...,即String[],因此采用这种Constructor的时候,getConstructor()的参数应为String[].class

最终构造的语句为

Class.forName("java.lang.ProcessBuilder").getMethod("start").invoke(Class.forName("java.lang.ProcessBuilder").getConstructor(String[].class).newInstance((Object[])new String[][]{{"open", "-a", "Calculator"}}));

注意到newInstance()中传入的参数是(Object[]) String [][]类型的,并不是String[]。这是为什么呢?

一开始尝试传入String[]{"open", "-a", "Calculator"},报错java.lang.IllegalArgumentException: wrong number of arguments。于是调试比较两个Constructor在newInstance时传入的参数有何不同。

下图是List.class时传入的参数

下图是String[].class时传入的参数[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y8vLntIG-1639315281205)(Img/image-20211212145333040.png)]

可以注意到传递到newInstance的时候,少了一层,所以构建的时候在外面添加一层列表new String[][]{{"open", "-a", "Calculator"}}

但是这样传递发现还是报错java.lang.IllegalArgumentException: argument type mismatch。注意到编译器有warning,Type String[][] of the last argument to method newInstance(Object...) doesn't exactly match the vararg parameter type. Cast to Object[] to confirm the non-varargs invocation, or pass individual arguments of type Object for a varargs invocation.

于是把添加强制类型转化为(Object[])new String[][]{{"open", "-a", "Calculator"}}。可以成功运行


私有方法的调用

上面两个例子都是调用的Public方法,如果要获取private方法呢?

前面我们使用了getMethod来获取public方法,getConstructor来获取构造器,下面来学习getDeclaredmethodgetDeclaredConstructor两个方法。这两个方法获取的是当前类中声明的方法,包扩所有私有的方法但不包括从父类继承的方法。

还以Runtime为例,Runtime的构造方法是一个private方法。我们利用getDeclaredConstructor获取它的constructor并调用一个计算器进程。

Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getDeclaredConstructor().newInstance(), "open -a Calculator");

运行上述语句,得到一个报错

java.lang.IllegalAccessException: Class RuntimeExec2 can not access a member of class java.lang.Runtime with modifiers "private"

虽然得到了私有方法,但是还需要进一步打破对私有方法的调用,这里使用setAccsessible(true)来打破访问限制。

Constructor<?> con = Class.forName("java.lang.Runtime").getDeclaredConstructor();
con.setAccessible(true);Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(con.newInstance(), "open -a Calculator");

运行上述语句成功调起计算器。

更灵活的调用方式

如果不允许使用Class.forName("java.lang.Runtime").getMethod(),这个时候怎么办呢?

可以使用反射机制来获取getMethod方法。

Class.forName("java.lang.Runtime").getMethod("getRuntime")Class.forName("java.lang.Class").getMethod("getMethod", new Class[] {String, Class[].class}).invoke(Class.forName("java.lang.Runtime"), "getRuntime", new Class[0]);

小结

到此为止,我们已经学习了如何利用反射机制调用任意类的任意方法。

Reference

  1. java反射机制
  2. Java高级特性——反射

  1. http://c.biancheng.net/view/6907.html ↩︎

Java反序列化(一) - Java反射机制相关推荐

  1. Flex前端与Java服务端交互,反射机制挑大旗

    Flex作为RIA的一支,提供了非常丰富多彩的客户端实现,并且编写起来非常灵活.Java提供了强大的功能实现,与Flex结合也让Java开发穿上了华丽外衣 . BlazeDS 是LCDS的一个衍生版 ...

  2. JAVA语言中的反射机制

    在Java 运行时 环境中,对于任意一个类,能否知道这个类有哪些属性和方法?     对于任意一个对象,能否调用他的方法?这些答案是肯定的,这种动态获取类的信息,以及动态调用类的方法的功能来源于JAV ...

  3. Java高级语法笔记-反射机制(Reflection) (1)

    反射机制:在C/C++里面是没有的. 反射机制是Java的一个非常重要的机制.一些著名的应用框架都使用了此机制. java.lang.Class它是Java语法的一个基础类,用于描述一个class对象 ...

  4. Java的反射作用_java反射机制的作用与优点

    java的反射机制就是增加程序的灵活性,避免将程序写死到代码里, 例如: 实例化一个 person()对象, 不使用反射, new person(); 如果想变成 实例化 其他类, 那么必须修改源代码 ...

  5. java 求正割_Java 反射机制详解

    动态语言 动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化.比如众所周知的ECMAScript(JavaScript)便是一个动态语言.除此之外如Ru ...

  6. Java基础篇:反射机制详解

    反射机制 反射机制.反射含义 1.利用反射获取类对象的三种方法 2.利用反射获取类对象的类名.方法.属性 3.利用反射获取类对象中的构造器.构造器参数类型.实例化构造器 实例化重点 类的加载方式不同 ...

  7. Java 数据交换格式反射机制SpringIOC原理分析

    数据交换格式&反射机制&SpringIOC原理分析 什么是数据交换格式? 数据交换格式使用场景 JSON简单使用 什么是JSON? JSON格式的分类 常用JSON解析框架 使用fas ...

  8. Java核心特性之反射机制

    1.反射概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为j ...

  9. JAVA基础,注解反射机制

    文章目录 注解 (非常重要) 什么是注解 基本注解 @Override @Deprecated @SuppressWarnings @SafeVarargs @FunctionalInterface ...

最新文章

  1. JavaScript 异步编程--Generator函数、async、await
  2. 阿里面试 Java 都问什么?万字总结!
  3. 【面试经验分享】Java 面试中的那些潜规则
  4. python中判断变量的类型
  5. etcd集群部署与遇到的坑
  6. wchar_t 的输出问题
  7. win8 网络受限解决
  8. matlab中x从0到5不含0,关于MATLAB的数学建模算法学习笔记
  9. A+B in Hogwarts (20)
  10. slf4j注解log报错_这个注解一次搞定限流与熔断降级:@SentinelResource
  11. 【JavaScript】javaScript基础知识回顾
  12. 数字核心 驱动转型:SAP S/4HANA 数字化转型论坛 - 杭州站 即刻报名
  13. 怎么制作铁闸门_咖啡师养成记 | 教你做一杯合格的拿铁咖啡
  14. 高斯过程回归(输出学习法!)
  15. 中医教你按摩手指,治疗百病
  16. python 极客学院 正则表达式
  17. Linux以百万兆字节显示内存大小
  18. 正则表达式限制输入框输入
  19. android换肤动画,Android换肤(二) — 插件式换肤
  20. OpenCV Java入门一 在MAC系统上安装OpenCV

热门文章

  1. 电视剧中一看就崩溃的镜头
  2. 余弦相似度 高维数据_从勾股定理到余弦相似度-程序员的数学基础
  3. Eventually Consistent(最终一致性)(转)
  4. java古诗_java--补全诗句代码
  5. 十六、基于FPGA的CRC校验设计实现
  6. 拨开国产 COS 系统的重重迷雾
  7. 刚子扯谈:神马网络营销
  8. 小生云服务器,HobitLab#2--云服务器的有效利用之搭建tiny tiny RSS
  9. M2M系统是什么,你知道吗?
  10. nii数据的各种处理详解