【源码分析】Java中的lambda表达式会生成内部类吗?是如何生成的?

news2024/12/23 5:26:47

文末附结论


分析

以该程序为例子

public class LambdaTest {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println("asdwerwerwe");
        });
        t1.start();
        System.out.println("end!!!");
    }
}

调用javap -c -p LambdaTest.class之后得到:

Compiled from "LambdaTest.java"
public class com.atguigu.juc.test.LambdaTest {
  public com.atguigu.juc.test.LambdaTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/Thread
       3: dup
       4: invokedynamic #3,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
       9: invokespecial #4                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
      12: astore_1
      13: aload_1
      14: invokevirtual #5                  // Method java/lang/Thread.start:()V
      17: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      20: ldc           #7                  // String end!!!
      22: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      25: return

  private static void lambda$main$0();
    Code:
       0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #9                  // String asdwerwerwe
       5: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}
  1. 在编译阶段,Java编译器将Lambda表达式转换为一个私有的静态方法,即lambda$main$0方法。

  2. invokedynamic #3, 0其实就是把生成的lambda$main$0方法与实际的调用点进行绑定。

    1. invokedynamic字节码会在运行时根据引导方法和方法类型来动态地解析和绑定方法的调用。
    2. 具体的动态调用逻辑由引导方法决定,在Lambda表达式的情况下,引导方法通常由LambdaMetafactory提供,用于创建函数式接口的实例并与Lambda表达式绑定。
    3. invokedynamic #3, 0 表示在这里使用引导方法(常量池中索引为3)和方法类型(索引为0)进行动态调用。

    #3是常量池中的符号引用,对应的是bootstrap引导方法

    在这里插入图片描述

    在这里插入图片描述

    这一串字符表示LambdaMetafactory生成的方法描述符(Method Descriptor)。在Java字节码中,方法描述符用于描述方法的参数类型和返回类型。

    (Ljava/lang/invoke/MethodHandles$Lookup;:该部分表示方法的第一个参数类型,即MethodHandles.Lookup类型的参数。MethodHandles.Lookup是Java核心库中的一个类,用于执行动态方法调用。
    Ljava/lang/String;:表示方法的第二个参数类型,即String类型的参数。
    Ljava/lang/invoke/MethodType;:表示方法的第三个参数类型,即MethodType类型的参数。MethodType是Java核心库中的一个类,用于描述方法的类型信息,包括参数类型和返回类型。
    Ljava/lang/invoke/MethodType;:表示方法的第四个参数类型,与第三个参数类型相同。
    Ljava/lang/invoke/MethodHandle;:表示方法的第五个参数类型,即MethodHandle类型的参数。MethodHandle是Java核心库中的一个类,用于执行方法调用。
    Ljava/lang/invoke/MethodType;):表示方法的最后一个参数类型,即MethodType类型的参数。
    

那我们把视角转向LambdaMetafactory.metafactory()

public class LambdaMetafactory {
	public static CallSite metafactory(MethodHandles.Lookup caller,
                                       String invokedName,
                                       MethodType invokedType,
                                       MethodType samMethodType,
                                       MethodHandle implMethod,
                                       MethodType instantiatedMethodType)
            throws LambdaConversionException {
        AbstractValidatingLambdaMetafactory mf;
        mf = new InnerClassLambdaMetafactory(caller, invokedType,
                                             invokedName, samMethodType,
                                             implMethod, instantiatedMethodType,
                                             false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
        mf.validateMetafactoryArgs();
        return mf.buildCallSite();
    }
    ......
}

在这个方法里打个断点以debug模式运行,从下图可以看到根据相关的信息创建了一个InnerClassLambdaMetafactory工厂对象,然后利用工厂对象进行创建,但是还是不能明确的看到创建了内部类。

在这里插入图片描述

InnerClassLambdaMetafactory内部类对象mf的一些属性可以看到有创建内部类的端倪。

在这里插入图片描述

紧接着,来到mf.buildCallSite()方法里,可以看到调用spinInnnerClass()

如下图注释所示:spinInnnerClass()的作用是生成一个实现函数式接口的类文件,并定义并返回该类。

具体如何生成的class文件,就不再深究,具体是调用了UNSAFE类的defineAnonymousClass方法

spinInnnerClass()执行完,生成的class对象如下图所示:

在这里插入图片描述

之后生成了该类的一个对象inst,可以在此处调用inst.run()方法执行了lambda表达式内的内容,以此来验证是真的生成一个内部类,并且把内部类的对象和该接口进行绑定。

在这里插入图片描述

总结

回答文件标题中的问题:

在Java中lambda表达式会生成内部类吗?

lambda表达式会生成内部类,但并不像匿名内部类那样生成一个内部类文件,而是动态的生成内部类。

在Java中是如何生成内部类的?

