前言
Java 反射是Java语言的一个很重要的特征,它使得Java具体了“动态性”。 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。
Java 反射机制主要提供了以下功能:在运行时判断任意一个对象所属的类。在运行时构造任意一个类的对象。在运行时判断任意一个类所具有的成员变量和方法。 在运行时调用任意一个对象的方法。
Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods 的所有信息,并可于运行时改变fields内容或调用methods。
一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。 尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在 Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的 class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透 class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
Java反射机制API介绍
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
①:Class类:代表一个类。【注:这个Class类进行继承了Object,比较特别】
②:Field 类:代表类的成员变量(成员变量也称为类的属性)。
③:Method类:代表类的方法。
④:Constructor 类:代表类的构造方法。
<一>简单演示
要想使用反射机制,首先得获得需要处理的类或者对象的Class对象。我们有如下3中获取方式
①:使用Class的静态方法forName(): 例如:Class.forName("java.lang.Class");
②:使用XXX.Class语法: 例如:String.Class;
③:使用具体某个对象.getClass()方法: 例如String str="abc"; Class<?> tClass=str.getClass();
package reflection;
import java.lang.reflect.Method;
/**
* @Description:使用反射来获取Class中的方法,包括私有的方法
*/
public class Reflection1 {
public static void main(String[] args) {
Class<?> classType = Class.class;
Method[] methods = classType.getDeclaredMethods();
//获取Class类的所有方法
for(int i=0; i<methods.length; i++) {
System.out.println(methods[i]);
}
}
}
<二>Class类的常用方法,如下:
①: getName():获得类的完整名字。
②: getFields():获得类的public类型的属性。
③: getDeclaredFields():获得类的所有属性。
④: getMethods():获得类的public类型的方法。
⑤: getDeclaredMethods():获得类的所有方法。
⑥:getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字parameterTypes参数指定方法的参数类型。
⑦:getConstructors():获得类的public类型的构造方法。
⑧:getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
⑨:newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
先看上面的⑧和⑨其中都能生成对象,但是因为构造函数有无参和有参构造函数两种,所以我们分两种情况考虑
情况一:无参构造方法
<a>首先我们去获取Class对象,然后直接通过Class对象去调用newInstance()方法
Class<?> classType = Reflection1.class;
Object object = classType.newInstance();
<b>首先我们也是去获取Class对象,然后去去调用getConstructor()得到Constructor对象,接着直接调用newInstance()即可
Class<?> classType = Reflection2.class;
// Object reflection2 = classType.newInstance();
Constructor<?> constructor = classType.getConstructor(newClass[] {});
Object reflection2 = constructor.newInstance(newObject[]{});
情况二:有参构造方法
Class<?> classType = Person.class;
Constructor cons = classType.getConstructor(newClass[]{String.class,int.class});
Object obj = cons.newInstance(newObject[]{“zhangsan”, 19});
<三>利用反射机制执行目标类的方法
package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* @Description:使用反射访问类中的方法
*/
public class Reflection2 {
public int sum(inta, int b) {
return a + b ;
}
public static void main(String[] args) throws Exception {
Class<?> classType = Reflection2.class;
//下面两行代码等同 Object object = classType.newInstance();
Constructor<?> constructor = classType.getConstructor(newClass[]{});
Object reflection2 = constructor.newInstance(newObject[]{});
//利用反射机制反射出类的方法
Method method = classType.getMethod("sum",new Class[]{int.class,int.class});
//动态执行method方法,invoke方法返回Object对象,如method返回int型,则invoke
//会将int转换成Integer对象
Object result = method.invoke(reflection2,new Object[]{6,10});
System.out.println(result);
}
}
由于Java访问权限检查机制,私有变量和方法除了在本类中的其他地方都不能访问。可是我们利用反射机制可以绕过访问权限的检查。 具体的实现需要使用reflect包中的AccessibleObject类中的setAccessible方法。实际上类Constructor,Field,Method继承了AccessibleObject类。
package reflection;
class Test {
private String addStr(String str) {
return "This is the " + str;
}
}
package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* @Description:使用反射访问类中的私有方法
*/
public class Reflection3 {
public static void main(String[] args) throws Exception {
Test test =new Test();
Class<?> classTest = test.getClass();
Method addStrMethod = classTest.getDeclaredMethod("addStr",new Class[]{String.class});
//设定是否对addStr方法的访问权限进行检查,true为不检查
addStrMethod.setAccessible(true);
Object result = addStrMethod.invoke(test,new Object[]{"反射执行私有方法"});
System.out.println(result);
}
}
下面我们来看看数组的反射使用,java.lang.Array 类提供了动态创建和访问数组元素的各静态方法。
Class classType = Class.forName("java.lang.String");
Object array = Array.newInstance(String.class,3);
Array.set(array,1, "第一个元素");
System.out.println(Array.get(array,1));