先来一段反射的概念:

在程序运行的时候动态装载类,查看类的信息,生成对象,或操作生成对象。
类在运行的时候,可以得到该类的信息,并且可以动态的修改这些信息

反射类的首要操作 - 获取类

获取类有 三种 方法:

假设类名为 Cat

(1)直接根据类名获取类

Class a = Cat.class;

(2)通过Class.forName 获取类,需要打全指定类的路径

Class a = Class.forName("org.xiaopan.test.Cat");

注意:

Class.forName 需要一个异常处理。不然编辑器无法通过。

(3)通过实例化的方式获取类

Class a = (new Cat()).getClass();

反射构造方法

吾有一类,曰:

class Cat{    //构造方法,形参为空    public Cat(){        System.out.println("Cat->nullCat");    }    //构造方法,形参为 一个String类型    public Cat(String a){        System.out.println("Cat->aCat,a=" + a);    }    //私有构造方法,形参为 一个String类型 和 一个Integer类型    private Cat(String a,Integer b){        System.out.println("Cat->abCat,a=" + a + "b=" + b.toString());    }    //构造方法,形参为 一个String数组类型 和 一个Map类型    public Cat(String[] aa,Map bb){        System.out.println("Cat->aabbCat");        System.out.println("aa=" + Arrays.toString(aa));        System.out.println("bb=" + bb);    }}

无参构造方法调用

try{    //获取到类    Class a = Cat.class;    //通过反射获取到指定类下的构造方法    //要获取的构造方法为:    //public Cat()    //由于该构造方法无参数,所以我们传入一个 null 即可,也可以不传    Constructor constructor1 = a.getConstructor(null);    //实例化类    constructor1.newInstance(null);}catch (Exception e){    System.out.println(e);}

输出:

注意:

getConstructor 需要加一个异常处理。

一个参数的构造方法调用

try{    //获取到类    Class a = Cat.class;    //通过反射获取到指定类下的构造方法    //要获取的构造方法为:    //public Cat(String a)    //由于该构造方法有一 String类型 参数    //在进行 getConstructor 反射时,就需要指定传参类型为 String.class    Constructor constructor1 = a.getConstructor(String.class);   //在实例化时,进行传参   constructor1.newInstance("testvalue");}catch (Exception e){    System.out.println(e);}

输出:

多个参数的私有构造方法调用

注意,这里调用的构造方法是私有的哦~

try{    //获取到类    Class a = Class.forName("org.xiaopan.test.Cat");    //通过反射获取到指定类下的构造方法    //要获取的 私有 构造方法为:    //private Cat(String a,Integer b)    //    //由于是 私有 方法,获取私有方法的函数为 getDeclaredConstructor    //由于有两个参数,所以需要在 getDeclaredConstructor 传入对应的参数类型    Constructor c = a.getDeclaredConstructor(String.class, Integer.class);    //设置强制反射    c.setAccessible(true);    //构造函数实例化,并传参    //这里有个注意点,看下文的注意    c.newInstance(new Object[]{"abcd",123456});}catch (Exception e){    System.out.println(e);}

输出:

注意:

参考了大佬的文章(参考文章在本文末尾 Referer 中),文章说 jdk1.4和 jdk1.5 处理调用的方法有区别

jdk1.4中,数组每个元素对应一个参数

jdk1.5中,整个数组是一个参数,用一个对象包起来

所以我们调用的传参的时候,需要使用这种格式:

c.newInstance(new Object[]{"abcd",123456});//即new Object[]{"abcd",123456} 的格式,用一个对象包裹起来

形参为数组和Map类型的构造方法调用

字符串数组

创建格式:

String[] a = {"abc", "def"};

Map:

java中的map,可以理解为“可自定义键值的数组”

形参为数组和Map类型的构造方法调用:

try{    //获取类    Class a = (new Cat()).getClass();    //调用的构造方法为:    //public Cat(String[] aa,Map bb)    //照常打 xxx.class 即可。java 万物皆对象    Constructor c = a.getConstructor(String[].class, Map.class);    //创建一个map    Map m = new HashMap();    m.put("a_key","a_value");    //实例化构造函数,注意要用 Object包裹的形式    //new String[]{} 是当场初始化字符串数组,当场赋值    c.newInstance(new Object[]{new String[]{"a","b","c"}, m});}catch (Exception e){    System.out.println(e);}

输出:

反射方法

简介:

反射方法和上文的反射构造方法差不多,如果是私有的话也是要设置强行调用,并且获取方法的函数为 getDeclaredxxxx

吾有一类:

class Cat{    public void a(){        System.out.println("a invoke");    }    public String[] b(String[] b){        return b;    }    public static void c(){        System.out.println("cccccc");    }}

反射无参数方法

try{    //获取类    Class a = Class.forName("org.xiaopan.test.Cat");    //先实例化,后面调用方法的时候需要使用实例化好的类    //注意,实例化之后返回的类型就是对于的类,做好类型转换    Cat cat =(Cat) a.newInstance();    //调用的方法为:    //public void a()    //    //获取方法,需要指定要获取的方法名    Method m = a.getMethod("a", null);    //调用方法,调用方法时,用 上一步代码中获取到的方法进行 invoke 调用操作    //而invoke方法中,第一个参数是实例化好的类    //第二个参数就是需要传入的参数    m.invoke(cat,null);}catch (Exception e){    System.out.println(e);}

输出:

反射有参数有返回值方法

try{    //获取类    Class a = Class.forName("org.xiaopan.test.Cat");    //先实例化,后面调用方法的时候需要使用实例化好的类    //注意,实例化之后返回的类型就是对于的类,做好类型转换    Cat cat =(Cat) a.newInstance();    //调用的方法为:    //public String[] b(String[] b)    //    //获取方法,需要指定要获取的方法名    Method m = a.getMethod("b",String[].class);    //调用方法,调用方法时,用 上一步代码中获取到的方法进行 invoke 调用操作    //调用时,如果参数是字符串数组,或者两个以上的参数    //最好使用 new Object[]{} 的形式传入    //兼容性好    //由于有返回值,我们在调用的时候也需要进行接收    //接受类型就看调用的类返回的类型了    String[] strs = (String[]) m.invoke(cat, new Object[]{new String[]{"str1","str2","str3"}});    //打印数组:    //for each打印数组,先指定抽出来的元素类型    //然后以冒号 : 分隔,左边是抽出元素变量名,右边是原数组    for (String str:strs){        System.out.println(str);    }}catch (Exception e){    System.out.println(e);}

输出:

反射静态方法

由于静态方法不需要实例化类,所以在 getMethod 的时候,直接传个 null 即可。也不需要 newInstance 类了。

try{    //获取类    Class a = Class.forName("org.xiaopan.test.Cat");    //调用的方法为:    //public static void c()    //    //获取方法    Method m = a.getMethod("c");    //由于是静态方法。直接调用,类对象中传入null即可    m.invoke(null);}catch (Exception e){    System.out.println(e);}

输出:

反射属性

反射属性也大同小异

吾有一类:

class Cat{    public String name = "maomao";      //公共 String类型 属性    public static Boolean sex = true;   //公共 静态 String类型 属性    private Integer age = 10;           //私有 Integer类型 属性}

反射公共属性

try{    //获取类    Class a = Class.forName("org.xiaopan.test.Cat");    //先实例化,后面获取属性的时候需要使用实例化好的类    Cat cat = (Cat) a.newInstance();    //属性:    //public String name = "maomao";    //    //获取属性    Field m = a.getField("name");    //获取属性值    //需要传入实例化类作为对象    String name = (String) m.get(cat);    System.out.println(name);}catch (Exception e){    System.out.println(e);}

输出:

反射公共静态属性

静态属性也一样,不需要实例化即可调用:

try{    //获取类    Class a = Class.forName("org.xiaopan.test.Cat");    //属性:    //public static Boolean sex = true;    //    //获取属性    Field m = a.getField("sex");    //获取属性值,静态属性不需要实例化类,直接传入null作为类对象即可    Boolean b = (Boolean) m.get(null);    System.out.println(b);    //设置属性值    m.set(null,false);    b = (Boolean) m.get(null);    System.out.println(b);}catch (Exception e){    System.out.println(e);}

输出:

反射私有属性

私有属性也一样,需要暴力反射

try{    //获取类    Class a = Class.forName("org.xiaopan.test.Cat");    //先实例化,后面获取属性的时候需要使用实例化好的类    Cat cat = (Cat) a.newInstance();    //属性:    //private Integer age = 10;    //    //获取私有属性    Field m = a.getDeclaredField("age");    //设置强制反射    m.setAccessible(true);    //获取属性值    Integer age = (Integer) m.get(cat);    //注意输出的时候要将非String类型 toString 哦,规范一点    System.out.println(age.toString());}catch (Exception e){    System.out.println(e);}

输出:

引用包错误的报错:

用IDEA写代码的时候,可以会遇到奇怪报错,如:

代码本来就没问题,但还是报错了:

这个时候可以看看代码最上面,看看IDEA是不是自动引入了错误的包:

发现有引用错误的包,将其删掉即可,然后再重新在 Method上进行修复:

Runtime.getRuntime.exec 反射

了解 Runtime.getRuntime.exec

Runtime.getRuntime.exec 是Java中执行系统命令的方法

简单使用如下:

byte[] a = new byte[1024];Process cmd = Runtime.getRuntime().exec("whoami");InputStream input = cmd.getInputStream();input.read(a);String res = new String(a);System.out.println(res);

我们来一一分析下,重点就两大块:Process 和 InputStream

Process cmd = Runtime.getRuntime().exec(“whoami”)

首先先看看 Runtime.getRuntime().exec 是什么东西,返回值类型是什么样的:

在手册上查看描述:

可知 exec 函数就是执行系统命令用的

在去看看源码做二次确认

返回类型是 Process 类型,所以我们调用的时候用 Process 类型接收返回值

Process类提供进程输入、输出等进程方法。粗浅的说就是一个进程类

通过文档可以得知,我调用的这个exec方法需要一个String类型的参数,即要执行的系统命令

InputStream input = cmd.getInputStream()

其中:

InputStream    输入流,即数据流入,读入数据

OutputStream 输出流,即数据输出,写入数据

该代码读取上一步 Process 类型的数据流

input.read(a);

在上一步调用getInputStream后,可执行 read 函数。

将当前的数据流读取出来,写入到一个 byte[]类型的变量里。

String res = new String(a);

将byte类型转换成字符串。以便后面打印输出

这就是一个简单的 Java 命令执行并回显结果。

我们可以看到主要调用了 Runtime.getRuntime().exec

那么我们要如何通过反射的方式进行调用呢?

反射调用 Runtime.getRuntime().exec

第一种方式,通过强行反射私有构造方法,用 Runtime 实例化进行反射

这里有一个小坑,Runtime的构造函数是私有的:

所以我们要强制反射私有构造方法,而且不能直接 newInstance Class:

错误写法:

直接用Class来进行实例化

Class runtime = Class.forName("java.lang.Runtime");runtime.newInstance();

会报错:

java.lang.IllegalAccessException: Class org.xiaopan.test.Main can not access a member of class java.lang.Runtime with modifiers “private”

正确写法:

先强制反射Runtime的构造方法,再实例化构造方法。

Class runtime = Class.forName("java.lang.Runtime");Constructor c = runtime.getDeclaredConstructor();c.setAccessible(true);c.newInstance();

反射调用Runtime.gettime.exec

byte[] a = new byte[1024];try{    //获取Runtime类    Class runtime = Class.forName("java.lang.Runtime");    //获取Runtime类构造方法    Constructor c_runtime = runtime.getDeclaredConstructor();    //设置强制反射    c_runtime.setAccessible(true);    //Runtime类的构造方法 实例化   //由于 Runtime类构造方法返回类型为 Runtime类,所以需要使用 Runtime 类型变量进行接收    Runtime r = (Runtime) c_runtime.newInstance();    //获取 Runtime类的方法 exec    Method m = runtime.getMethod("exec", String.class);    //调用 exec 方法,传入对象为 Runtime类的构造方法实例化    //由于 exec 方法返回的是 Process 类型数据,所以需要使用 Process 类型变量进行接收    Process p = (Process) m.invoke(r,"whoami");    //读入数据流,读入到 byte[] 类型的变量中    p.getInputStream().read(a);    System.out.println(new String(a));}catch (Exception e){    System.out.println(e);}

成功输出:

第二种方式,不进行 Runtime实例化,直接通过getRuntime进行反射

注意点:

发现盲点:在本节一开头,调用系统命令函数 exec 的形式如下:

Runtime.getRuntime().exec("whoami");

我们去源码翻翻 getRuntime()是个什么函数

我们可以发现,getRuntime就是为了返回 Runtime类 实例的,感觉应该是一个 单例模式

我们遵守源码的规则,直接调用 getRuntime,拿到 Runtime类实例

注意点:

由于 getRuntime方法 返回的是 Runtime类实例,所以反射的时候需要显示类型转换。

代码如下:

byte[] a = new byte[1024];try{    //获取 Runtime 类    Class runtime = Class.forName("java.lang.Runtime");    //获取getRuntime方法    Method m = runtime.getMethod("getRuntime");    //调用getRuntime方法,并用 Runtime类 类型进行接收,显式转换成 Runtime类    //调用的时候,在上文查看源码时,发现是不需要传入参数的    //不需传入参数,我们直接传一个 null 进去即可    Runtime r = (Runtime) m.invoke(null);    //由于上一个代码中调用 getRuntime 方法,返回了 Runtime类    //我们直接就可以调用底下的 exec 方法了    Process p = r.exec("ifconfig");    //数据输入流,读入数据    InputStream res = p.getInputStream();    res.read(a);    System.out.println(new String(a));}catch (Exception e){    System.out.println(e);}

成功输出:

Referer:

java手册:

https://www.oracle.com/cn/java/technologies/java-se-api-doc.html

大佬文章:

https://blog.csdn.net/ju_362204801/article/details/90578678

精彩推荐

c++ 反射_Java代码审计基础之反射相关推荐

  1. 反射 字段_java核心基础之反射

    前言 大家好,我是 jack xu,今天跟大家介绍核心基础里面的反射,反射这个东西你说它重要也重要,不重要也不重要.重要是当你看一些框架的源码时候,里面会用到反射的代码,你不会是看不懂的.不重要是因为 ...

  2. java为什么要用反射_Java中为什么需要反射?反射要解决什么问题?

    一句话概括就是使用反射可以赋予jvm动态编译的能力,否则类的元数据信息只能用静态编译的方式实现,例如热加载,Tomcat的classloader等等都没法支持 Java中编译类型有两种: 静态编译:在 ...

  3. java代码审计常见漏洞_java代码审计基础教程之V2会议系统多个漏洞集合/无需登录...

    因为学习java并不是很长时间,也没有做深入的研究.但是在学习之后,发现可以审计出一些简单的javaweb漏洞,所以想这这里和大家分享一下.0x01审计之初首先,我拿到了源码之后,大概看了一下这个系统 ...

  4. 反射 数据类型_Java基础:反射机制详解

    一.什么是反射: (1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法.本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取t对 ...

  5. java 反射泛型方法_java基础之反射和泛型以及注解

    java基础之反射和泛型以及注解 泛型擦除 泛型擦除: 泛型只在编译时期有效,编译后的字节码文件中不存在泛型信息. 声明泛型集合,集合两端类型必须一致.类型也可以用包装类型,泛型的类型必须是引用类型, ...

  6. java 反射教程_Java基础教程——反射机制

    Java反射机制 Java反射机制是Java语言的一个重要特性,使得Java语言具备"动态性": 在运行时获取任意一个对象所属的类的相关信息; 在运行时构造任意一个类的对象: 在运 ...

  7. java display.getdefault()_java基础(十一 )-----反射——Java高级开发必须懂的

    本文我们通过一个实际的例子来演示反射在编程中的应用,可能之前大家对反射的学习,仅仅是停留在概念层面,不知道反射究竟应用在哪,所以是一头雾水.相信通过这篇教程,会让你对反射有一个更深层次的认知. 概念 ...

  8. Java反射机制的基本概念与使用_Java进阶之reflection(反射机制)——反射概念与基础...

    反射机制是Java动态性之一,而说到动态性首先得了解动态语言.那么何为动态语言? 一.动态语言 动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化.比如常见 ...

  9. java getinstance 反射_Java 基础篇之反射

    使用反射获取程序运行时的对象和类的真实信息. 获取 Class 对象 每个类被加载之后,系统会为该类生成一个对应的 Class 对象,通过该 Class 对象可以访问到 JVM 中的这个类. 使用 C ...

最新文章

  1. 病人还能生存多久?现在AI能给出更精准的预测
  2. C++ priority_queue用法
  3. 品牌价值越发重要的时代,谁才是服务行业标杆?
  4. sql 增加链接服务器,SQL server利用脚本添加链接服务器,可设置别名
  5. 整理了一下目前的专栏文章,基本可以完整解决普通问题了 - 知乎专栏
  6. python_str 字符串的所有方法
  7. “轻量级的”Istio,微软开源了一个基于 Envoy 的服务网格
  8. 全球搜索引擎盛会在即 呼叫搜索模式呼之欲出
  9. springboot学习笔记-3 整合redismongodb
  10. 一些常用的前端基础操作
  11. xposed框架定位修改怎么用_Android中Xposed框架篇-修改系统位置信息实现自身隐藏功能...
  12. p6spy mysql8_druid数据源集成p6spy踩坑
  13. 使用idea启动vue项目
  14. 别再售卖 5块钱 的 Win10 激活码了,后果很严重
  15. python 节假日_python获取全年节假日
  16. 小米手机业务取得增长,似乎从华为手机在海外受挫中获益
  17. 笔记本电脑f11功能键_电脑的键盘上的F1至F12的功能分别是什么
  18. <2021SC@SDUSC>博客(9)山东大学软件工程应用与实践Jpress代码分析(8)
  19. W05 - 999、云小蜜人工智能训练师
  20. 操系统原理课内实习二

热门文章

  1. LCD RGB 控制技术 时钟篇(下)
  2. 二 关于s5p4418 无线wifi模块出现SDIO读写错误的解决方法
  3. Nginx动静分离-tomcat
  4. 电脑边充电边用好吗_平板电脑充电推车厂家哪家好?
  5. c++调用mysql存储过程_C++中ADO调用MySQL存储过程失败,诡异的语法异常,求解中,附源码...
  6. php接收get数组参数吗,php获取到data参数,如何与数组匹配
  7. CodeDay#7 启动 | 北京欢迎你
  8. 游戏打包过程枯燥且繁琐,如何提升打包效率?
  9. Flink SQL 在字节跳动的优化与实践
  10. 游戏服务器端引擎——DogSE的设计