  1. lambda表达式在编译的时候被编译器生成一个private static 的方法(名字类似lambda$main$0)
  2. 并且会生成invokdynamic字节码指令,调用相应的bootstrap method引导方法。
  3. 该方法能够按照相应的接口动态的生成一个内部类,并将内部类的方法和编译期生成的lambda$main$0方法进行绑定(可以理解为等同),
  4. 最后调用new ConstantCallSite(MethodHandles.constant(samBase, inst));返回调用点。
    1. 这个地方我的理解就是,把按照lambda表达式生成的内部类实例对象与该接口进行绑定,形成一个调用点。

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

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

相关文章

C/C++陷阱——临时变量的产生和特性

C/C陷阱——临时变量的产生和特性 在学习C常引用时&#xff0c;有这样一段代码引起了我的注意&#xff1a; int a 1; double& b a;当我编译这段代码时&#xff0c;竟然报错了&#xff1a; 按理来说&#xff0c;初始化引用时不能涉及权限的放大&#xff08;如用const in…

黑群晖自动调整cpu模式

DS918 黑群晖好像是不支持cpu动态频率&#xff0c;导致cpu一直在最高频率运行&#xff0c;导致功耗比较高&#xff0c;但是某些版本的系统不支持设置为自动根据cpu负载动态调整频率&#xff0c;自己写了个脚本&#xff0c;支持自动根据cpu使用率调整cpu模式 脚本下载链接如下 …

朴实无华的三天每日一题

leetcode八分钟补完三天的每日一题哈哈哈哈 主打一个求同存异 import java.util.Arrays;class Solution {public int singleNumber(int[] nums) {Arrays.sort(nums);int ans 0;for (int i 0; i < nums.length; ) {try {if (nums[i] nums[i 1]) {i 2;} else {ans nums…

讲解 CSS 过渡和动画 — transition/animation (很全面)

前言 由于用户越来越注重 Web应用 的使用体验&#xff0c;随之而来的是 Web应用 需要提供了更加完善的 Web 动画 效果来实现以平滑的状态贯穿于用户的整个使用过程中。现在&#xff0c;这已经是司空见惯了&#xff0c;用户潜意识是希望可以获得更快的反馈响应和更友好的用户界…

2023年全球新能源云母材料市场发展展望分析:储能云母市场规模快速增长[图]

云母作为电气设备的基础材料&#xff0c;下游应用领域涉及高温冶炼、电力等传统行业&#xff0c;并在近几年逐步扩展到新能源汽车、电化学储能等新兴行业。2022年&#xff0c;全球云母材料市场规模保持稳定增长至180.0亿元&#xff0c;期间年复合增长率约为13.2%。预计未来&…

React TreeSelect设置默认展开项的方法

需要实现TreeSelect组件的onTreeExpand、treeExpandedKeys方法。 代码样例如下&#xff1a; 1.TreeSelect标签部分 render() {const {codeselect} this.props;const {treeExpandedKeys} this.state ................<TreeSelectshowSearch{false}dropdownStyle{{ maxHei…

day06_面向对象基础

今日内容 1 复习 2 面向对象编程介绍 3 面向对象 类,属性,方法 ,创建对象,使用对象内存关系 一、复习 周一: jdk,配置环境变量,idea,HelloWorld 变量,基本数据类型 周二: 运算符,if,if-else 周三: if-elseif-elseif-else,循环(while,dowhile,for) 周四: 方法设计(参数,返回值),…

vue3学习(六)--- 插槽slot

文章目录 匿名插槽具名插槽作用域插槽渲染作用域 动态插槽 插槽就是&#xff1a;子组件中的提供给父组件使用的一个占位符&#xff0c;用<slot></slot> 表示&#xff0c;父组件可以在这个占位符中填充任何模板代码&#xff0c;如 HTML、组件等&#xff0c;填充的内…

C++项目实战——基于多设计模式下的同步异步日志系统-⑫-日志宏全局接口设计(代理模式)

文章目录 专栏导读日志宏&全局接口设计全局接口测试项目目录结构整理示例代码拓展示例代码 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计划导师&#xff0c;阿里云专家博主&#xff0c;C…

安全典型配置(三)使用ACL禁止特定用户上网案例

【微|信|公|众|号&#xff1a;厦门微思网络】 安全典型配置&#xff08;一&#xff09;使用ACL限制FTP访问权限案例_厦门微思网络的博客-CSDN博客本例中配置的本地用户登录密码方式为irreversible-cipher&#xff0c;表示对用户密码采用不可逆算法进行加密&#xff0c;非法用…

VulnHub lazysysadmin

一、信息收集 1.nmap扫描开发端口 开放了&#xff1a;22、80、445 访问80端口&#xff0c;没有发现什么有价值的信息 2.扫描共享文件 enum4linux--扫描共享文件 使用&#xff1a; enum4linux 192.168.103.182windows访问共享文件 \\192.168.103.182\文件夹名称信息收集&…

YOLO目标检测——抽烟吸烟数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;公共场所监管、健康风险评估、戒烟干预数据集说明&#xff1a;YOLO目标检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富。使用lableimg标注软件标注&#xff0c;标注框质量高&#xff0c;含voc(xml)、coco(json)和yolo(txt)三…

mask-R-CNN

前言 代码 论文 # Mask-rcnn 算法在 torch vision 中有直接实现&#xff0c;可以直接引用使用在自己的工作中。 import torchvision model torchvision.models.detection.maskrcnn_resnet50_fpn(weightsMaskRCNN_ResNet50_FPN_Weights.DEFAULT)Mask R-CNN&#xff08;Mask R…

uni-app实现拍照功能

直接些这样的组件代码 <template><view><button click"takePhoto">拍照</button><image :src"photoUrl" v-if"photoUrl" mode"aspectFit"></image></view> </template><script&g…

Web(3)网络扫描与window,Linux命令

NMAP各种选项的使用 三种情况修改IP地址总结&#xff1a; 1.为漏洞环境配ip地址 注&#xff1a;1.打开metasploitable后&#xff0c;用mafadmin/msfadmin登录后&#xff0c;重新设置密码&#xff0c;su root登录; 为此创建一个ip地址&#xff0c;命令&#xff1a;ifconfig et…

centos 7.9离线安装wget

1.下载安装包 登录到wget官网上下载最新的wget的rpm安装包到本地 http://mirrors.163.com/centos/7/os/x86_64/Packages/ 2.上传安装包到服务器 3.安装 rpm -ivh wget-1.14-18.el7_6.1.x86_64.rpm 4.查看版本 wget -V

智能测径仪数据采集系统 棒材产线智能化提升方向必备

棒材企业通过学习先进技术和进行技术交流&#xff0c;形成了建设智能化生产线的共识&#xff0c;然而棒材生产线目前存在数据采集效率低、物料跟踪难、质量管控弱、成品质量差、库区混乱、成本居高等问题。通过增加钢坯表面检测、钢坯无头焊接、控轧控冷、自动测径、高速冷床、…

C++DAY46

myWidget::myWidget(QWidget *parent): QWidget(parent) {this->resize(1280,720);this->setWindowTitle("300英雄");this->setWindowIcon(QIcon("D:/BaiduNetdiskDownload/孤独摇滚图标/1.png"));this->setStyleSheet("background-color…

AI电销机器人好不好用关键是什么?

影响AI电销机器人是否好用的两个因素分别是&#xff0c;识别系统以及线路。 有很多电销企业都想找一个好用的AI电销机器人&#xff0c;可是什么样的机器人才是好用的机器人呢?有哪些因素会影响AI电销机器人好不好用呢? 添加图片注释&#xff0c;不超过 140 字&#xff08;可选…

内存访问与栈

内存访问与栈 1 内存分段2 DS和[address]3 mov、add、sub指令形式4 栈4.1 入栈与出栈4.2 SS与SP4.3 空栈4.4 栈顶超界4.5 push、pop指令 5 小结 本文属于《 X86指令基础系列教程》之一&#xff0c;欢迎查看其它文章。 1 内存分段 在x86程序执行时&#xff0c;内存会被分段&am…