面试官:说说Lambda表达式底层原理?

简介: 面试官:说说Lambda表达式底层原理?

Lambda 表达式是 Java 8 引入的一种简洁的表示匿名方法的方式,使用它可以用于替代某些匿名内部类对象,从而让程序更简洁,可读性更好。但 Lambda 表达式的底层是如何实现的呢?接下来我们一起来看。

1.未Lambda表达式

未使用 Lambda 表达式之前,我们创建一个线程,可以这样写:

Thread t1 = new Thread(new Runnable() {
   
   
    @Override
    public void run() {
   
   
        System.out.println("t1");
    }
});
t1.start();

其中 Runnable 匿名内部类,查看 Runnable 源码,我们可以看到 Runnable 的实现如下:

1.1 什么是匿名内部类?

匿名内部类是在 Java 中定义的一个没有名称的内部类。它通常在一个类的成员位置或者方法体内直接定义,并且立即实例化。

匿名内部类的主要用途在于简化代码,避免为了实现一个简单的功能而定义一个完整的类。它特别适用于只需要一次使用的类,比如实现一个接口的单方法(即函数式接口)的场合。

PS:自从 Java 8 引入 Lambda 表达式后,很多原本使用匿名内部类的地方可以被更简洁的 Lambda 表达式替代。

上面代码中的 new Runnable 就是一个标准匿名内部类的使用。

1.2 什么是@FunctionalInterface?

@FunctionalInterface 是 Java 8 引入的一个注解,它用于标记一个接口为函数式接口

函数式接口是指只包含一个抽象方法的接口。这个注解虽然不是必需的,但它提供了一种明确的方式告诉编译器和开发者,这个接口是设计为函数式接口的。

@FunctionalInterface 注解的作用如下:

  1. 编译时检查:当一个接口被标记为 @FunctionalInterface 时,编译器会检查该接口是否只有一个抽象方法。如果不符合函数式接口的定义(即存在多个抽象方法),编译器会报错,提醒开发者修正。这为开发者提供了明确的编译时保障,确保所标记的接口确实符合函数式接口的要求。
  2. 代码明确性:即使不加 @FunctionalInterface 注解,只要接口符合函数式接口的定义,它仍然可以被视为函数式接口。但注解的存在增加了代码的明确性和可读性,使得其他开发者更容易理解该接口的设计意图。
  3. 支持 Lambda 表达式:函数式接口的主要目的是为了支持 Lambda 表达式。通过 Lambda 表达式,开发者可以以更简洁的方式实现函数式接口的抽象方法,从而减少模板代码,使代码更加简洁和易于理解。由于 Lambda 表达式本身不包含类型信息,Java 编译器需要一种机制来确定 Lambda 表达式对应的目标类型。函数式接口就扮演了这一角色——Lambda 表达式可以被赋值给任何兼容的函数式接口类型,编译器会依据接口的唯一抽象方法来推断 Lambda 表达式的参数类型和返回类型。

在 Java 标准库中,有许多使用 @FunctionalInterface 注解的接口,如 java.util.function 包下的 Function、Predicate、Consumer 等,这些接口都是函数式接口,广泛用于数据处理、过滤、转换等操作。此外,在 Spring Boot 框架中,也经常使用函数式接口来定义事件监听器、回调函数等。

2.使用Lambda表达式

未使用 Lambda 表达式之前,我们创建一个线程是这样写的:

Thread t1 = new Thread(new Runnable() {
   
   
    @Override
    public void run() {
   
   
        System.out.println("t1");
    }
});
t1.start();

而用了 Lambda 表达式,我们可以这样写:

Thread t1 = new Thread(() -> {
   
    System.out.println("t1"); });
t1.start();

从上述代码可以看出,当我们使用 Lambda 表达式之后,代码就变得更简洁和优雅了。

3.Lambda详解

Lambda 表达式的语法形式如下:
(parameters) -> expression

或者是:

(parameters) -> {
   
    statements; }
以上语法含义如下:
  1. 参数列表:在圆括号内的部分,用于定义传递给 Lambda 体的参数。参数列表可以为空,也可以包含多个参数,参数之间用逗号隔开。
  2. 箭头符号是 Lambda 表达式的分隔符,将参数列表与表达式或语句块分隔开。
  3. Lambda 体:包含了具体的执行逻辑,可以是一个表达式或是一个由多个语句组成的代码块。

3.1 使用场景

Lambda 表达式主要用于执行函数式接口(Function Interface),即只有一个抽象方法的接口。常见的函数式接口包括 java.util.function 包下的 Predicate、Function、Consumer 等。

3.2 举个例子

假设我们有一个List,并且我们想要对这个列表进行过滤操作,只保留偶数元素。使用 Lambda 表达式可以非常方便地实现这一功能:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
   
   
    public static void main(String[] args) {
   
   
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        // 使用 Lambda 表达式过滤出偶数
        List<Integer> evenNumbers = numbers.stream()
                                           .filter(n -> n % 2 == 0)
                                           .collect(Collectors.toList());
        System.out.println(evenNumbers); // 输出 [2, 4, 6, 8]
    }
}

