搜索
首页Javajava教程Java怎么看Lambda源码
Java怎么看Lambda源码May 13, 2023 am 11:10 AM
javalambda

Java怎么看Lambda源码


1、Demo

首先我们来看一个 Lambda 表达式的 Demo,如下图:

Java怎么看Lambda源码

代码比较简单,就是新起一个线程打印一句话,但对于图中 () -> System.out.println ( “ lambda is run “ ) 这种代码,估计很多同学都感觉到很困惑,Java 是怎么识别这种代码的?

如果我们修改成匿名内部类的写法,就很清楚,大家都能看懂,如下图:

Java怎么看Lambda源码

那是不是说 () -> System.out.println ( “ lambda is run “ ) 这种形式的代码,其实就是建立了内部类呢?其实这就是最简单 Lambda 表达式,我们是无法通过 IDEA 看到源码和其底层结构的,下面我们就来介绍几种可看到其底层实现的方式。

2、异常判断法

我们可以在代码执行中主动抛出异常,打印出堆栈,堆栈会说明其运行轨迹,一般这种方法简单高效,基本上可以看到很多情况下的隐藏代码,我们来试一下,如下图:

Java怎么看Lambda源码

从异常的堆栈中,我们可以看到 JVM 自动给当前类建立了内部类(错误堆栈中出现多次的 $ 表示有内部类),内部类的代码在执行过程中,抛出了异常,但这里显示的代码是 Unknown Source,所以我们也无法 debug 进去,一般情况下,异常都能暴露出代码执行的路径,我们可以打好断点后再次运行,但对于 Lambda 表达式而言,通过异常判断法我们只清楚有内部类,但无法看到内部类中的源码。

3、javap 命令法

javap 是 Java 自带的可以查看 class 字节码文件的工具,安装过 Java 基础环境的电脑都可以直接执行 javap 命令,如下图:

Java怎么看Lambda源码

命令选项中,我们主要是用-v -verbose 这个命令,可以完整输出字节码文件的内容。

接下来我们使用 javap 命令查看下 Lambda.class 文件,在讲解的过程中,我们会带上一些关于 class 文件的知识。

我们在命令窗口中找到 Lambda.class 所在的位置,执行命令:javap -verbose Lambda.class,然后你会看到一长串的东西,这些叫做汇编指令,接下来我们来一一讲解下( 所有的参考资料来自 Java 虚拟机规范,不再一一引用说明):

汇编指令中我们很容易找到 Constant pool 打头的一长串类型,我们叫做常量池,官方英文叫做 Run-Time Constant Pool,我们简单理解成一个装满常量的 table ,table 中包含编译时明确的数字和文字,类、方法和字段的类型信息等等。table 中的每个元素叫做 cpinfo,cpinfo 由唯一标识 ( tag ) + 名称组成,目前 tag 的类型一共有:

Java怎么看Lambda源码

贴出我们解析出来的部分图:

Java怎么看Lambda源码

  1. 图中 Constant pool 字样代表当前信息是常量池;

  2. 每行都是一个 cp_info ,第一列的 #1 代表是在常量池下标为 1 的位置 ;

  3. 每行的第二列,是 cp_info 的唯一标识 ( tag ) ,比如 Methodref 对应着上表中的 CONSTANT_Methodref(上上图中表格中 value 对应 10 的 tag),代表当前行是表示方法的描述信息的,比如说方法的名称,入参类型,出参数类型等,具体的含义在 Java 虚拟机规范中都可以查询到,Methodref 的截图如下:
    Java怎么看Lambda源码

  4. 每行的第三列,如果是具体的值的话,直接显示具体的值,如果是复杂的值的话,会显示 cp_info 的引用,比如说图中标红 2 处,引用两个 13 和 14 位置的 cp_info,13 表示方法名字是 init,14 表示方法无返回值,结合起来表示方法的名称和返回类型,就是一个无参构造器;

  5. 每行的第四列,就是具体的值了。

对于比较重要的 cp_info 类型我们说明下其含义:

  1. InvokeDynamic 表示动态的调用方法,后面我们会详细说明;

  2. Fieldref 表示字段的描述信息,如字段的名称、类型;

  3. NameAndType 是对字段和方法类型的描述;

  4. MethodHandle 方法句柄,动态调用方法的统称,在编译时我们不知道具体是那个方法,但运行时肯定会知道调用的是那个方法;

  5. MethodType 动态方法类型,只有在动态运行时才会知道其方法类型是什么。

我们从上上图中标红的 3 处,发现 Ljava/lang/invoke/MethodHandles$Lookup,java/lang/invoke/LambdaMetafactory.metafactory 类似这样的代码,MethodHandles 和 LambdaMetafactory 都是 java.lang.invoke 包下面的重要方法,invoke 包主要实现了动态语言的功能,我们知道 java 语言属于静态编译语言,在编译的时候,类、方法、字段等等的类型都已经确定了,而 invoke 实现的是一种动态语言,也就是说编译的时候并不知道类、方法、字段是什么类型,只有到运行的时候才知道。

