七分钟理解 Java 的反射 API
像java一样,一种具有反射功能的语言。允许开发人员在运行时检查类型、方法、字段、注解等,并在程序运行时决定是否使用。 为此,Java的反射API提供类,类,字段,构造函数,方法,注释和其他。 使用它们可以与编译时未知的类型进行交互,例如创建未知类的实例并对它们调用方法。
这个快速提示旨在让您深度了解什么是反射,它在Java中的使用,以及它可以用于什么。 之后,你将准备好开始或工作更长的教程。 为了充分使用它,你应该很好地理解Java的类构造器,特别是什么类和方法以及它们如何关联。 了解注释可解锁单独的部分。
Reflection API
我们从一个简单的Java代码开始(代码针对有一定java基础的阅读人员)
URL url = new URL("https://sitepoint.com/java ");
String urlString = url.toExternalForm();
System.out.println(urlString);
我决定在编译时(即当我在写代码时)创建一个URL对象,并且调用了其中的一些方法。下面演示了我使用Java的反射API完成了同样的事情:
// the gateway to reflection is the `Class` instance
// for the class you want to operate on
Class<?> type = Class.forName("java.net.URL");
// fetches the constructor that takes a `String` argument
// and uses it to create a new instance for the given string
Constructor<?> constructor = type.getConstructor(String.class);
Object instance = constructor.newInstance("https://sitepoint.com/java");
// fetches the `toExternalForm` method and invokes it on
// the instance that was just created
Method method = type.getMethod("toExternalForm");
Object methodCallResult = method.invoke(instance);
System.out.println(methodCallResult);
使用反射API的确比直接写代码要笨重一点. 但是使用反射你会发现, 你在代码当中的调用细节 (比如说我用的URL这个类以及我调用其中的方法) 变成了仅仅一个参数. 结果呢,在编译期间URL以及toExternalForm并没有绑定, 它们是在程序开始运行的时候才被决定绑定的
反射的大多数用例都是“框架”的场景下 ,想想junit, 比如说, 执行所有被@ test注解的方法. 一旦框架在classpath扫描的时候找到注解它就会调用getMethod以及invoke函数去执行用户代码. spring和其他的一些web框架在搜索控制器以及url映射的收也差不多是这么做的,对可扩展的应用程序来说反射的另外一个用途就是在运行时加载用户提供的插件
基本类型和方法
调用反射API的方法是Class :: forName。 在它的简单形式中,这个静态方法只需要一个完全限定的类名,并为它返回一个Class实例。 该实例可用于获取字段,方法,构造函数等。
通过构造器函数获取类对象,getConstructor方法可以使用构造函数参数的类型调用,就像我上面做的那样。 类似地,可以通过调用getMethod并传递其名称以及参数类型来访问特定方法。 上面的getMethod(“toExternalForm”)调用没有指定任何类型,因为该方法没有参数。
这里有一个方法:
Class<?> type = Class.forName("java.net.URL ");
// `URL::openConnection` has an overload that accepts a java.net.Proxy
Method openConnection = type.getMethod("openConnection ", Proxy.class);
这些调用返回的实例分别是Constructor和Method类型。 要调用底层成员,他们提供类似于Constructor :: newInstance和Method :: invoke的方法。 后者的一个有趣的细节是,要调用该方法的实例需要作为第一个参数传递给它(它指类的实例)。 其他参数将被传递给被调用的方法。
继续openConnection示例:
openConnection.invoke(instance, someProxy);
如果要调用静态方法,则将忽略实例参数,因此可以为null。
注解
注解是反射的重要组成部分。 事实上,注解主要针对反射。 它们旨在提供程序运行时访问的元信息,然后用于塑造程序的行为。 (如上所述,JUnit的@Test和Spring的@Controller和@RequestMapping是很好的例子。)
所有重要的反射相关类型,如类,字段,构造函数,方法和参数实现AnnotatedElement接口。 链接的Javadoc包含了注释如何与这些元素(直接呈现,间接呈现或关联)相关的详细解释,但是它最简单的形式是:getAnnotations方法以注释实例数组的形式返回该元素上存在的注释 ,然后可以访问其成员。
写在最后:欢迎留言讨论,加关注,持续更新!