对于众多java应用程序员来说,java反射也许是个即熟悉又陌生的东西,说熟悉是经常会听到“反射”这个字眼,说陌生是因为写应用程序并不怎么直接跟反射打交道,更多地用到反射技术的往往是一些稍底层一点的东西,像一些框架之类的。那么什么是反射呢?
先来看看官方的说法
Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine.
反射是由应用程序使用,用于检查或修改在Java虚拟机中运行的应用程序的运行时行为。
这个说法感觉还是很抽象的,我查了一些资料,结合对API的理解,用自己的话来解释一下什么是反射(Reflection)。反射是一种编程语言的能力,或者通俗地讲它是一种技术,什么样的技术呢?它可以在程序运行的时候获取类的构造器、方法、属性等元素,并可以赋值或调用类的方法。(貌似只有java才有这种能力,其它语言都没有,这是我猜的 - 参考),那么这种能力有什么用呢?
我认为是为了我们的程序更具灵活性,比如在使用运行时注解时就要用到反射来检查类或类的元素有没添加注解,如果添加了,添加的是什么注解。然后再根据检查结果作出不同的处理。
除了运行时注解,反射还被用在动态代理上,以及无法通过new来创建对象的场合。
下面来看看在代码中怎么使用。通常情况下跟反射相关的类有以下几个:
java.lang.Class
java.lang.reflect.Constructor
java.lang.reflect.Field
java.lang.reflect.Method
如果涉及到泛型和数组,还会用到Type接口和Array类,这两个东西会复杂点。
java.lang.reflect.Type
java.lang.reflect.Array
先来看看Class类,Class类是一切反射操作的入口,无论是想要获取一个类的构造器,还是类的属性,或是一个类的方法都要先有这个类的Class实例,获取Class实例的方法通常有以下几种:
// 一 :通过getClass()方法
String str="hello world";
Class> clazz1 = str.getClass();
// 二:通过类的class属性
Class> clazz2 = String.class;
// 三:通过Class类的静态方法forName(String className)
Class> clazz3 = Class.forName("java.lang.String");
有了Class实例,就像演登上了舞台,可以尽情地表演了,看几个常用的api:
跟构造器相关的:
Constructor getConstructor(Class>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
Constructor>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
Constructor>[] getDeclaredConstructors()
返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
跟成员变量相关的:
Field getDeclaredField(String name)
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
Field[] getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
Field getField(String name)
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
Field[] getFields()
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
跟方法相关的:
Method getDeclaredMethod(String name, Class>... parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Method getMethod(String name, Class>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
API就不多列了,下面写个例子来感受一下,先写个类,私有公有的字段、构造方法、方法,以及带参数不带参数的方法,这里没有涉及到泛型和数组,今后有空的话再补上:
/**
* 用于理解反射的类
*
* @author Huangming 2019/6/22
*/
public class TestReflection {
private String privateField;
public String publicField;
private TestReflection() {
System.out.println("I am from private construct");
}
public TestReflection(String a, String b) {
System.out.println("I am from public construct");
System.out.println("参数是:{" + a + ", " + b + "}");
this.privateField = a;
this.publicField = b;
}
private void privateMethod() {
System.out.println("I am from private method");
}
public void publicMethod(String s, int i) {
System.out.println("I am from public method");
System.out.println("参数是:{" + s + "," + i + "}");
}
}
接下来编写工具类,反射的相关操作就写在util类里面,
/**
* 反射相关操作的类
*
* @author Huangming 2019/6/22
*/
public class ReflectionUtil {
public Object parseConstructor(Class clazz) {
Constructor[] constructors = clazz.getDeclaredConstructors();
Object instance = null;
try {
for (int i = 0; i < constructors.length; i++) {
Constructor constructor = constructors[i];
int modifier = constructor.getModifiers();
System.out.println("构造方法 " + constructor.getName() + " 的修饰符是" + Modifier.toString(modifier));
if (!constructor.isAccessible()) {
// 注意,isAccessible()的返回值并不代表是否可访问,具体看setAccessible(boolean flag)源码上的注释
constructor.setAccessible(true);
}
// 取构造函数的参数
Class[] parameterTypes = constructor.getParameterTypes();
for (int m = 0; m < parameterTypes.length; m++) {
Class p = parameterTypes[m];
System.out.println("它的第" + (m + 1) + "个参数类型为" + p.getName());
}
if (parameterTypes.length == 0) {
// 无参构造函数
System.out.println("它没有参数");
System.out.println("使用反射调用无参构造函数。。");
constructor.newInstance();
} else {
System.out.println("使用反射调用有参构造函数。。");
// 这里省略了创建参数的步骤(包括参数类型、参数个数的判断)
instance = constructor.newInstance("a", "b");
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return instance;
}
public void parseFields(Object object, Class clazz) {
Field[] fields = clazz.getDeclaredFields();
try {
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
int modifier = field.getModifiers();
System.out.println("成员变量 " + field.getName() + " 的修饰符是" + Modifier.toString(modifier));
if (!field.isAccessible()) {
// 注意,isAccessible()的返回值并不代表是否可访问,具体看setAccessible(boolean flag)源码上的注释
field.setAccessible(true);
}
System.out.println("用反射读取成员变量的值:" + field.get(object).toString());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public void parseMethods(Object object, Class clazz) {
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
int modifier = method.getModifiers();
System.out.println("方法 " + method.getName() + " 的修饰符是" + Modifier.toString(modifier));
if (!method.isAccessible()) {
// 注意,isAccessible()的返回值并不代表是否可访问,具体看setAccessible(boolean flag)源码上的注释
method.setAccessible(true);
}
// 取方法的参数
Class[] parameterTypes = method.getParameterTypes();
for (int m = 0; m < parameterTypes.length; m++) {
Class p = parameterTypes[m];
System.out.println("它的第" + (m + 1) + "个参数类型为" + p.getName());
}
System.out.println("用反射调用该方法");
if (parameterTypes.length == 0) {
try {
method.invoke(object, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} else {
try {
// 这里省略了创建参数的步骤(包括参数类型、参数个数的判断)
method.invoke(object, "abc", 999);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
最后写测试类:
/**
* @author Huangming 2019/6/19
*/
public class Main {
public static void main(String[] args) {
try {
Class c = Class.forName("com.example.proxyinjava.TestReflection");
ReflectionUtil util = new ReflectionUtil();
Object instance = util.parseConstructor(c);
util.parseFields(instance, c);
util.parseMethods(instance, c);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果:
构造方法 com.example.proxyinjava.TestReflection 的修饰符是private
它没有参数
使用反射调用无参构造函数。。
I am from private construct
构造方法 com.example.proxyinjava.TestReflection 的修饰符是public
它的第1个参数类型为java.lang.String
它的第2个参数类型为java.lang.String
使用反射调用有参构造函数。。
I am from public construct
参数是:{a, b}
成员变量 privateField 的修饰符是private
用反射读取成员变量的值:a
成员变量 publicField 的修饰符是public
用反射读取成员变量的值:b
方法 privateMethod 的修饰符是private
用反射调用该方法
I am from private method
方法 publicMethod 的修饰符是public
它的第1个参数类型为java.lang.String
它的第2个参数类型为int
用反射调用该方法
I am from public method
参数是:{abc,999}
反射相关的api并不是太难,却是很重要的一个工具,了解了它的使用,对今后的注解等其它内容会更容易理解。知识都是串起来的,如果哪天读某个框架的源码时发现用到了反射,你要是不理解的话,那么对那个框架的理解也会大打折扣。我的感悟,不要小看了基础的东西。
由于水平有限,如果文中存在错误之处,请大家批评指正,欢迎大家一起来分享、探讨!
博客:http://blog.csdn.net/MingHuang2017
GitHub:https://github.com/MingHuang1024
Email: [email protected]
微信:724360018