Java基础Java反射机制浅解
Posted 南瓜__pumpkin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础Java反射机制浅解相关的知识,希望对你有一定的参考价值。
反射的某场景说明
应用场景: 当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。(IDEA编辑器对Java代码执行了代码分析)
场景工作过程: 编译器怎么找到class的属性和方法?
- 根据实例对象、导入路径找到指定类文件,可能是.java或编译文件.class;
- 找到指定文件后,JVM会把类文件加载进内存;
- 如果是.class编译文件,则进行反编译;
- 创建类文件的实例对象,对静态变量,静态代码块执行初始化操作;(类加载)
- 接收获取到的对象信息,并进行相应的处理,比如检测.并列出属性和方法。(反射)
类加载机制: 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行,其作用如下。
- 创建class实例对象,可以先使用
Class x = Class.forName(类名)
获取class类型; - 然后使用
.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对象的方法:
- 利用对象实例调用getClass()方法获取该对象的Class实例;
- 使用Class类的静态方法forName(“包名+类名”),用类的名字获取一个Class实例;
- 运用 类名.class 的方式来获取Class实例。
类加载器为每个实例对象生成一个Class描述对象
- 编译器为每个.java文件生成编译文件.class,即JVM可以加载执行的字节码
- JVM执行.class代码,运行程序
运行时期间,当我们需要实例化任何一个类时,JVM会首先尝试看看在内存中是否有这个类,如果有,那么会直接创建类实例;
如果没有,那么就会根据类名去加载这个类。
当加载一个类,或者当加载器(class loader)的defineClass()被JVM调用,便会为这个类产生一个Class对象,用来表达这个类,该类的所有实例都共同拥有着这个Class对象,而且是唯一的。
参考
以上是关于Java基础Java反射机制浅解的主要内容,如果未能解决你的问题,请参考以下文章