比如这行代码:Runnable runnable = () -> System.out.println(“lambda is run”); 在编译器编译的时候 () 这个括号编译器并不知道是干什么的,只有在运行的时候,才会知道原来这代表着的是 Runnable.run() 方法。invoke 包里面很多类,都是为了代表这些 () 的,我们称作为方法句柄( MethodHandler ),在编译的时候,编译器只知道这里是个方法句柄,并不知道实际上执行什么方法,只有在执行的时候才知道,那么问题来了,JVM 执行的时候,是如何知道 () 这个方法句柄,实际上是执行 Runnable.run() 方法的呢?

首先我们看下 simple 方法的汇编指令:

Java怎么看Lambda源码

从上图中就可以看出 simple 方法中的 () -> System.out.println(“lambda is run”) 代码中的 (),实际上就是 Runnable.run 方法。

我们追溯到 # 2 常量池,也就是上上图中标红 1 处,InvokeDynamic 表示这里是个动态调用,调用的是两个常量池的 cp_info,位置是 #0:#37 ,我们往下找 #37 代表着是 // run:()Ljava/lang/Runnable,这里表明了在 JVM 真正执行的时候,需要动态调用 Runnable.run() 方法,从汇编指令上我们可以看出 () 实际上就是 Runnable.run(),下面我们 debug 来证明一下。

我们在上上图中 3 处发现了 LambdaMetafactory.metafactory 的字样,通过查询官方文档,得知该方法正是执行时, 链接到真正代码的关键,于是我们在 metafactory 方法中打个断点 debug 一下,如下图:

Java怎么看Lambda源码

metafactory 方法入参 caller 代表实际发生动态调用的位置,invokedName 表示调用方法名称,invokedType 表示调用的多个入参和出参,samMethodType 表示具体的实现者的参数,implMethod 表示实际上的实现者,instantiatedMethodType 等同于 implMethod。

以上内容总结一下:

1:从汇编指令的 simple 方法中,我们可以看到会执行 Runnable.run 方法;

2:在实际的运行时,JVM 碰到 simple 方法的 invokedynamic 指令,会动态调用 LambdaMetafactory.metafactory 方法,执行具体的 Runnable.run 方法。

所以可以把 Lambda 表达值的具体执行归功于 invokedynamic JVM 指令,正是因为这个指令,才可以做到虽然编译时不知道要干啥,但动态运行时却能找到具体要执行的代码。

接着我们看一下在汇编指令输出的最后,我们发现了异常判断法中发现的内部类,如下图:

Java怎么看Lambda源码

上图中箭头很多,一层一层的表达清楚了当前内部类的所有信息。

以上是Java怎么看Lambda源码的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:亿速云。如有侵权,请联系admin@php.cn删除
带你搞懂Java结构化数据处理开源库SPL带你搞懂Java结构化数据处理开源库SPLMay 24, 2022 pm 01:34 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于结构化数据处理开源库SPL的相关问题,下面就一起来看一下java下理想的结构化数据处理类库,希望对大家有帮助。

Java集合框架之PriorityQueue优先级队列Java集合框架之PriorityQueue优先级队列Jun 09, 2022 am 11:47 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于PriorityQueue优先级队列的相关知识,Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,下面一起来看一下,希望对大家有帮助。

完全掌握Java锁(图文解析)完全掌握Java锁(图文解析)Jun 14, 2022 am 11:47 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于java锁的相关问题,包括了独占锁、悲观锁、乐观锁、共享锁等等内容,下面一起来看一下,希望对大家有帮助。

一起聊聊Java多线程之线程安全问题一起聊聊Java多线程之线程安全问题Apr 21, 2022 pm 06:17 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于多线程的相关问题,包括了线程安装、线程加锁与线程不安全的原因、线程安全的标准类等等内容,希望对大家有帮助。

详细解析Java的this和super关键字详细解析Java的this和super关键字Apr 30, 2022 am 09:00 AM

本篇文章给大家带来了关于Java的相关知识,其中主要介绍了关于关键字中this和super的相关问题,以及他们的一些区别,下面一起来看一下,希望对大家有帮助。

Java基础归纳之枚举Java基础归纳之枚举May 26, 2022 am 11:50 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于枚举的相关问题,包括了枚举的基本操作、集合类对枚举的支持等等内容,下面一起来看一下,希望对大家有帮助。

java中封装是什么java中封装是什么May 16, 2019 pm 06:08 PM

封装是一种信息隐藏技术,是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法;封装可以被认为是一个保护屏障,防止指定类的代码和数据被外部类定义的代码随机访问。封装可以通过关键字private,protected和public实现。

归纳整理JAVA装饰器模式(实例详解)归纳整理JAVA装饰器模式(实例详解)May 05, 2022 pm 06:48 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于设计模式的相关问题,主要将装饰器模式的相关内容,指在不改变现有对象结构的情况下,动态地给该对象增加一些职责的模式,希望对大家有帮助。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前By尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
4 周前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!