反射概述
反射是指对于任何一个Class类,在”运行的时候”都可以直接得到这个类的全部成分。
在运行时,可以直接得到这个类的构造器对象:
Constructor
在运行时,可以直接得到这个类的成员变量对象:
Field
在运行时,可以直接得到这个类的成员方法对象:
Method
这种运行时 动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。
反射的关键
反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。
1 | HelloWorld.java -> javac ->HelloWorld.class |
1、反射的基本作用、关键?
- 反射是在运行时候获取类的字节码文件对象: 然后可以解析类中的全部成分。
- 反射的核心思想和关键就是: 得到编译后得class文件对象。
反射获取类对象
获取Class对象的方式:
- Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
- 类名.class:通过类名的属性class获取
- 对象.getClass():getClass()方法在Object类中定义着
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个
Class对象功能:
获取功能:
1.获取成员变量
Field[] getFields()
Field getField(String name)
Field[] getDeclareFields()
Field getDeclareField(String name)
2.获取构造方法
Constructor<?>[] getConstructors()
Constructor
Constructor
Constructor<?>[] getDeclareConstructors()
3.获取成员方法
Method[] getMethods()
Method getMethod(String name,类<?>… parameterTypes)
Method[] getDeclareMethods()
Method getDeclareMethod(String name,类<?>… parameterTypes)
4.获取类名
String getName()
getMethod
获取类的⽅法:
forName
实例化类对象的⽅法:
newInstance
获取函数的⽅法:
getMethod
执⾏函数的方法:
invoke
Class.forName
如果你知道某个类的名字,想获取到这个类,就可以使⽤ forName
来获取
如果直接执行这段代码的话是错误的,因为Runtime 类的构造方法是私有的
1 | Class clazz = Class.forName("java.lang.Runtime"); |
只能通过 Runtime.getRuntime() 来获取到 Runtime 对象
1 | package Le1a; |
利用反射获取构造器对象
getConstructor
getConstructor 接收的参数是构造函数列表类型,因为构造函数也支持重载, 所以必须用参数列表类型才能唯一确定一个构造函数
public ProcessBuilder(List
获取到构造函数后,我们使用 newInstance 来执行。 比如,我们常用的另一种执行命令的方式ProcessBuilder,我们使用反射来获取其构造函数,然后调用 start() 来执行命令:
1 | Class clazz = Class.forName("java.lang.ProcessBuilder"); |
public ProcessBuilder(String… command)
这个构造函数用到了可变长参数(varargs),在编译的时候会编译成一个数组,如下两种写法在底层是等价的
1 | public void hello(String[] names) {} |
String[].class 传给 getConstructor ,获取 ProcessBuilder 的第二种构造函数:
1 | Class clazz = Class.forName("java.lang.ProcessBuilder"); clazz.getConstructor(String[].class) |
在调用 newInstance 的时候,因为这个函数本身接收的是一个可变长参数,我们传给 ProcessBuilder 的也是一个可变长参数,二者叠加为一个二维数组,所以整个Payload如下:
1 | Class clazz = Class.forName("java.lang.ProcessBuilder"); |
getDeclared
如果一个方法或构造方法是私有方法,就要用到getDeclared
系列的反射,通过setAccessible(true)
暴力反射来破坏封装性
1 | package Le1a; |