首页 > 编程语言 >JDK8:Lambda表达式使用介绍,Lambda表达式源码及原理分析

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

时间:2023-08-07 12:38:22浏览次数:53  
标签:java String 源码 Lambda public 表达式 lambda



文章目录

  • 一、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() {
	@Override
	public 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>(){
		@Override
		public 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_0
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V
       4: return

  public void accept(java.lang.Object);
    Code:
       0: aload_1
       1: checkcast     #14                 // class java/lang/String
       4: invokestatic  #20                 // Method com/itheima/lambda/SourceDemo.lambda$demo$53:(Ljava/lang/String;)V
       7: 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自带的函数接口


标签:java,String,源码,Lambda,public,表达式,lambda
From: https://blog.51cto.com/u_13540373/6992397

相关文章

  • StampedLock使用及源码分析:号称比读写锁还要快的锁
    文章目录一、StampedLock锁概述1、StampedLock锁简介2、ReentrantReadWriteLock回顾3、ReentrantReadWriteLock导致锁饥饿问题4、锁饥饿问题的缓解5、StampedLock与ReentrantReadWriteLock的对比6、StampedLock特点7、StampedLock的缺点二、StampedLock的使用1、StampedLock的三种......
  • 预约上门系统源码开发,改变服务行业的未来
    预约上门系统源码开发是一项复杂而有挑战性的任务,但也是实现智能化预约服务的关键一步。通过自主开发预约上门系统的源码,企业可以完全定制系统的功能、界面和安全性,从而为用户提供更高效、便捷、个性化的预约体验。本文将带你深入了解预约上门系统源码开发的基本步骤,并提供一些示例......
  • RTSP流媒体服务器LntonNVR(源码版)平台硬件更改设备的DNS的具体操作步骤
    LntonNVR作为视频边缘计算网关,提供了软件平台版和硬件设备版两种选择。硬件版本的LntonNVR具有体积小、方便部署等特点,配置完成后可以直接放置在现场,只需通电并连接网络即可使用。因此,很多项目中用户都会选择部署LntonNVR来满足其需求。当用户在配置完固定IP后遇到无法访问域名地址......
  • Pythonre.compile:用于优化正则表达式匹配的工具
    https://blog.csdn.net/www_xuhss_com/article/details/130858409?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-2-130858409-blog-86527810.235%5Ev38%5Epc_relevant_sort_base3&depth_1-utm_......
  • 详解直播应用源码Android端优质技术(三):可变比特率
     直播应用源码平台作为如今一个火爆的平台深受现代人的喜爱,而直播行业也是流行的媒体形式之一,所以不论是直播应用源码的观众用户还是作为直播应用源码的主播用户人数都是巨大的,并且用户地区涵盖了世界各个国家。这时候,直播应用源码平台就需要开发技术来去提高平台的稳定,提升平台......
  • Nacos源码 (2) 核心模块
    整体架构服务管理:实现服务CRUD,域名CRUD,服务健康状态检查,服务权重管理等功能配置管理:实现配置管CRUD,版本管理,灰度管理,监听管理,推送轨迹,聚合数据等功能元数据管理:提供元数据CURD和打标能力插件机制:实现三个模块可分可合能力,实现扩展点SPI机制事件机制:实现异步化事件通知,sdk......
  • 详解直播应用源码Android端优质技术(三):可变比特率
    直播应用源码平台作为如今一个火爆的平台深受现代人的喜爱,而直播行业也是流行的媒体形式之一,所以不论是直播应用源码的观众用户还是作为直播应用源码的主播用户人数都是巨大的,并且用户地区涵盖了世界各个国家。这时候,直播应用源码平台就需要开发技术来去提高平台的稳定,提升平台的质......
  • post请求,go源码会把“+”字符转成了空格导致的验签失败问题
    问题描述:安卓7.29的包客户端书城男女图书页面显示异常,冷启动、下拉刷新等都无法恢复,个别用户清除缓存数据后恢复。(说明:安卓从72880开始的包,客户端书城接口升级为v7:/api/v7/book-store,post请求) 问题原因:线上书城男女图书v7接口part1的post接口请求,个别手机出现401验签失败。......
  • 【JavaScript11】正则表达式 RegExp对象
    定义正则表达式(英语:RegularExpression,在代码中常简写为regex、regexp或RE)使用单个字符串来描述、匹配一系列符合某个句法规则的字符串搜索模式。搜索模式可用于文本搜索和文本替换。创建RexExp对象有两种方式创建RexExp对象第一种:使用字面量创建RegExp对象的语法:第......
  • 正则表达式
    正则表达式常用于在给定的字符串中查询、提取、替换指定的特征值。元字符:元字符可以用于匹配一些特殊规则。1、元字符..可以匹配任意一个字符,但是不能用于匹配换行符。例:x.o,代表匹配“以x开头,之后是一个任意字符,紧跟着是字母o”的字符串。x.o这样的正则表达式,可以匹配xxo、......