前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Java】JDK 8 Lambda 表达式参考资料和文章整理

【Java】JDK 8 Lambda 表达式参考资料和文章整理

原创
作者头像
阿东
发布2023-07-21 14:15:16
1960
发布2023-07-21 14:15:16
举报

#java

简介

现在的工作中JDK 8的Lambda表达式基本是需要必备的技能。本文是一篇有关Lambda 表达式的文章和优质资料整理。

第一篇 关于Java Lambda表达式看这一篇就够了

https://objcoding.com/2019/03/04/lambda/

一篇博客,介绍的比较到位,虽然文章很长但是通篇看完完全没有想点右上角的冲动。

此外,目录结构非常清晰。

image.png
image.png

第二篇 Java 8 lambda 表达式10个示例

https://juejin.im/post/5abc9ccc6fb9a028d6643eea

关于Lambda的十个案例。

  • 例1、用lambda表达式实现Runnable
  • 例2、使用Java 8 lambda表达式进行事件处理
  • 例3、使用lambda表达式对列表进行迭代
  • 例4、使用lambda表达式和函数式接口Predicate
  • 例5、如何在lambda表达式中加入Predicate
  • 例6、Java 8中使用lambda表达式的Map和Reduce示例
  • 例6.2、Java 8中使用lambda表达式的Map和Reduce示例
  • 例7、通过过滤创建一个String列表
  • 例8、对列表的每个元素应用函数
  • 例9、复制不同的值,创建一个子列表
  • 例10、计算集合元素的最大值、最小值、总和以及平均值
  • Lambda表达式 vs 匿名类
  • Java 8 Lambda表达式要点
  • 10个Java lambda表达式、流API示例

如何在lambda表达式中加入谓词?

代码如下,其实本质就是把用户的操作“拼接”,包括筛选,合并等操作,依赖java.util.function.Predicate此接口实现。

代码语言:java
复制
// 甚至可以用and()、or()逻辑函数来合并Predicate,
// 例如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
    .filter(startsWithJ.and(fourLetterLong))
    .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n)); 

第三篇 关于Lambda的一个详解(较为简单)

https://segmentfault.com/a/1190000009186509

关于Lambda的一个详解。

第四篇 Oracale官方案例

https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html

虽然做的非常简陋,但是确实是最快的上手案例。

image.png
image.png

第五篇 Java Lambda 表达式入门

https://blog.csdn.net/renfufei/article/details/24600507

比较久远的一个文章,更多是批判的角度看待Lambda,分析的挺有意思的。

译者认为超过3行的逻辑就不适用Lambda表达式了。虽然看着很先进,其实Lambda表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能。

本人建议不要乱用,因为这就和某些很高级的黑客写的代码一样,简洁,难懂,难以调试,维护人员想骂娘.)

扩展

匿名内部类和Lambda表达式的关键区别

结论

