反射相关概念

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

反射机制流程图(或可直接移步)

class对象获取

对于普通用户我们可以采用以下方法创建实例:

Person test = new Person();

而我们在创建class类的实例对象却不能使用上述方法,运行会抛出错误

Class test = new Class();

因为其构造器是私有的,只有JVM能够创建Class对象

获取办法

1、类的.class属性

第一种就是最简单明了的方式,我们可以通过类名的属性class获取。

Class c1=ReflectDemo.class;

2、实例化对象的getClass()方法

第二种我们可以先实例化一个对象,之后在调用getClass()方法。

ReflectDemo demo2= new ReflectDemo();
Class c2 = demo2.getClass();

3、Class.forName(String className):动态加载类

第三种则是调用Class类中的forName方法,将字节码文件加载进内存,返回Class对象。

Class c3 = Class.forName("reflectdemo.ReflectDemo");

注意点

我们一般使用第三种通过Class.forName方法去动态加载类。且使用forName就不需要import导入其他类,可以加载我们任意的类。

而使用类.class属性,需要导入类的包,依赖性太强,在大型项目中容易抛出编译错误;

而使用实例化对象的getClass()方法,需要本身创建一个对象,本身就没有了使用反射机制意义。

所以我们在获取class对象中,一般使用Class.forName方法去获取。

获取数组类型的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对象了,反射调用内部类的时候需要使用$来代替.,如com.anbai.Test类有一个叫做Hello的内部类,那么调用的时候就应该将类名写成:com.anbai.Test$Hello

获取成员

获得上一个class对象后,可通过如下方法获得成员变量Field:

Field[] getFields() :获取所有public修饰的成员变量

Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符

Field getField(String name) 获取指定名称的 public修饰的成员变量

Field getDeclaredField(String name) 获取指定的成员变量

成员方法Method,构造函数Constructor也是类似的

获取到java.lang.reflect.Method对象以后我们可以通过Methodinvoke方法来调用类方法。

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

获取成员变量值:

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

修改成员变量值:

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

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

如果我们需要修改被final关键字修饰的成员变量,那么我们需要先修改方法

// 反射获取Field类的modifiers
Field modifiers = field.getClass().getDeclaredField("modifiers");// 设置modifiers修改权限
modifiers.setAccessible(true);// 修改成员变量的Field对象的modifiers值
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);// 修改成员变量值
field.set(类实例对象, 修改后的值);

反射创建类对象

一般我们使用Class对象的newInstance()方法来进行创建类对象。

使用的方式也特别简单,只需要通过forname方法获取到的class对象中进行newInstance方法创建即可。

Class c = Class.forName("com.reflect.MethodTest"); // 创建Class对象Object m1 =  c.newInstance(); // 创建类对象

一个eg:

import java.lang.reflect.Method;
public class ReflectTest {public void reflectMethod() {System.out.println("反射测试成功!!!");}public static void main(String[] args) {try {Class c = Class.forName("com.reflect.ReflectTest"); // 创建Class对象Object m = c.newInstance(); // 创建类实例对象Method method = c.getMethod("reflectMethod"); // 获取reflectMethod方法method.invoke(m); // 调用类实例对象方法} catch (Exception e) {e.printStackTrace();}}
}

注意点

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

invoke方法第一个参数并不是固定的:

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

如果调用这个方法是静态方法,第一个参数就是类;

反射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"));

为何要设置accessible?

在Java的任何一个类都必须有一个或多个构造方法,如果代码中没有创建构造方法那么在类编译的时候会自动创建一个无参数的构造方法。

public class Runtime {  /** Don't let anyone else instantiate this class */ private Runtime() {}}

从上面的Runtime类代码注释我们看到它本身是不希望除了其自身的任何人去创建该类实例的,因为这是一个私有的类构造方法,所以我们没办法new一个Runtime类实例即不能使用Runtime rt = new Runtime();的方式创建Runtime对象,但借助反射机制,修改方法访问权限从而能间接的创建出了Runtime对象。

runtimeClass1.getDeclaredConstructorruntimeClass1.getConstructor都可以获取到类构造方法,区别在于后者无法获取到私有方法,所以一般在获取某个类的构造方法时候我们会使用前者去获取构造方法。如果构造方法有一个或多个参数的情况下我们应该在获取构造方法时候传入对应的参数类型数组,如:clazz.getDeclaredConstructor(String.class, String.class)

如果我们想获取类的所有构造方法可以使用:clazz.getDeclaredConstructors来获取一个Constructor数组。

获取到Constructor以后我们可以通过constructor.newInstance()来创建类实例,同理如果有参数的情况下我们应该传入对应的参数值,如:constructor.newInstance("admin", "123456")。当我们没有访问构造方法权限时我们应该调用constructor.setAccessible(true)修改访问权限就可以成功的创建出类实例了。

若不执行私有方法?

Runtime.getRuntime().exec(String command)//重载Runtime.exec

反射机制方法

Java安全(二) 反射相关推荐

