什么是反射和反射机制
反射其实就是把java类中的各个成分映射成一个个java对象。
即在运行状态中:
1.对于任意一个类,能够知道这个类的所有属性和方法。
2.对于任意一个对象,都能够调用它的任意一个属性和方法。
这种动态获取信息及动态调用对象的方法的功能就叫反射机制。
PS:java中存在一个全局类,名字叫Class的类(常说的class是一个修饰类的关键字,这里说的Class是一个类名,不是关键字),这个Class类包含了所有的实例对象,通过操作这个Class类,我们就能够操控任意类。
为什么Java会有反射?
每一个事物的产生都有它的应用场景。在看到上面的定义的时候,初次学习反射的同学肯定是一头雾水的。
这里抛开概念,看看在不使用反射的时候是怎么写程序的。

那如果用反射怎么写呢?如下:

可以看到,代码量显著变大。但这里就有一个好处了,可以看到,我们一方面没有import这个User这个类,另一方面我们是用字符串来传入User这个类的,因此如果把com.User.User这个字符串放在外部,由传参或者配置文件给程序的话,程序就可以动态传入执行了。
Java程序在运行起来的时候,首先是经过javac编译成class字节码,然后再由jvm去解析执行。java还是一门静态类型语言,变量的类型在编译之前就需要提前确定,不然编译就通不过。这里就现在了程序的灵活性。比如上面的程序,实例化对象的类型是User,如果我们想改成User的子类WOMEN,就需要改代码重新编译。
介于静态语言的以上问题,很多静态语言就扩展出了反射机制,能够动态获取信息及动态调用对象方法。
反射API
java.lang.Class类
这个Class类其实就是前面说的,由JVM在类加载的时候自动创建的,这个类的实例保存了对于类中的方法和属性等信息。
一般反射时进行的操作是获取要反射类的Class对象,从而获取类型元数据(metadata),比如字段、属性、构造器、方法等,获取后并调用。
- forName方法可以根据类的完全限定名称获取Class对象。会加载和连接,根据initialize参数决定是否初始化。(我们常用的不指定initialize就是默认初始化)
- newInstance 创建此Class对象表示的类的新实例 内部实现是调用的Constructor.newInstance方法
- getClasses 获取在此Class对象对应的类型中声明的public类或接口成员的Class对象数组,包括从超类继承的public类和接口成员
- getDeclaredClasses 获取在此Class对象对应的类型中声明的类或接口成员的Class对象数组,包括public, protected, default (package) access,private类和接口,但不包括继承的类和接口
- getFields 获取此Class对象表示的类或接口的所有可访问public字段Field数组 包括继承的
- getDeclaredFields 获取此Class对象表示的类或接口的所有 public、protected、default(package)access和private字段Field数组 不包括继承的
- getField 获取此Class对象表示的类或接口的指定的可访问public字段Field对象 会递归向父类、父接口查
- getDeclaredField 获取此Class对象表示的类或接口的指定的public、protected、default(package)access和private字段Field对象 不包括继承的
- getMethods 获取该class对象表示的类或接口的所有public方法Method数组 包括继承的
- getDeclaredMethods 获取该class对象表示的类或接口的public, protected, default (package) access,private方法Method数组 不包括继承的
- getMethod 获取该class对象表示的类或接口的指定的public方法Method数组 会递归向父类、父接口查
- getDeclaredMethod 获取该class对象表示的类或接口的指定的public方法Method数组 不包括继承的
- getConstructors 获取这个class对象表示的类的所有public构造函数Constructor数组
- getDeclaredConstructors 获取这个class对象表示的类的所有public、protected、default(package)access和private 构造函数Constructor数组
- getConstructor 获取这个class对象表示的类的指定的public构造函数Constructor对象
- getDeclaredConstructor 获取这个class对象表示的类的指定的public、protected、default(package)access和private 构造函数Constructor对象
java.lang.reflect 包
java.lang.reflect 包提供了反射中用到类,主要的类说明如下:
Constructor
获取Class对象中的构造方法
- getConstructor 获取当前类或父类单个公共构造方法
- getConstructors 获取当前类或父类所有公共构造方法
- getDeclaredConstructor 获取当前类单个构造方法
- getDeclaredConstructors 获取当前类所有构造方法
Method
获取Class对象中的方法
- getMethod 获取当前类或父类单个公共方法
- getMethods 获取当前类或父类所有公共方法
- getDeclaredMethod 获取当前类单个方法
- getDeclaredMethods 获取当前类所有方法
- invoke 执行指定对象 obj 中的Method
Field
获取Class对象中的成员属性
- getField 获取单个当前类或父类的public变量
- getFields 获取当前类或父类的所有public变量
- getDeclaredField 获取单个当前类的public、private、protected变量
- getDeclaredFields 获取所有当前类的public、private、protected变量
使用反射
Class对象获取
首先,我们需要获取目标对象的一个Class对象。
Class这个类的对象(实例)的获取方式主要有4种:
PS:通常只用Class.forName。