匿名内部类this 指向的是匿名内部类的所属对象(也就是Runnable

Lambda:this 指向当前运行的类(也就是Test),也就是当前运行的对象

this的指向对象

  • 匿名内部类:this 指向的是匿名内部类的所属对象
  • Lambda:this 指向当前运行的类,也就是当前运行的对象

jvm层面

  • 匿名内部类:会生成一个 $1到匿名内部类的对象,使用new指令生成对象并且执行
  • Lambda:会生成一个私有方法,使用invokedynamic指令调用

重点部分在匿名内部类的编译过程。为了更好到理解,自己做了一个实验 。

匿名内部类编译过程

  1. 创建一个基本的匿名内部类。
image.png
image.png

代码如下:

代码语言:java
复制
/**
 * 匿名内部类和Lambda的区别
 */
@Test
public void test1() throws InterruptedException {
	new Thread(new Runnable() {
		public void run() {
			String name = Thread.currentThread().getName();
			System.err.println(name + " is run as a thread ");
		}
	}).start();
	Thread.sleep(2000);

}
  1. 查看classes 。编译后的 class 文件如下,看到明显多出来一个LambdaTest1$1.class
image.png
image.png
  1. 打开cmd使用cd命令移动到对应目录下面。或者使用Idea 快速打开对应目录
image.png
image.png

小技巧 1:

编译后到 classes,如果想要直接查看 class 文件的反编译内容,可以直接把 class 文件拖拽到 idea 里面进行打开,就可以看到 class 到反编译内容~~~

  1. 反编译的内容如下,可以看到匿名内部类的编译后的内容经过解释之后,实际上是创建了一个匿名内部类的对象
image.png
image.png
  1. <font color='red'>在第一步的基础上,我们除了打印线程的名称,还可以打印 this 看一下匿名内部类的this 是啥东西</font>。

个人打印之后得到到匿名内部类的 this 如下: com.jdk.Lambda.LambdaTest1$1@2fd014c6

小技巧 2:

如何使用javap 查看java的汇编指令呢?

以上面到案例为例:

打开cmd,进入对应的class目录,我们可以执行javap <options> <classes>的格式进行翻译操作 执行javap -c -l -p xxx,得到结果:

选项的含义?

-help --help -? 输出此用法消息 -version 版本信息,其实是当前javap所在jdk的版本信息,不是class在哪个jdk下生成的。 -v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息) -l 输出行号和本地变量表 -public 仅显示公共类和成员 -protected 显示受保护的/公共类和成员 -package 显示程序包/受保护的/公共类 和成员 (默认) -p -private 显示所有类和成员 -c 对代码进行反汇编 -s 输出内部类型签名 -sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列) -constants 显示静态最终常量 -classpath < path > 指定查找用户类文件的位置 -bootclasspath < path > 覆盖引导类文件的位置

  1. 为了更进一步了解底层的jvm指令执行,使用javap -c -l -p xxx反编译结果如下
代码语言:java
复制
Compiled from "LambdaTest1.java"
public class com.jdk.Lambda.LambdaTest1 {
  public com.jdk.Lambda.LambdaTest1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 11: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   Lcom/jdk/Lambda/LambdaTest1;

  public void test1() throws java.lang.InterruptedException;
    Code:
       0: new           #2                  // class java/lang/Thread
       3: dup
       4: new           #3                  // class com/jdk/Lambda/LambdaTest1$1
       7: dup
       8: aload_0
       9: invokespecial #4                  // Method com/jdk/Lambda/LambdaTest1$1."<init>":(Lcom/jdk/Lambda/LambdaTest1;)V
      12: invokespecial #5                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
      15: invokevirtual #6                  // Method java/lang/Thread.start:()V
      18: ldc2_w        #7                  // long 2000l
      21: invokestatic  #9                  // Method java/lang/Thread.sleep:(J)V
      24: return
    LineNumberTable:
      line 19: 0
      line 24: 15
      line 25: 18
      line 27: 24
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      25     0  this   Lcom/jdk/Lambda/LambdaTest1;
}

可以发现,匿名内部类使用了 new到指令来实现生成匿名对象,调用invokespecial执行内部的run方法

接下来我们使用Lambda简写上面的匿名内部类代码。

Lambda表达式编译过程

  1. 和上面的流程类似,代码如下:
代码语言:java
复制
import java.util.concurrent.TimeUnit;

/**
 * @program: Lambda
 * @description: 测试jdk1.8
 * @author: zhaoxudong
 * @create: 2020-05-05 11:42
 **/
public class Test {

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            System.err.println(" test" + Thread.currentThread().getName() + " is run as a thread " );
        }).start();
        TimeUnit.SECONDS.sleep(5L);
    }
}
  1. 同样的,我们先把class文件放到idea下面,查看反编译之后的代码
代码语言:java
复制
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import java.util.concurrent.TimeUnit;

public class Test {
    public Test() {
    }

    public static void main(String[] args) throws InterruptedException {
        (new Thread(() -> {
            System.err.println(" test" + Thread.currentThread().getName() + " is run as a thread ");
        })).start();
        TimeUnit.SECONDS.sleep(5L);
    }
}
  1. 打开class所在目录,我们会发现Lambda并没有像匿名内部类一样创建了一个$1 的对象,那么他是如何处理的?
  2. 我们使用javap -c -l -p xxx 翻译 class, 得到结果如下:
