JDK8:Lambda表达式使用介绍,Lambda表达式源码及原理分析

news/2024/11/20 13:42:28/

文章目录

  • 一、Lambda表达式使用
    • 1、Lambda表达式介绍
    • 2、Lambda使用规范
      • (1)Lambda基础格式
    • 3、Lambda表达式与传统方式比对
      • (1)遍历集合
      • (2)使用Lambda替换匿名内部类使用
      • (3)实现Lambda实现集合排序
  • 二、Lambda表达式底层原理解析
    • 1、反编译lambda
    • 2、静态私有函数生成过程
      • (1)查看内部类的内容
    • 3、forEach分析

一、Lambda表达式使用

1、Lambda表达式介绍

Lambda表达式是Java8中非常重要的一个新特性,其基于函数式编程的思想,支持将代码作为方法参数进行使用。可以
把Lambda表达式理解为通过一种更加简洁的方式表示可传递的匿名函数。

它本身没有名称,而且不像方法那样属于某一个类,但是可以有参数列表、代码体、返回值。使用了Lambda表达式之后就不需要再去编写匿名类了。

2、Lambda使用规范

(1)Lambda基础格式

(参数列表) -> {方法体
}

参数列表:即匿名方法的形参
-> :Lambda运算符
方法体:用于执行业务逻辑。可以是单一语句,也可以是语句块。如果是单一语句,可以省略花括号。当需要返回值,如果方法体中只有一条语句,可以省略return,会自动根据结果进行返回。

// 1)没有参数的Lambda表达式
()->new Student();// 2)只有一个参数的Lambda表达式
x -> {System.out.println(x);return x;
}// 3)有多个参数的Lambda表达式
(int x,int y) ->{System.out.println(x);System.out.println(x);return x+y;
}
// 上述可以进行简写,因为在Lambda中,参数列表中参数的数据类型可以交给JVM根据上下文进行推断。所以可以不用定义类型。
(x,y) ->{System.out.println(x);System.out.println(y);return x+y;
}// 4)一个参数和仅一条语句的Lambda表达式
x -> 3+x;// 5)多个参数和仅一条语句的Lambda表达式
(x,y) -> x+y;

3、Lambda表达式与传统方式比对

(1)遍历集合

public static void main(String[] args) {String[] language = {"c", "c++","c#","java","python","go","hive","php"};List<String> languageList = Arrays.asList(language);//旧的循环方式for (String s : languageList) {System.out.println(s+",");}//lambda循环languageList.forEach(s-> System.out.println(s+","));
}

(2)使用Lambda替换匿名内部类使用

//匿名内部类
Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("Hello world !");}
};//使用Lambda
Runnable runnable1 = ()-> System.out.println("hello world");

(3)实现Lambda实现集合排序

public static void main(String[] args) {String[] language = {"c", "c++","c#","java","python","go","hive","php"};//旧的循环比较方式Arrays.sort(language,new Comparator<String>(){@Overridepublic int compare(String o1, String o2) {return (o1.compareTo(o2));}});//lambda循环比较Arrays.sort(language,(o1,o2)-> (o1.compareTo(o2)));
}

二、Lambda表达式底层原理解析

1、反编译lambda

定义一个使用Lambda表达式的方法:

public class SourceDemo {public static void demo(){String[] language = {"c", "c++","c#","java","python","go","hive","php"};List<String> list = Arrays.asList(language);list.forEach(s-> System.out.println(s));}public static void main(String[] args) {SourceDemo.demo();}
}

将当前.java文件编译生成.class文件,执行命令后,会在当前文件夹生成对应的.class文件

javac SourceDemo.java

将.class文件进行反编译,查看文件内容:

javap -p SourceDemo.class

生成内容如下:

Compiled from "SourceDemo.java"
public class com.itheima.lambda.SourceDemo {public com.itheima.lambda.SourceDemo();public static void demo();public static void main(java.lang.String[]);private static void lambda$demo$0(java.lang.String);
}

此时可以发现,代码中执行Lambda表达式的部分生成了一个静态私有函数。这个静态私有函数的函数干就是Lambda表达式里面的内容。

2、静态私有函数生成过程

那么对于这个静态私有函数,在JDK8内部是如何实现调用的呢?可以查看LambdaMetafactory类,该类下有一个metafactory方法,lambda表达式每一次在执行的时候都会进入到这个方法中,并且为lambda表达式创建一个内部类。

// java.lang.invoke.LambdaMetafactory#metafactory
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();
}

