五分钟理解Java的反射API

反射API

Java是一种具有反射功能的语言。允许开发人员在运行时检查类型、方法、字段、注解等,并在程序运行时决定是否使用。 为此,Java的反射API提供类,类,字段,构造函数,方法,注释和其他。 使用它们可以与编译时未知的类型进行交互,例如创建未知类的实例并对它们调用方法。

这个快速提示旨在让您深度了解什么是反射,它在Java中的使用,以及它可以用于什么。 之后,你将准备好开始或工作更长的教程。 为了充分使用它,你应该很好地理解Java的类构造器,特别是什么类和方法以及它们如何关联。 了解注释可解锁单独的部分。

举个栗子

我们从一个简单的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方法以注释实例数组的形式返回该元素上存在的注释 ,然后可以访问其成员。

总结

Java的反射API允许在运行时自动检查类型,方法,注释等,并调用在编译时未知的构造函数和方法。首先,请调用 Class.forName("fully.qualified.class.Name") ,然后调用getConstructorsgetMethodsgetAnnotations或类似方法。调用伴随构造函数的newInstance和方法的invoke一起发生 。

你也可以使用反射分解代码并更改非公共字段或调用非公共方法 - 这是一个冒险的做法,而且在Java 9中变得更加困难。如果你很好奇,并希望知道所有的API的来龙去脉,在Java的官方文档中给出了反射线索。到目前为止,API有点过时并有一些缺点,但是新的替代方案存在,查看方法句柄(自Java 7)和变量句柄(自Java 9)。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值