关于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. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.

大概意思是:反射技术通常被用来检测和改变应用程序在 Java 虚拟机中的行为表现。它是一个相对而言比较高级的技术,通常它应用的前提是开发者本身对于 Java 语言特性有很强的理解的基础上。值得说明的是,反射是一种强有力的技术特性,因此可以使得应用程序突破一些藩篱,执行一些常规手段无法企及的目的。

通俗概括一下:反射是个很牛逼的功能,能够在程序运行时修改程序的行为。但反射是非常规手段,反射有风险,应用需谨慎。

Class

因为 Java 是面向对象的语言,基本上是以类为基础构造了整个程序系统;

类的规格说明书:反射中要求提供的规格说明书, Class;

Class 就是一个对象,它用来代表运行在 Java 虚拟机中的类和接口。

Class 的获取

反射的入口是 Class,但是反射中 Class 是没有公开的构造方法的,所以就没有办法像创建一个类一样通过 new 关键字来获取一个 Class 对象。

获取 Class 对象有 3 种方式:

  1. 通过一个对象的 getClass() 方法。
  2. 通过 .class 关键字。
  3. 通过 Class.forName()。

Field

获取一个 Class 中 Field 方式有 4 种:

public Field getField(String name);   //只能获取public修饰的字段

public Field getDeclaredField(String name);   //可以获取修饰private和其他修饰的字段

public Field[] getFields();

public Field[] getDeclaredFields()
那有人可能就有疑问了,getDeclaredField()能获取所有的修饰的字段,那还要getField干嘛?听我道来,其实getDeclaredField() 并非万能的,有一种属性它就没有办法获取的到,那就是从父类继承下来的 Field。

那如果非要获取一个 Class 继承下来的非 public 修饰的 Field 要怎么办?

答案是通过getSupperClass获取这个 Class 的 superClass。然后调用这个 superClass 的 getDeclaredField() 方法。

另外,getFields() 和 getDeclaredField() 与 getField() 和 getDeclaredFields() 作用类似,只不过它们返回的是所有的符合条件的 Field 数组。

Method

Method、Constructor 和 Field 一样都是 Class 的成员。所以,有很多共性,调用方法都很类似。

public Method getMethod(String name,pars);   //只能获取public修饰的方法

public Method getDeclaredMethod(String name,pars);   //可以获取修饰private和其他修饰的方法

public Method[] getMethods();

public Method[] getDeclaredMethods()

Constructor

getConstructor()

getDeclaredConstructor()

getConstructors()

getDeclaredConstructors()

这里需要注意的是,getConstructor() 和 getConstructors() 也没有办法获取 SuperClass 的 Constructor。

getInterfaces()

光看名字,大家可能都会觉得 getInterfaces() 的作用是获取一个类中定义的接口,但是其实不是的,getInterfaces() 获取的是一个类所有实现的接口。

注意:

获取私有字段和方法的时候,如果需要操作对他们进行操作,需要加一行代码:

field.setAccessible(true);

 否则会报错。所以,在反射中如果要操作被 private 修饰的对象,那么就必须调用它的 setAccessible(true)。

setAccessible() 的秘密

我们已经知道 Field、Method 和 Constructor 都有 setAccessible() 这个方法,至于是什么呢?这是因为它们有共同的祖先 AccessObject。

看源码可以知道,AccessObject内部有个overide 变量,如果一个 Method 的 overide 为 false 的话,它就会根据 Modifiers 判断是否具有访问权限,判断 modifiers 是不是 public,如果不是的话就返回 false。所以 protected、private、default 修饰符都会返回 false,只有 public 都会返回 true。

所以,如果通过反射方式去操作一个 Field、Method 或者是 Constructor,最好先调用它的 setAccessible(true) 以防止程序运行异常。

Class.newInstance() 和 Constructor.newInstance() 的区别

我们知道,通过反射创建一个对象,可以通过 Class.newInstance() 和 Constructor.newInstance() 两种。那么,它们有什么不同的地方吗?

总体而言,Class.newInstance() 的使用有严格的限制,那就是一个 Class 对象中,必须存在一个无参数的 Constructor,并且这个 Constructor 必须要有访问的权限。

但是,通过 Constructor.newInstance() 却没有这种限制。Constructor.newInstance() 适应任何类型的 Constructor,无论它们有参数还是无参数,只要通过 setAccessible() 控制好访问权限就可以了。

所以,一般建议优先使用 Constructor.newInstance() 去创建一个对象实例。

那么,对应到反射中如何正常调用静态方法和非静态方法呢?

关键在于 Method.invoke() 的第一个参数,static 方法因为属于类本身所以不需要对象,那么非静态方法的就必须要传入一个对象了,而且这个对象也必须是与 Method 对应的。

谨慎使用 Method.invoke() 方法

通过 Method 调用它的 invoke 方法,这应该是整个反射机制中的灵魂了。但是,正因为如此,我们就得小心处理它的很多细节。

我们知道,在正常流程开发中,调用静态方法直接用 类.方法() 的形式就可以调用,而调用非静态的方法那就必须先创建对象,再通过对象调用相应的方法。

那么,对应到反射中如何正常调用静态方法和非静态方法呢?

Method method1 = clz.getDeclaredMethod("test1");

    method1.invoke(null);
 Method method2 = clz.getDeclaredMethod("test2");

    method2.invoke(new TestMethod());

 关键在于 Method.invoke() 的第一个参数,static 方法因为属于类本身所以不需要对象,那么非静态方法的就必须要传入一个对象了,而且这个对象也必须是与 Method 对应的。

Method.invoke() 参数的秘密

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

第一个 Object 参数代表的是对应的 Class 对象实例,这在上面一节已经见识到了。而后面的参数就是可变形参了,它接受多个参数。

反射头痛的地方就是它有太多的 Exception 需要处理。
这里写图片描述

总之,还是那句话:反射有风险,编码需谨慎。

参考:
https://frank909.blog.csdn.net/article/details/76223206

总结

反射牛逼的地方,在于它可以绕过一定的安全机制,比如操纵 private 修饰的 Field、Method、Constructor。并且它还有能力改变 final 属性的 Field。但是反射头痛的地方就是它有太多的 Exception 需要处理。

猜你喜欢

转载自blog.csdn.net/Andrew_Yuan/article/details/88389037