  1. java 反射应用_java反射(二)--反射应用案例

    一.反射实例化对象 经过一系列的分析之后发现虽然可以获取Class类的实例化对象,但是依然觉得这个对象的获取意义不是很大,因此可以通过以下几个案例去理解反射的核心意义 --反射实例化对象:获取Clas ...

  2. 浅说Java中的反射机制(一)

    在学习传智播客李勇老师的JDBC系列时,会出现反射的概念,由于又是第一次见,不免感到陌生.所以再次在博客园找到一篇文章,先记录如下: 引用自java中的反射机制,作者bingoideas.(()为我手 ...

  3. java 有哪些反射机制_Java 的反射机制你了解多少?

    不知道多少次听说过了Java反射机制的使用,比如:Spring 框架如何实例化IoC容器中的Bean,编码过程中如何动态的清理对象中的字段信息等等.工作中只是听说.看同事们编码实践,但是自己却只是概念 ...

  4. java swing jbutton_Java 反射

    点击上方"凌天实验室","星标或置顶公众号" 漏洞.技术还是其他,我都想第一时间和你分享 1 前  言 本章为新手向零基础 Java 反射学习笔记. 截取部分本 ...

  5. JAVA注解和反射(笔记)

    注解简介 Annotation是从JDK5.0开始引入的新技术). Annotation的作用 : 不是程序本身(可以对程序作出解释.(这一点和注释(comment)没什么区别) 可以被其他程序(比如 ...

  6. 反射-获取java私有内部类反射类型、私有字段

    获取JAVA私有内部类反射类型 方式一 Class.forName("外部类完整路径$内部私有类类名"); 方式二 通过获取对应私有内部类的字段而获取 完整的类名 Class.fo ...

  7. Java 高级特性 --- 反射

    From:Java 高级特性 --- 反射:https://www.jianshu.com/p/9be58ee20dee From:Java 基础之 --- 反射(非常重要):https://blog ...

  8. java cookbook 3_CookBook/Java核心/3-Java反射.md at master · zhgdbut/CookBook · GitHub

    #Java核心(三)反射 Java反射给我们提供了在运行时检查甚至修改应用行为的机制. 反射是java高级的核心技术,所有有经验的程序员都应该理解. 通过反射机制,我们可以在运行时检视 类.接口.枚举 ...

  9. JAVA中的反射机制和模块化

    目录 一.类加载 1.1类加载描述 1.2类的加载 1.3类的连接 1.4类的初始化 1.4.1类初始化的作用 1.4.2初始化步骤 1.4.3类的初始化时机 二.反射 2.1反射的概述 2.2获取C ...

最新文章

  1. 公司inur php id_,PHP——个人信息管理系统
  2. github如何make contribute to 其它开源项目
  3. Apache RocketMQ在linux上的常用命令
  4. git上传分支的原理_几张图让你彻底弄懂git工作流(二) ——git分支
  5. php图片长宽处理,用php调整图片宽高 | 学步园
  6. 【Flink】Flink SQL 自定义 Source format
  7. java json float_java – Json解析问题(值自动更改为float)
  8. C#程序打包安装部署之创建快捷方式
  9. Codeforces Round #442 (Div. 2) D. Olya and Energy Drinks
  10. Ubunu16.04安装CPU版本Tensorflow
  11. (二)零基础入门C语言 --- C语言之入门课程
  12. 产品选型“神器” TIA Selection Tools 之选择 S7-1500T 全程详解
  13. Win7 下替代NetMeeting的屏幕共享工具 InletexEMC
  14. IMDB 2003.07.12 最新排名
  15. 监控软件Zabbix安装使用
  16. 云桌面终端CT3200,硬件与信号连接
  17. 23计算机考研人第一周反思总结
  18. JVM 运行时内存空间详解——元空间
  19. Kali Linux 2016.1 x86 x64 安装 wine QQ
  20. 重构手法之重新组织函数

热门文章

  1. 战列舰机械计算机,问个事,二战时期的战列舰可以装备垂直稳定仪么?
  2. FPGA自学之路1(半加器和全加器的实现)
  3. Android 性能优化四个方面总结
  4. EasyExcel表头校验,表内容校验
  5. win10系统脚本批处理开启和停止服务器,批处理命令一键彻底关闭WIN10自动更新...
  6. 解决scalac Error: bad option -make:transitive
  7. MFC ODBC数据库操作编程(二)
  8. 应用宝成腾讯王卡官方应用商店 全部APP下载免流量
  9. 解密街头立体画的创作过程
  10. AT命令交互之-COPS选择营运商