代码语言:java
复制
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 9: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   LTest;

  public static void main(java.lang.String[]) throws java.lang.InterruptedException;
    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: invokevirtual #5                  // Method java/lang/Thread.start:()V
      15: getstatic     #6                  // Field java/util/concurrent/TimeUnit.SECONDS:Ljava/util/concurrent/TimeUnit;
      18: ldc2_w        #7                  // long 5l
      21: invokevirtual #9                  // Method java/util/concurrent/TimeUnit.sleep:(J)V
      24: return
    LineNumberTable:
      line 12: 0
      line 14: 12
      line 15: 15
      line 16: 24
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      25     0  args   [Ljava/lang/String;

  private static void lambda$main$0();
    Code:
       0: getstatic     #10                 // Field java/lang/System.err:Ljava/io/PrintStream;
       3: new           #11                 // class java/lang/StringBuilder
       6: dup
       7: invokespecial #12                 // Method java/lang/StringBuilder."<init>":()V
      10: ldc           #13                 // String  test
      12: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      15: invokestatic  #15                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
      18: invokevirtual #16                 // Method java/lang/Thread.getName:()Ljava/lang/String;
      21: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: ldc           #17                 // String  is run as a thread
      26: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      29: invokevirtual #18                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      32: invokevirtual #19                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      35: return
    LineNumberTable:
      line 13: 0
      line 14: 35
}

对比匿名内部类和Lambda表达式,可以发现Lambda的不同在于使用了私有方法 加上 invokedynamic指令进行编译,而不会产生一个匿名内部类的对象class文件。

  1. 我们可以看下Lambda的 this打印的是什么东西
代码语言:java
复制
public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            System.err.println(" test" + Thread.currentThread().getName() + " is run as a thread " );
            System.err.println("Lambda this is = " + this);
        }).start();
        TimeUnit.SECONDS.sleep(5L);
    }

这里惊讶的发现,编译居然无法通过!!!! 我们再对比使用匿名内部类的方式:

这种验证方式也可以证明 <font color='red'>Lambda的 this 和匿名内部类的 this 有着本质的区别</font>。

代码语言:java
复制
public static void main(String[] args) throws InterruptedException {
    new Thread(new Runnable() {
        public void run() {
            String name = Thread.currentThread().getName();
            System.err.println(name + " is run as a thread " + this);
        }
    }).start();
    Thread.sleep(2000);
}
  1. 既然无法使用正常方式打印this, 我们换一种方式,那么这里稍微调整代码。

最终代码如下:

代码语言:java
复制
/**
 * @program: Lambda
 * @description: 测试jdk1.8
 * @author: zhaoxudong
 * @create: 2020-05-05 11:42
 **/
public class Test {

    Runnable run = () ->{
        System.err.println(this);
    };

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Test().run).start();
        TimeUnit.SECONDS.sleep(5L);
    }

    @Override
    public String toString() {
        return "test";
    }
}

执行上面的代码,可以得到结果test

在匿名内部类如何得到结果呢?

可以看下面的代码,结果依然是打印 test

代码语言:java
复制
/**
 * @program: smartframwork
 * @description: Lambda表达式学习1
 * @author: zhaoxudong
 * @create: 2020-05-05 10:29
 **/
public class LambdaTest1 {


    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            public void run() {
                String name = Thread.currentThread().getName();
                System.err.println(name + " is run as a thread " + this);
            }

            @Override
            public String toString() {
                return "test";
            }
        }).start();
        Thread.sleep(2000);
    }


    @Override
    public String toString() {
        return "LambdaTest1{}";
    }
}

Java8里面的java.util.Spliterator接口有什么用?

参考博客:Java8里面的java.util.Spliterator接口有什么用?

一句话概括:为了多线程并行遍历元素而设计到迭代器

版本:JDK 8

目的:简化复杂到并行迭代程序编写。

参考:Java7 的Fork/Join(分支/合并)框架。

其他参考资料:

  1. 第一篇:CSDN上面到解答
  2. 第二篇:JAVA8 stream 中Spliterator的使用(一)
  3. 第三篇:JAVA8 stream 中Spliterator的使用(二)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 第一篇 关于Java Lambda表达式看这一篇就够了
  • 第二篇 Java 8 lambda 表达式10个示例
  • 第三篇 关于Lambda的一个详解(较为简单)
  • 第四篇 Oracale官方案例
  • 第五篇 Java Lambda 表达式入门
  • 扩展
    • 匿名内部类和Lambda表达式的关键区别
      • 结论
      • 匿名内部类编译过程
      • Lambda表达式编译过程
    • Java8里面的java.util.Spliterator接口有什么用?
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档