文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

优于反射(Reflection):在Java中使用方法句柄和变量句柄

2024-11-29 19:29

关注

审校 | 重楼

区分资深Java开发人员的因素之一是熟悉反射(reflection)及其先进替代品。反射为Java开发提供了超能力,但它很麻烦,容易出错,并且存在性能瓶颈。现代Java正在努力用标准化的选项取代反射,包括方法句柄(MethodHandle变量句柄(VarHandle。与反射一样,这些类也允许你访问对象上的方法和字段,但使用的是更清晰的API。

句柄的力量

顾名思义,MethodHandle和VarHandle都为你提供了句柄,它们是引用对象元属性的变量。这些句柄使你能够直接处理方法和字段。它们是特殊的变量,引用运行时环境的某些部分,否则将对代码隐藏。

这些功能的起点是MethodHandle上的各种查找方法,这些方法提供了一种以编程方式查找类元数据的现代方法。这类似于旧的反射API的方法,如getDeclaredMethod,但具有更多的结构和安全性。

一旦有了类元数据的句柄,就可以使用MethodHandle和VarHandle以编程方式对类实例上存在的方法和字段进行调用。在底层,JVM管理这些方法,通常比使用反射获得更好的性能。

方法和变量句柄 VS. Java反射

要真正理解MethodHandles和Varhandles——它们是做什么的以及它们为什么有用——了解一些关于Java反射的知识是有帮助的。这将帮助你理解为什么反射会演变成这些较新的API。

最根本的问题是这些技术——反射、方法句柄、变量句柄——需要满足什么需求当我们可以简单地实例化一个对象,调用它的公共方法并访问它的公共成员时,我们为什么要用编程的方式来做这些事情呢

在很多情况下,不能通过公共方法访问需要的程序,所以必须绕过正常的路线。这主要发生在你编写类似框架代码时,该代码对一系列类进行操作并对它们进行非标准操作。

以一个持久性框架为例,你需要将类映射到表和表之间,因此你需要内省(Introspect)类以了解它们具有哪些字段和方法。这种情况也会出现在应用程序代码中,特别是当你需要访问遗留库中无法访问的部分时。

决定使用哪种技术要了解需要什么。如果你可以使用普通的Java调用来解决这个问题,那么就是可行的方法。如果需要更复杂的东西,先看看标准的API(比如MethodHandles和VarHandles。只有当这些都无法实现时,才应该转而依靠反射

下述示例可以帮助你理解为什么JAVA开发工具包(JDK)更喜欢句柄而传统的Java反射。

使用反射来访问方法

我们将从一个反射示例开始,因为它很常见,并且会给我们一个已知的参考。请记住,这是最后的解决方案。

假设有这个类:

public class MyClass {
 private String name;
 public MyClass(String name) {
 this.name = name;
 }
 public String getName() { 
 return name;
 }
}

这是一个非常简单的事情只是一个用于保存字符串名的类。要创建这个类,我们可以使用普通实例化

MyClass objectInstance = new MyClass("John Doe");

下面是使用反射访问该方法的示例:

Class clazz = objectInstance.getClass();
Method method = clazz3.getDeclaredMethod("getName");
String value = (String) method.invoke(objectInstance);
System.out.println(value); // prints "John Doe"

使用MethodHandles来访问方法

方法句柄为我们提供了与反射相同的功能,但语法更安全

Class clazz = objectInstance.getClass(); 
MethodHandle handle = MethodHandles.lookup().findVirtual(clazz, "getName", methodType(String.class));
String value = (String) handle.invoke(objectInstance);
System.out.println(value); // Prints “John Doe”

我们以同样的方式开始,从实例中获取类。然后,我们在MethodHandles上使用lookup(). findvirtual()方法。这是MethodHandles设计的主要目的之一提供一种更简洁、JDK认可的方法来查找方法。这种方法还针对JVM优化进行了增强。

接下来,我们将使用handle.invoke调用带有句柄的方法并传入对象实例。

直接访问字段

假设我们之前的类MyClass上面有name字段但没有访问器。我们现在需要更强的程序来访问它,因为我们要直接访问私有成员(Private Member)。下面是我们使用标准反射的方法

Class clazz = objectInstance.getClass(); 
Field field = clazz.getDeclaredField("name"); 
field.setAccessible(true); 
String value = (String) field.get(objectInstance);
System.out.println(value); // prints “John Doe”

注意,我们再次直接处理对象的元数据,比如它的类和它的字段。我们可以使用setAccessible操作字段的可访问性这被认为是有风险的,因为它可能会改变目标代码所写的限制。这是使私有字段对我们可见的关键部分。

现在让我们使用变量句柄做同样的事情

Classl clazz = objectInstance.getClass();
VarHandle handle = MethodHandles.privateLookupIn(clazz, 
 MethodHandles.lookup()).findVarHandle(clazz, "name", String.class);
String value = (String) handle.get(objectInstance);
System.out.println(value4); // prints "John Doe"

这里,我们使用privateLookupIn,因为该字段被标记为私有Private。还有一个通用lookup(),它将尊重访问修饰符,因此它更安全,但不会找到私有字段。

虽然上面的代码可以运行,但出于性能原因,建议静态地实例化句柄本身,如下所示

private static VarHandle HANDLE;
 static {
 try {
 HANDLE = MethodHandles.privateLookupIn(MyClass.class, MethodHandles.lookup()).findVarHandle(MyClass.class, "name", String.class);
 } catch (Throwable t){
 throw new RuntimeException(t);
 }
 }

// …

System.out.println("static: " + HANDLE.get(objectInstance));

这里,我们静态地实例化了HANDLE变量,然后在稍后的正常代码流中使用它。这也突出了句柄本身是为类型MyClass定义的,然后为实例(ObjectInstance重用。

注意,直接实例化句柄需要知道类的名称。如果你不知道类的名称,则不能使用这种方法。

方法和变量句柄的限制

尽管它们为标准化的JDK带来了强大的功能,但方法句柄和变量句柄并不打算涵盖Java反射API中的所有功能。它们只是涵盖了一个重点范围查找类元数据并使用它来访问常规Java限制之外的方法和字段。其余sun.misc. Unsafe中的反射力量正逐渐被其他包所取代。

如前所述,MethodHandles和VarHandle不支持实例化类,这在某些情况下会产生限制。

是时候思考反射替代方案了

花点时间说服自己远离反射是必要的,也是值得的。如果你研究了基准测试,就会发现方法句柄和变量句柄的性能普遍优于反射。另一方面,它们更安全、更地道,并且JVM代码库正在采用这些方法它们的普及也只是时间问题而已

在基准测试中,静态声明句柄可以显著提高性能。这是因为JVM可以在编译时内联这些信息。但是,如前所述,这样做并总是可——例如,如果你在编译时不知道类的名称。

除了性能之外,基于正确性等因素考虑,反射也正逐渐被弃用。最终,无论如何都需要迁移工作。现在是时候开始移动代码库中那些具有现代替代品如MethodHandles和VarHandle的部分了!

原文Better than reflection: Using method handles and variable handles in Java,作者:Matthew Tyson

来源:51CTO内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