在这个例子中,n -> n % 2 == 0 是一个 Lambda 表达式,它接受一个整数 n 作为输入参数,并返回一个布尔值。这个 Lambda 表达式被用作 filter 方法的参数,该方法期望一个 Predicate 类型的函数式接口实例。

4.Lambda底层原理

Lambda 底层运行原理如下:

  1. 在程序运行时,会在类中生成一个匿名内部类,匿名内部类会实现接口,并重写接口中的抽象方法。
  2. 类中会生成一个静态方法,静态方法中的代码就是 Lambda 表达式中的代码。
  3. 匿名内部类重写的抽象方法,会调用上一步的静态方法,从而实现 Lambda 代码的执行。

所以,综合来说,Lambda 表达式其实是匿名内部类的语法糖,这个语法糖在程序执行时会进行兑现,也就是生成匿名内部类并进行任务执行。

课后思考

使用 javap 能否看出 Lambda 表达式的执行原理呢?说说具体的操作步骤。

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

相关文章
|
1月前
|
消息中间件 存储 缓存
大厂面试高频:Kafka 工作原理 ( 详细图解 )
本文详细解析了 Kafka 的核心架构和实现原理,消息中间件是亿级互联网架构的基石,大厂面试高频,非常重要,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:Kafka 工作原理 ( 详细图解 )
|
3天前
|
存储 SQL 关系型数据库
MySQL进阶突击系列(03) MySQL架构原理solo九魂17环连问 | 给大厂面试官的一封信
本文介绍了MySQL架构原理、存储引擎和索引的相关知识点,涵盖查询和更新SQL的执行过程、MySQL各组件的作用、存储引擎的类型及特性、索引的建立和使用原则,以及二叉树、平衡二叉树和B树的区别。通过这些内容,帮助读者深入了解MySQL的工作机制,提高数据库管理和优化能力。
|
4月前
|
安全 Java 容器
【Java集合类面试二十七】、谈谈CopyOnWriteArrayList的原理
CopyOnWriteArrayList是一种线程安全的ArrayList,通过在写操作时复制新数组来保证线程安全,适用于读多写少的场景,但可能因内存占用和无法保证实时性而有性能问题。
|
1月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
1月前
|
存储 安全 Java
面试高频:Synchronized 原理,建议收藏备用 !
本文详解Synchronized原理,包括其作用、使用方式、底层实现及锁升级机制。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
面试高频:Synchronized 原理,建议收藏备用 !
|
4月前
|
JavaScript 前端开发
【Vue面试题二十七】、你了解axios的原理吗?有看过它的源码吗?
文章讨论了Vue项目目录结构的设计原则和实践,强调了项目结构清晰的重要性,提出了包括语义一致性、单一入口/出口、就近原则、公共文件的绝对路径引用等原则,并展示了单页面和多页面Vue项目的目录结构示例。
|
2月前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
2月前
|
SQL 存储 关系型数据库
美团面试:binlog、redo log、undo log的底层原理是什么?它们分别实现ACID的哪个特性?
老架构师尼恩在其读者交流群中分享了关于 MySQL 中 redo log、undo log 和 binlog 的面试题及其答案。这些问题涵盖了事务的 ACID 特性、日志的一致性问题、SQL 语句的执行流程等。尼恩详细解释了这些日志的作用、所在架构层级、日志形式、缓存机制以及写文件方式等内容。他还提供了多个面试题的详细解答,帮助读者系统化地掌握这些知识点,提升面试表现。此外,尼恩还推荐了《尼恩Java面试宝典PDF》和其他技术圣经系列PDF,帮助读者进一步巩固知识,实现“offer自由”。
美团面试:binlog、redo log、undo log的底层原理是什么?它们分别实现ACID的哪个特性?
|
23天前
|
安全 算法 网络协议
网易面试:说说 HTTPS 原理?HTTPS 如何保证 数据安全?
45岁老架构师尼恩在其读者交流群中分享了关于HTTP与HTTPS的深入解析,特别针对近期面试中常问的HTTPS相关问题进行了详细解答。文章首先回顾了HTTP的工作原理,指出了HTTP明文传输带来的三大风险:窃听、篡改和冒充。随后介绍了HTTPS如何通过结合非对称加密和对称加密来解决这些问题,确保数据传输的安全性。尼恩还详细解释了HTTPS的握手过程,包括如何通过CA数字证书验证服务器身份,防止中间人攻击。最后,尼恩强调了掌握这些核心技术的重要性,并推荐了自己的技术资料,帮助读者更好地准备面试,提高技术水平。
|
2月前
|
负载均衡 算法 Java
蚂蚁面试:Nacos、Sentinel了解吗?Springcloud 核心底层原理,你知道多少?
40岁老架构师尼恩分享了关于SpringCloud核心组件的底层原理,特别是针对蚂蚁集团面试中常见的面试题进行了详细解析。内容涵盖了Nacos注册中心的AP/CP模式、Distro和Raft分布式协议、Sentinel的高可用组件、负载均衡组件的实现原理等。尼恩强调了系统化学习的重要性,推荐了《尼恩Java面试宝典PDF》等资料,帮助读者更好地准备面试,提高技术实力,最终实现“offer自由”。更多技术资料和指导,可关注公众号【技术自由圈】获取。
蚂蚁面试:Nacos、Sentinel了解吗?Springcloud 核心底层原理,你知道多少?