【Java基础】Java反射机制浅解


反射的某场景说明

应用场景: 当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。(IDEA编辑器对Java代码执行了代码分析)

场景工作过程: 编译器怎么找到class的属性和方法?

  1. 根据实例对象、导入路径找到指定类文件,可能是.java或编译文件.class;
  2. 找到指定文件后,JVM会把类文件加载进内存;
  3. 如果是.class编译文件,则进行反编译;
  4. 创建类文件的实例对象,对静态变量,静态代码块执行初始化操作;(类加载)
  5. 接收获取到的对象信息,并进行相应的处理,比如检测.并列出属性和方法。(反射)

类加载机制: Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,但出于加载效率等方面的考虑,只会加载目前用到的类。有些类因为编译期用不到,所以没有被加载到JVM,等到程序运行过程中需要动态加载某些类。

反射: 根据Oracle官方的解释,反射使Java代码能够发现有关 已加载类的字段、方法和构造函数的信息 ,并在安全限制内使用反射的字段、方法、构造函数 对其底层对应的对象进行操作

一句话概括: 动态获取类信息、动态调用对象方法的功能,被称为Java语言的反射机制。

潜在安全问题: Java反射机制可以无视类方法、变量去访问权限修饰符(如protected、private等),并且可以调用任意类的任意方法、访问和修改成员变量值。

看了很多篇文章,没有找到很好的解释,看来必须结合代码理解。
在这里插入图片描述

其他场景: JavaBean 和 JSP 之间的调用也是通过反射实现的。反射最重要的用途是开发各种通用框架,如 Spring 框架和 ORM 框架,都是通过反射机制实现的。,它是各种容器实现的核心。

对于一般的开发者来说,使用反射技术的频率相对较低。但大部分Java的的应用框架采用反射机制,掌握该机制可以提高代码审计能力。

反射代码

应用代码

常见的代码方式实现增删功能:

String name = request.getParameter("name"); // 获取操作参数
Command command = null;
if(name.equals("Delect")){
	command = new DelectCommand();
}else if (name.equals("Add")){
	command = new addCommand();
}	

使用Java反射重构代码以减少代码行:

String name = request.getParameter("name"); // 获取操作参数
Class CommandClass = Class.forName(name + "Command");
Command command = (Command) CommandClass.newInstance();
command.doAction(request);

要想理解这两段代码,需要看一下本文章的基础知识部分。

运用反射机制代码的第2、3行,其作用如下。

  1. 创建class实例对象,可以先使用 Class x = Class.forName(类名) 获取class类型;
  2. 然后使用 .newInstance()方法 方法创建class的实例对象

直观优势:在程序运行前,程序不知道即将使用的class类。程序根据动态参数创建一个实例对象,更加灵活。

潜在的安全问题:此处代码定义,被实例化的class必须以 Command结尾,但缺少对 name 字段的传入限制,就会允许实例化实现 Command 接口的任何对象,从而导致安全问题。

安全问题

简单理解: 通过Class类创建class实例对象,开发者在编写代码时不再需要把所要实例化的class写死,从而实现 动态创建实例对象 的效果,大大减少代码量。但便利的同时,也导致了实例化对象的不可控,可能会导致安全问题。

思路:构造输入数据 -> 实例化非预期的class -> 调用非预期的方法利用漏洞。

审计:可以考虑通过关键字 Class 寻找相关操作。

总结

如果这就是我所理解的反射机制,那么它实现了 动态选择接口/实例对象及调用方法 的功能,可以根据不同的输入选择不同的实例对象和调用方法。

关于反射和正常创建实例对象的比较如下,记住Class的类是反射的源头。

  • 正常方式:通过完整的类名 > 通过new实例化 > 取得实例化对象
  • 反射方式:传入类名,获取Class类型 > 创建实例对象

基础知识

类、实例、Class实例(第2行代码)

Class文件: 在包 java.lang 下,有一个Class.java文件,是一个实实在在的类,Class对象就是这个Class类的实例。它被用来描述所有类的信息,内部可以记录类的成员、接口等信息。

java提供了下面几种获取到类的Class对象的方法:

  1. 利用对象实例调用getClass()方法获取该对象的Class实例;
  2. 使用Class类的静态方法forName(“包名+类名”),用类的名字获取一个Class实例;
  3. 运用 类名.class 的方式来获取Class实例。

类加载器为每个实例对象生成一个Class描述对象

  1. 编译器为每个.java文件生成编译文件.class,即JVM可以加载执行的字节码
  2. JVM执行.class代码,运行程序

运行时期间,当我们需要实例化任何一个类时,JVM会首先尝试看看在内存中是否有这个类,如果有,那么会直接创建类实例;

如果没有,那么就会根据类名去加载这个类。

当加载一个类,或者当加载器(class loader)的defineClass()被JVM调用,便会为这个类产生一个Class对象,用来表达这个类,该类的所有实例都共同拥有着这个Class对象,而且是唯一的。

参考

《Java反射机制(创建Class对象的三种方式)》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值