(1)查看内部类的内容

如果想查看内部类里面的内容,可以在lambda表达式执行之前,添加:

System.setProperty("jdk.internal.lambda.dumpProxyClasses", "D://");

这个方法会将运行时生成的内部类class文件进行输出到D盘。
当该文件生成后,可以通过javap -c -p class文件名查看文件中的内容:

final class com.itheima.lambda.SourceDemo$$Lambda$1 implements java.util.function.Consumer {private com.itheima.lambda.SourceDemo$$Lambda$1();Code:0: aload_01: invokespecial #10                 // Method java/lang/Object."<init>":()V4: returnpublic void accept(java.lang.Object);Code:0: aload_11: checkcast     #14                 // class java/lang/String4: invokestatic  #20                 // Method com/itheima/lambda/SourceDemo.lambda$demo$53:(Ljava/lang/String;)V7: return
}

此时可以发现编译后的Lambda表达式已经被执行。

综上所述,Lambda表达式在执行的时候,会调用LambdaMetafactory.metafactory动态的生成内部类,在方法内调用SourceDemo$&Lambda$1,内部类里的调用方法块并不是动态生成的,只是在原class里已经编译生成了一个静态的方法,内部类只需要调用该静态方法。

3、forEach分析

我们点进去这个实例,查看forEach的源码:

default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for (T t : this) {action.accept(t);}
}

我们发现,传递的参数,就是一个Consumer。

而Consumer就是JDK8自带的Function函数:
函数式接口-lambda函数与jdk8自带的函数接口


http://www.ppmy.cn/news/999910.html

相关文章

46.C++模板

今天进行了新的学习&#xff0c;关于c模板的使用。模板是 C 中一种泛型编程的机制&#xff0c;允许在编写代码时使用参数化类型或参数化值。通过模板&#xff0c;可以编写通用的代码&#xff0c;以处理多种不同类型的数据&#xff0c;从而提高代码的复用性和灵活性。 C 中有两…

【Rust 基础篇】Rust动态大小类型:理解动态大小类型与编写安全的代码

导言 Rust是一种以安全性和高效性著称的系统级编程语言&#xff0c;其设计哲学是在不损失性能的前提下&#xff0c;保障代码的内存安全和线程安全。在Rust中&#xff0c;动态大小类型&#xff08;DST&#xff09;是一种特殊的类型&#xff0c;它的大小在编译时无法确定&#x…

C++ 什么时候使用 vector、list、以及 deque?

如果需要高效地快速访问(随即存取)&#xff0c;并且不在乎插入和删除的效率&#xff0c;使用 vector 如果需要大量的插入和删除&#xff0c;而且不关心快速访问 (随即存取) &#xff0c;使用 list 如果需要快速访问 (随即存取) &#xff0c;并且关心两端数据插入和删除&#…

Vue模版语法

先看以下例题是回顾vue的用法 <body><div id"box">{{myname}} - {{myage}}</div><script>var vm new Vue({el:"#box",data:{myname:"lyx",myage:26}})</script></body> 运行结果如下&#xff1a;vue对象被…

windows创建不同大小的文件命令

打开命令窗口&#xff08;windowsR输入cmd打开&#xff09; 输入&#xff1a;fsutil file createnew C:\Users\Desktop\fileTran\10M.txt 10240000&#xff0c;创建10M大小的文件。 文件若存在需要先删除。

MongoDB教程-8

ObjectId 在之前的所有章节中&#xff0c;我们一直在使用MongoDB的Object Id。在本章中&#xff0c;我们将了解ObjectId的结构。 ObjectId是一个12字节的BSON类型&#xff0c;具有以下结构-- 1. 前4个字节代表自unix epoch以来的秒数 接下来的3个字节是机器标识符 接下来的2…

复习之linux系统的引导修复

启动Linux系统时&#xff0c;需要先通电&#xff0c;接着系统会自动进行bios初始化&#xff0c;对硬件进行检测并初始化硬件时钟&#xff0c;之后就进入了 Linux系统引导过程。Linux系统引导过程的具体内容和引导修复方法将在下文中进行详细介绍。由于我们在引导修复时需要利用…

Stable Diffusion - SDXL 1.0 全部样式设计与艺术家风格的配置与提示词

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132072482 来源于 Anna Dittmann 安娜迪特曼&#xff0c;艺术家风格的图像&#xff0c;融合幻想、数字艺术、纹理等样式。 SDXL 是 Stable Diffus…