- 第一种方法是通过类的全路径字符串获取 Class 对象,不需要事先import导入
- 第二种方法有限制条件:需要导入类的包;
- 第三种方法已经有了 new了对象,不再需要反射。多此一举。
- 第四种方法首先要获取个classLoader实例,之后再通过字符串获取Class对象。
Class对象实例获取
在成功获取到Class对象后,就需要操作这个对象,从而实现我们的目的了。而这首先就需要去获取对象的实例。
但在创建实例的时,由于类可能存在多个构造方法,因此在反射中针对不同的构造方法来创建实例的方式也是不一样的。
如图,目标类存在3种构造方法

公有无参构造方法
Class.newInstance()
公有含参构造方法(也可以调用无参构造器)
看这个方法名,就不难看出,这个是专门获取构造器的一个方法。因此它不局限于含参构造器。
Constructor Class.getConstructor(Class<?>... parameterTypes)
Constructor[] getConstructors();
私有含参构造方法(也可以调用公有构造方法)
需要注意的是:调用非public的Constructor时,必须首先通过setAccessible(true)设置私有变为公有。并且setAccessible(true)可能会失败。
Constructor getDeclaredConstructor(Class<?>... parameterTypes);
Constructor[] getDeclaredConstructors();
Class对象成员方法获取
- Method getMethod(name, Class…):获取某个public的Method(包括父类)
- Method getDeclaredMethod(name, Class…):获取当前类的某个Method(不包括父类)
- Method[] getMethods():获取所有public的Method(包括父类)
- Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
获取Method对象之后,可以调用如下函数:
1.getName():返回方法名称,例如:”getScore”;
2.getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
3.getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
4.getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
5.invoke(object,new Object[]{}) 调用执行方法
6.setAccessible() 设置private函数为public属性
获取公有成员方法
Method getMethod(name, Class…) //获取指定的方法
Method[] getMethods() //获取所有公有成员方法

获取私有方法
Method getDeclaredMethod(name, Class…)
Method[] getDeclaredMethods() 
Class对象成员变量获取
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
可以通过上面的四种方法获取成员变量对象,下面就是要对成员变量对象做一些操作,目前支持的函数为
- 1.getName():返回字段名称,例如,”name”;
- 2.getType():返回字段类型,也是一个Class实例,例如,String.class;
- 3.getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
- 4.setAccessible(): 设置变量为public
- 5.set(): 设置字段值
- 6.get():获取字段值
针对如图例子进行测试:

获取公有变量
Field Class.getField(name) //获取指定的公有变量
Field[] Class.getFields()//获取所有公有变量
获取私有变量
当目标成员变量为私有变量(private)时无法通过上述方法获取,需使用 getDeclaredFields或getDeclaredField获取目标私有成员变量
并且访问私有成员变量时需要将setAccessible设置为true,关闭安全检查后才可获取私有成员变量内容
注:getDeclaredFields或getDeclaredField并不是只用来获取private变量,public变量同样也获取到
getDeclaredField和getField区别
- getDeclaredField只可获取类本身 public private protected成员属性
- getField可获取类本身及父类的public成员属性
PS:用起来其实就是多了个setAccessible操作,其他都是和正常的一样的


获取父类私有变量(方法也可以用这个思路去获取)
那么这里问题来了,如果我们想要获取父类的私有方法怎么办呢?其实解决方法比较简单,就是通过反射获取父类对应的Class对象。
Class<?> superclass = sonClass.getSuperclass();获取superclass对象后,按照需求调用以下函数
1.Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
2.Method getDeclaredMethod(name, Class…):获取当前类的某个Method(不包括父类)
3.setAccessible(): 设置变量为public
其中setAccessible是必须被调用的为了修改其私有属性为public


小例子:利用反射执行Runtime
主要思路就是获取Class对象然后使用java.lang.reflect里提供的方法操作Class对象。
这里我们以java.lang.runtime为例。首先我们需要知道runtime有什么方法、是什么属性(公有私有)然后才能往下继续利用。
首先第一步是需要获取Class对象,即需要找到构造函数,构造实例,才能调用其他方法。


从源码我们可以看到,构造方法是私有方法,exec是公有方法。因此我们可以使用如下代码来利用反射执行命令。

再或者简短一点,因为Runtime类存在一个公有的getRuntime方法,能直接返回实例给我们。所以我们可以利用getRuntime这个方法直接一步到位,这样就跳过了私有构造器的获取。
