【java】深入分析try with resouce的原理 java如何通过方法句柄的方式进行方法调用

news2024/11/25 7:56:41

这个代码如果是复制用去,非常简单,但是推导过程真的特别艰辛,十几二十行的代码 博主和某T吵了一下午,到最后某T给的写法还是错误的,总之博主能写出这个demo实属不易。推荐看到本博客的同学自己再去深入学习相关的内容,毕竟某T都犯错的领域的知识点,并不是那么容易掌握的,出错我们得知道排查。

前言:博主为什么会知道方法句柄这个概念呢?

我们知道在jdk7中,引入了 try with resouce的用法,用来一定程度上替代try catch finally, 注意 这个try with resouce 不是让你这么写:

// 错误的
try{
}with{
}resouce{
}

它是字面意思,try中包含资源 :
try跟着的小括号里面 声明需要关闭的资源,正确写法:

   try( FileInputStream fileInputStream = new FileInputStream("xxx")) {

            System.out.println("业务逻辑");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

这样 fileInputStream 就会自动调用close方法了,本文就是讲述如何自动调用的。

  • 在java7 之后 新增了 AutoCloseable 接口
  • 在java7之前 资源类(如 InputStream 等),都是实现了 Closeable 接口, 而在java7之后 Closeable又继承了 AutoCloseable 接口, 因为间接继承了AutoCloseable接口 所以可以自动调用
  • 自动调用是因为 invokedynamic 指令
    (在 lambda 转funciton中 例如 People::getName 也用到该指令 此外调用sacla Groovy Kotlin等语言时 也会用到该指令)

invokedynamic指令通常与Java虚拟机规范中的方法类型(Method Type)和方法句柄(Method Handle)等概念一起使用。方法类型表示方法的参数和返回值类型,方法句柄表示方法的引用。

在运行时,JVM会根据提供的方法句柄和方法类型动态地选择需要调用的方法,并且可以在运行时生成新的方法。

方法句柄:

  • 是Java虚拟机中一个重要的概念,它可以看作是一个方法的引用,用于在运行时动态地选择需要调用的方法。

  • 可以将方法句柄视为一个轻量级的函数指针,它包含了方法的名称、参数类型和返回值类型等信息。

  • 在Java中,方法句柄是通过java.lang.invoke.MethodHandle类来表示的。

  • 方法句柄可以用来调用静态方法、实例方法、构造方法等,也可以用来获取字段的值或者设置字段的值。

  • 方法句柄通常是在编译时生成的,并且可以存储在常量池中。

  • 在运行时,可以使用方法句柄来动态地选择需要调用的方法,并且可以传递任意数量和类型的参数。

  • 方法句柄可以与invokedynamic指令配合使用,实现一些动态语言的特性,例如函数式编程和动态类型检查等

所以博主大胆推测:在动态调用方法的场景中 句柄的方式可以一定程度上代替反射

某T也回答 句柄方式是轻量级的 而反射是重量级的。

首先,使用反射 invoke() 方法调用方法是一种比较灵活的方式,可以调用任何类和任何方法,但是它的性能相对较低,因为每次调用方法都需要进行一定的运行时检查和解析。

而方法句柄的调用性能相对较高,因为方法句柄是在编译期就已经确定了调用的目标方法的签名和位置,避免了运行时的解析和检查。此外,方法句柄还可以被编译器优化和内联,进一步提高了性能。

于是,博主开始写一个句柄方式调用的demo:

public class MethodHandleController {

    public static void test(Integer a, Integer b) {
        System.out.println("测试句柄====有参");
    }

    public static void main(String[] args) throws Throwable {
        // 有参调用 如 test(Integer a, Integer b);
        MethodHandles.Lookup lookup = MethodHandles.lookup();

//        MethodType mt = MethodType.methodType(void.class, Integer.class, Integer.class);
//        MethodHandle mh = lookup.findStatic(MethodHandleController.class, "test", mt);
        // 调用方法
//       mh.invokeExact(Integer.valueOf(1), Integer.valueOf(2));

        // 也可以换成通用的 需要用户传入返回类型,方法参数数量 以及按顺序传入方法的参数类型
        MethodType genericType = MethodType.genericMethodType(2);

        MethodType methodType = genericType.changeParameterType(0, Integer.class);
        MethodType methodType1 = methodType.changeParameterType(1, Integer.class);

        MethodType methodType2 = methodType1.changeReturnType(void.class);

        // findStatic : 查找静态方法句柄
        MethodHandle commonMh = lookup.findStatic(MethodHandleController.class, "test", methodType2);

        Object invoke = commonMh.invoke(1, 2);

        // 需要严格匹配参数及返回值 例如调用了一个 void方法 如果我们用了变量去接收返回值 则会报错 所以不推荐该方法 毫无容错率
        commonMh.invokeExact(Integer.valueOf(1), Integer.valueOf(2));
        System.out.println("==");


        // 无参调用  findVirtual: 查找实例方法句柄 且需要bindTo实例

        MethodType noArgMethodType = MethodType.methodType(void.class);
        MethodHandle mh = lookup.findVirtual(MethodHandleController.class, "testNoArg", noArgMethodType)
                .bindTo(lookup.findConstructor(MethodHandleController.class, noArgMethodType).invoke());

        // 使用 invoke() 方法调用无参方法句柄
        mh.invoke();

        // 使用 invokeExact() 方法调用无参方法句柄
        mh.invokeExact();
    }

    public void testNoArg() {
        System.out.println("测试句柄==非静态====无参");
    }

}

这段代码中(包括被注释的)有很多细节 下面这段话 一定需要结合代码一起看:

  • 如果上述代码要做成通用的, 需要用户(使用者)传入返回类型,方法参数数量 以及按顺序传入方法的每个参数类型,然后我们for循环中,用methodType.changeParameterType 不断替换就好了。
    值得注意的是:MethodType.genericMethodType(num); num代表方法有几个参数,这也是为什么需要使用者传入方法参数数量的原因,而且默认生成的是Object类型,所以需要传入参数类型 通过 changeParameterType 方法进行替换

  • MethodType.genericMethodType(num);

  • MethodType.methodType() 方法的第一个参数 是方法的返回值类型,如果是void, 则为void.class

  • 如果方法是静态的,且方法已知,则使用
    lookup.findStatic,如果是非静态的 则使用lookup.findVirtual,且需要bindTo 一个实例

  • invokeExact 是一个极其严格的方法,在方法已知的时候,也就是不写成通用时,可以用这个方法提高效率,其它时候一律不推荐使用。
    例如我们的方法是 test(Integer a, Integer b),而执行时 commonMh.invokeExact(1, 2); 传入的是int类型的数字,就会报错了,它并不会自动的拆箱装箱;
    再例如,我们的方法是void test(), 如果在 commonMh.invokeExact(Integer.valueOf(1), Integer.valueOf(2));
    前面声明一个变量 去接收返回值:
    Object res = commonMh.invokeExact(Integer.valueOf(1), Integer.valueOf(2)); 那这行代码将会报错。

    在这里插入图片描述
    取而代之的是,我们可以使用invoke()方法

本文创作不易,转载请声明原出处,请勿直接搬运

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/425081.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用SQL语句操作Oracle数据库--数据查询详解(下篇)

数据查询是数据库的核心操作。上一篇文章我们介绍了单表查询操作,本文将继续介绍另一种重要的查询类型—[ 多表查询 ](涉及多个表的数据查询)。 本文我们将使用以下三个表来进行查询操作: TABLE1: S(学生基本信息表) TABLE2: C(…

Maven环境搭建及配置

文章目录一、系统要求二、安装步骤1.JDK检查2.下载Maven3.配置Maven环境变量4.检查配置一、系统要求 项目要求JDKMaven 3.3 要求 JDK 1.7或以上Maven 3.2 要求 JDK 1.6 或以上 Maven 3.0/3.1 要求 JDK 1.5 或以上磁盘Maven 自身安装需要大约 10 MB 空间。除此之外,…

​破除“内卷”,什么才是高阶智能座舱更优方案?

下一代智能座舱雏形已现。 从多屏互动到舱内全场景交互,从中控娱乐快速延伸到更多元化的车内娱乐平台;越来越多元化功能集中上车,座舱空间的营造(包括氛围灯、香氛等)以及AR技术的应用等等,开始深刻影响着…

活动目录密码策略管理

员工使用的密码可以决定或破坏组织中的数据安全性。但是,知道员工通常不遵循良好的密码卫生习惯也就不足为奇了。从在本机工具(如 Windows Active Directory 组策略)中设置弱密码和通用密码到宽松的密码策略规则,有几个因素对密码…

基于DSP+FPGA的机载雷达伺服控制系统的硬件设计与开发(一)总体设计

2.1 功能要求及性能指标 2.1.1 功能要求 (1)具备方位和俯仰两轴运动的能力; (2)方位轴可实现预置、周扫和扇扫功能; (3)俯仰轴可实现预置功能。 2.1.2 性能指标 (1&#…

【英语】大学英语CET考试,听力题答题技巧笔记(3-4)与技巧总结

文章目录听力技巧课3(长选项关键词)听力技巧课4(所有划关键词的方法,真题带练)听力技巧总结(1-4)听力技巧课1&2:地址 听力技巧课3(长选项关键词) 上课老…

JWT 认证机制

Session 认证机制需要配合 Cookie 才能实现。由于 Cookie 默认不支持跨域访问,所以,当涉及到前端跨域请求后端按口的时候,需要做很多额外的配置,才能实现跨域 Session 认证。 JWT (英文全称: JSON Web Token)是目前最流行的跨域认…

LeetCode算法小抄--二叉搜索树

LeetCode算法小抄--二叉搜索树二叉搜索树(BST)寻找第 K 小的元素[230. 二叉搜索树中第K小的元素](https://leetcode.cn/problems/kth-smallest-element-in-a-bst/)二叉搜索树的转化[538. 把二叉搜索树转换为累加树](https://leetcode.cn/problems/convert-bst-to-greater-tree/…

AI提效工具|借助chatgpt快速读论文,快速总结、归纳、索引相似文章

目前新论文层出不穷,“快速阅读论文”成为研究者们一个必备能力。本文简单记录了近期出现的两个借助chatgpt来帮助我们快速读论文的“神器”,帮助大家快速上手应用,迅速提升论文阅读速度。 此外,本人也会定期更新记录一些类似的“…

酷开科技携手蓝凌数字化OA,释放数字生产力

疫情虽已落幕,但协同办公理念却逐步升级,伴随企业进入到以数字办公为基础的新周期。以提升管理效率为目的的协同办公在人工智能、大数据等技术的加持下,快速步入赋能办公、提升办公效率的自律型协作模式。 数字化时代,时间、空间…

涨点技巧:IOU篇---Yolov8引入WIoU,SIoU,EIoU,α-IoU,不同数据集验证能涨点

1.IOU介绍 IoU其实是Intersection over Union的简称,也叫‘交并比’。IoU在目标检测以及语义分割中,都有着至关重要的作用。 首先,我们先来了解一下IoU的定义: 我们可以把IoU的值定为为两个图形面积的交集和并集的比值,如下图所示: 1.1 Yolov8自带IOU方法 GIoU, DIoU,…

[测试猿课堂]接口自动化软件测试Requests模块从0到精通 一篇搞定

Requests模块是Python中一个非常流行的第三方库,用于处理HTTP请求。在接口自动化测试中,Requests模块可用于模拟发送HTTP请求并检查响应数据,以验证API的功能和性能。以下是与Requests模块相关的一些知识点,这些知识点都是从事接口…

【Python_Scrapy学习笔记(七)】基于Scrapy框架实现数据持久化

基于 Scrapy框架实现数据持久化 前言 本文中介绍 如何基于 Scrapy 框架实现数据持久化,包括 Scrapy 数据持久化到 MySQL 数据库、MangoDB数据库和本地 csv 文件、json 文件。 正文 1、Scrapy数据持久化到MySQL数据库 在 settings.py 中定义 MySQL 相关变量 # 定…

cuda-gdb 基础使用指南

cuda-gdb 基础使用指南 本文的cuda-gdb的简单入门指导,主要的参考是官方文档.但是原文是英文,又找了腾讯家的文档翻译机器,可惜水平着实一般.如果在使用过程中有更细的要求,可以看文档,本文最后贴出原文的目录,可以自己按图索骥,看看有没有其他的需求. 入门要求 …

GDOUCTF2023 Reverse题解

文章目录题目附件Check_Your_LuckTEA基本逻辑:show函数setKey函数encode函数(tea算法):judge函数解题脚本doublegamesnakefun迷宫关键循环题目附件 链接:https://pan.baidu.com/s/1W0GisS4R-rHYHK4Bu167_g?pwdnw4c Check_Your_Luck 可以看到五条方程,根据方程可…

Flutter开发日常练习-小猫咪杂货店

贴三张效果图 1.欢迎页面 2.商品展示列表 3.购物车页面 因为数据是本地的所以创建本地数据 final List _shopItems [["ZaoShui.", "25.00", "assets/8b10de68e58cfef6bd5f22e5321537.jpg", Colors.green],["ZaoQi.", "25.0…

Unity --- 枪类与子弹类的设计初探 与 Physics Material

1.类的设计方法 --- 首先将不同的对象相同的行为和方法做到一个类A中,然后再给每个对象各创建一个类,并且都继承大类A,同时在自己的类中创建自己独有的方法或者属性 1.一般子弹的射击都是用射线检测的方式去做的 --- 用碰撞器会出现我们不想要…

STM32-高级定时器输出比较模式实验

比较输出功能:定时器通过对预设的比较值与定时器特定寄存器的值做比较之后,并依据相应的输出模式从而实现各类输出,如PWM输出、电平翻转、单脉冲模式等。一般来说,STM32的通用定时器和高级定时器都具有比较输出功能,不…

使用putty在云服务器上安装jdk

在云服务器上安装jdk的步骤: 1. 登录到云服务器,打开putty终端,并使用root账户登录。 2. 在终端中输入以下命令,更新软件包列表: sudo apt-get update 3. 安装OpenJDK 8,请在终端中输入以下命令&#…

leetcode-1041. 困于环中的机器人

leetcode-1041. 困于环中的机器人1. 算法题目2 . 实现思路3. 参考代码1. 算法题目 题目如下: 在无限的平面上,机器人最初位于 (0, 0) 处,面朝北方。注意: 北方向 是y轴的正方向。南方向 是y轴的负方向。东方向 是x轴的正方向。西方向 是x轴的…