Jdk8新特性

【JavaSE】jdk8新特性 尚硅谷JavaSE笔记——jdk8新特性。个人实践总结,知识记忆架构不一定适合所有人,建议先阅读文章目录 阅读详情

1. Java版本迭代概述

1.1 发布特点(小步快跑,快速迭代)

发行版本发行时间备注
Java 1.01996.01.23Sun公司发布了Java的第一个开发工具包
Java 5.02004.09.30①版本号从1.4直接更新至5.0;②平台更名为JavaSE、JavaEE、JavaME
Java 8.02014.03.18此版本是继Java 5.0以来变化最大的版本。是长期支持版本(LTS)
Java 9.02017.09.22此版本开始,每半年更新一次
Java 10.02018.03.21
Java 11.02018.09.25JDK安装包取消独立JRE安装包,是长期支持版本(LTS)
Java 12.02019.03.19
......
Java17.02021.09发布Java 17.0,版本号也称为21.9,是长期支持版本(LTS)
......
Java19.02022.09发布Java19.0,版本号也称为22.9。

从Java 9 这个版本开始,Java 的计划发布周期是 6个月。

这意味着Java的更新从传统的以特性驱动的发布周期,转变为以时间驱动的发布模式,并且承诺不会跳票。通过这样的方式,开发团队可以把一些关键特性尽早合并到 JDK 之中,以快速得到开发者反馈,在一定程度上避免出现像 Java 9 两次被迫延迟发布的窘况。

针对企业客户的需求,Oracle 将以三年为周期发布长期支持版本(long term support)。

Oracle 的官方观点认为:与 Java 7->8->9 相比,Java 9->10->11的升级和 8->8u20->8u40 更相似。

新模式下的 Java 版本发布都会包含许多变更,包括语言变更和 JVM 变更,这两者都会对 IDE、字节码库和框架产生重大影响。此外,不仅会新增其他 API,还会有 API被删除(这在 Java 8 之前没有发生过)。

目前看这种发布策略是非常成功的,解开了 Java/JVM 演进的许多枷锁,至关重要的是,OpenJDK 的权力中心,正在转移到开发社区和开发者手中。在新的模式中,既可以利用 LTS 满足企业长期可靠支持的需求,也可以满足各种开发者对于新特性迭代的诉求。因为用 2-3 年的最小间隔粒度来试验一个特性,基本是不现实的。

1.2 名词解释

名词解释:Oracle JDK和Open JDK

这两个JDK最大不同就是许可证不一样。但是对于个人用户来讲,没区别。

Oracle JDKOpen JDK
来源Oracle团队维护Oracle和Open Java社区
授权协议Java 17及更高版本 Oracle Java SE 许可证 Java16及更低版本甲骨文免费条款和条件 (NFTC) 许可协议GPL v2许可证
关系由Open JDK构建,增加了少许内容
是否收费2021年9月起Java17及更高版本所有用户免费。 16及更低版本,个人用户、开发用户免费。2017年9月起,所有版本免费
对语法的支持一致一致
名词解释:JEP

JEP(JDK Enhancement Proposals):jdk 改进提案,每当需要有新的设想时候,JEP可以提出非正式的规范(specification),被正式认可的JEP正式写进JDK的发展路线图并分配版本号。

名词解释:LTS

LTS(Long-term Support)即长期支持。Oracle官网提供了对Oracle JDK个别版本的长期支持,即使发发行了新版本,比如目前最新的JDK19,在结束日期前,LTS版本都会被长期支持。(出了bug,会被修复,非LTS则不会再有补丁发布)所以,一定要选一个LTS版本,不然出了漏洞没人修复了。

版本开始日期结束日期延期结束日期
7(LTS)2011年7月2019年7月2022年7月
8(LTS)2014年3月2022年3月2030年12月
11(LTS)2018年9月2023年9月2026年9月
17(LTS)2021年9月2026年9月2029年9月
21(LTS)2023年9月2028年9月2031年9月

如果要选择Oracle JDK,目前可选的LTS版本为8、11、17三个。

1.3 各版本支持时间路线图

1.4 各版本介绍

jdk 9

Java 9 提供了超过150项新功能特性,包括备受期待的模块化系统、可交互的 REPL 工具:jshell,JDK 编译工具,Java 公共 API 和私有代码,以及安全增强、扩展提升、性能管理改善等。

特性太多,查看链接:

JDK 9

jdk 10

JDK 10

286: Local-Variable Type Inference 局部变量类型推断 296: Consolidate the JDK Forest into a Single Repository JDK库的合并 304: Garbage-Collector Interface 统一的垃圾回收接口 307: Parallel Full GC for G1 为G1提供并行的Full GC 310: Application Class-Data Sharing 应用程序类数据(AppCDS)共享 312: Thread-Local Handshakes ThreadLocal握手交互 313: Remove the Native-Header Generation Tool (javah) 移除JDK中附带的javah工具 314: Additional Unicode Language-Tag Extensions 使用附加的Unicode语言标记扩展 316: Heap Allocation on Alternative Memory Devices 能将堆内存占用分配给用户指定的备用内存设备 317: Experimental Java-Based JIT Compiler 使用Graal基于Java的编译器

319: Root Certificates 根证书 322: Time-Based Release Versioning 基于时间定于的发布版本

jdk 11

JDK 11

181: Nest-Based Access Control 基于嵌套的访问控制 309: Dynamic Class-File Constants 动态类文件常量 315: Improve Aarch64 Intrinsics 改进 Aarch64 Intrinsics 318: Epsilon: A No-Op Garbage Collector Epsilon — 一个No-Op(无操作)的垃圾收集器 320: Remove the Java EE and CORBA Modules 删除 Java EE 和 CORBA 模块 321: HTTP Client (Standard) HTTPClient API 323: Local-Variable Syntax for Lambda Parameters 用于 Lambda 参数的局部变量语法 324: Key Agreement with Curve25519 and Curve448 Curve25519 和 Curve448 算法的密钥协议 327: Unicode 10 328: Flight Recorder 飞行记录仪 329: ChaCha20 and Poly1305 Cryptographic Algorithms ChaCha20 和 Poly1305 加密算法 330: Launch Single-File Source-Code Programs 启动单一文件的源代码程序 331: Low-Overhead Heap Profiling 低开销的 Heap Profiling 332: Transport Layer Security (TLS) 1.3 支持 TLS 1.3 333: ZGC: A Scalable Low-Latency Garbage Collector    (Experimental) 可伸缩低延迟垃圾收集器 335: Deprecate the Nashorn JavaScript Engine 弃用 Nashorn JavaScript 引擎 336: Deprecate the Pack200 Tools and API 弃用 Pack200 工具和 API

jdk 12

JDK 12

189:Shenandoah: A Low-Pause-Time Garbage Collector (Experimental) 低暂停时间的GC 230: Microbenchmark Suite 微基准测试套件 325: Switch Expressions (Preview) switch表达式 334: JVM Constants API  JVM常量API 340: One AArch64 Port, Not Two 只保留一个AArch64实现 341: Default CDS Archives 默认类数据共享归档文件 344: Abortable Mixed Collections for G1 可中止的G1 Mixed GC 346: Promptly Return Unused Committed Memory from G1 G1及时返回未使用的已分配内存

jdk 13

JDK 13

350: Dynamic CDS Archives 动态CDS档案 351: ZGC: Uncommit Unused Memory ZGC:取消使用未使用的内存 353: Reimplement the Legacy Socket API 重新实现旧版套接字API 354: Switch Expressions (Preview) switch表达式(预览) 355: Text Blocks (Preview) 文本块(预览)

jdk 14

JDK 14

305: Pattern Matching for instanceof (Preview) instanceof的模式匹配 343: Packaging Tool (Incubator) 打包工具 345: NUMA-Aware Memory Allocation for G1 G1的NUMA-Aware内存分配 349: JFR Event Streaming JFR事件流 352: Non-Volatile Mapped Byte Buffers 非易失性映射字节缓冲区 358: Helpful NullPointerExceptions 实用的NullPointerExceptions 359: Records (Preview) 361: Switch Expressions (Standard) Switch表达式 362: Deprecate the Solaris and SPARC Ports 弃用Solaris和SPARC端口 363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector 删除并发标记扫描(CMS)垃圾回收器 364: ZGC on macOS 365: ZGC on Windows 366: Deprecate the ParallelScavenge + SerialOld GC Combination 弃用ParallelScavenge + SerialOld GC组合 367: Remove the Pack200 Tools and API 删除Pack200工具和API 368: Text Blocks (Second Preview) 文本块 370: Foreign-Memory Access API (Incubator) 外部存储器访问API

jdk 15

JDK 15

339: Edwards-Curve Digital Signature Algorithm (EdDSA) EdDSA 数字签名算法 360: Sealed Classes (Preview) 密封类(预览) 371: Hidden Classes 隐藏类 372: Remove the Nashorn JavaScript Engine 移除 Nashorn JavaScript 引擎 373: Reimplement the Legacy DatagramSocket API 重新实现 Legacy DatagramSocket API 374: Disable and Deprecate Biased Locking 禁用偏向锁定 375: Pattern Matching for instanceof (Second Preview) instanceof 模式匹配(第二次预览) 377: ZGC: A Scalable Low-Latency Garbage Collector ZGC:一个可扩展的低延迟垃圾收集器 378: Text Blocks 文本块 379: Shenandoah: A Low-Pause-Time Garbage Collector Shenandoah:低暂停时间垃圾收集器 381: Remove the Solaris and SPARC Ports 移除 Solaris 和 SPARC 端口 383: Foreign-Memory Access API (Second Incubator) 外部存储器访问 API(第二次孵化版) 384: Records (Second Preview) Records(第二次预览) 385: Deprecate RMI Activation for Removal 废弃 RMI 激活机制

jdk 16

JDK 16

338: Vector API (Incubator) Vector API(孵化器) 347: Enable C++14 Language Features JDK C++的源码中允许使用C++14的语言特性 357: Migrate from Mercurial to Git OpenJDK源码的版本控制从Mercurial (hg) 迁移到git 369: Migrate to GitHub OpenJDK源码的版本控制迁移到github上 376: ZGC: Concurrent Thread-Stack Processing ZGC:并发线程处理 380: Unix-Domain Socket Channels Unix域套接字通道 386: Alpine Linux Port 将glibc的jdk移植到使用musl的alpine linux上 387: Elastic Metaspace 弹性元空间 388: Windows/AArch64 Port 移植JDK到Windows/AArch64 389: Foreign Linker API (Incubator) 提供jdk.incubator.foreign来简化native code的调用 390: Warnings for Value-Based Classes 提供基于值的类的警告 392: Packaging Tool jpackage打包工具转正 393: Foreign-Memory Access API (Third Incubator) 394: Pattern Matching for instanceof Instanceof的模式匹配转正 395: Records Records转正 396: Strongly Encapsulate JDK Internals by Default 默认情况下,封装了JDK内部构件 397: Sealed Classes (Second Preview) 密封类

jdk 17

JDK 17

306: Restore Always-Strict Floating-Point Semantics 恢复始终严格的浮点语义

356: Enhanced Pseudo-Random Number Generators 增强型伪随机数生成器

382: New macOS Rendering Pipeline 新的macOS渲染管道

391: macOS/AArch64 Port macOS/AArch64端口

398: Deprecate the Applet API for Removal 弃用Applet API后续将进行删除

403: Strongly Encapsulate JDK Internals 强封装JDK的内部API

406: Pattern Matching for switch (Preview) switch模式匹配(预览)

407: Remove RMI Activation 删除RMI激活机制

409: Sealed Classes 密封类转正

410: Remove the Experimental AOT and JIT Compiler 删除实验性的AOT和JIT编译器

411: Deprecate the Security Manager for Removal 弃用即将删除的安全管理器

412: Foreign Function & Memory API (Incubator) 外部函数和内存API(孵化特性)

414: Vector API (Second Incubator) Vector API(第二次孵化特性)

415: Context-Specific Deserialization Filters 上下文特定的反序列化过滤器

1.5 JDK各版本下载链接

Java Archive | Oracle

 

1.6 如何学习新特性

对于新特性,我们应该从哪几个角度学习新特性呢?

  • 语法层面:

    • 比如JDK5中的自动拆箱、自动装箱、enum、泛型

    • 比如JDK8中的lambda表达式、接口中的默认方法、静态方法

    • 比如JDK10中局部变量的类型推断

    • 比如JDK12中的switch

    • 比如JDK13中的文本块

  • API层面:

    • 比如JDK8中的Stream、Optional、新的日期时间、HashMap的底层结构

    • 比如JDK9中String的底层结构

    • 新的 / 过时的 API

  • 底层优化

    • 比如JDK8中永久代被元空间替代、新的JS执行引擎

    • 比如新的垃圾回收器、GC参数、JVM的优化

2. Java8新特性:Lambda表达式

2.1 关于Java8新特性简介

Java 8 (又称为 JDK 8或JDK1.8) 是 Java 语言开发的一个主要版本。 Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。

  • 速度更快

  • 代码更少(增加了新的语法:Lambda 表达式)

  • 强大的 Stream API

  • 便于并行

    • 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。

    • Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。

  • 最大化减少空指针异常:Optional

  • Nashorn引擎,允许在JVM上运行JS应用

    • 发音“nass-horn”,是德国二战时一个坦克的命名

    • javascript运行在jvm已经不是新鲜事了,Rhino早在jdk6的时候已经存在。现在替代Rhino,官方的解释是Rhino相比其他JavaScript引擎(比如google的V8)实在太慢了,改造Rhino还不如重写。所以Nashorn的性能也是其一个亮点。

    • Nashorn 项目在 JDK 9 中得到改进;在JDK11 中Deprecated,后续JDK15版本中remove。在JDK11中取以代之的是GraalVM。(GraalVM是一个运行时平台,它支持Java和其他基于Java字节码的语言,但也支持其他语言,如JavaScript,Ruby,Python或LLVM。性能是Nashorn的2倍以上。)

2.2 冗余的匿名内部类

当需要启动一个线程去完成任务时,通常会通过java.lang.Runnable接口来定义任务内容,并使用java.lang.Thread类来启动该线程。代码如下:

package com.atguigu.fp;

public class UseFunctionalProgramming {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多线程任务执行!");
            }
        }).start(); // 启动线程
    }
}

本着“一切皆对象”的思想,这种做法是无可厚非的:首先创建一个Runnable接口的匿名内部类对象来指定任务内容,再将其交给一个线程来启动。

代码分析:

对于Runnable的匿名内部类用法,可以分析出几点内容:

  • Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;

  • 为了指定run的方法体,不得不需要Runnable接口的实现类;

  • 为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;

  • 必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;

  • 而实际上,似乎只有方法体才是关键所在。

2.3 好用的lambda表达式

略

2.4 Lambda 及其使用举例

Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

  • 从匿名类到 Lambda 的转换举例1

  • 从匿名类到 Lambda 的转换举例2

2.5 语法

Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:

  • 左侧:指定了 Lambda 表达式需要的参数列表

  • 右侧:指定了 Lambda 体,是抽象方法的实现逻辑,也即 Lambda 表达式要执行的功能。

语法格式一:无参,无返回值

@Test
public void test1(){
    //未使用Lambda表达式
    Runnable r1 = new Runnable() {
        @Override
        public void run() {
            System.out.println("我爱北京天安门");
        }
    };

    r1.run();

    System.out.println("***********************");

    //使用Lambda表达式
    Runnable r2 = () -> {
        System.out.println("我爱北京故宫");
    };

    r2.run();
}

语法格式二:Lambda 需要一个参数,但是没有返回值。

@Test
public void test2(){
    //未使用Lambda表达式
    Consumer<String> con = new Consumer<String>() {
        @Override
        public void accept(String s) {
            System.out.println(s);
        }
    };
    con.accept("谎言和誓言的区别是什么?");

    System.out.println("*******************");

    //使用Lambda表达式
    Consumer<String> con1 = (String s) -> {
        System.out.println(s);
    };
    con1.accept("一个是听得人当真了,一个是说的人当真了");

}

 语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”

@Test
public void test3(){
    //语法格式三使用前
    Consumer<String> con1 = (String s) -> {
        System.out.println(s);
    };
    con1.accept("一个是听得人当真了,一个是说的人当真了");

    System.out.println("*******************");
    //语法格式三使用后
    Consumer<String> con2 = (s) -> {
        System.out.println(s);
    };
    con2.accept("一个是听得人当真了,一个是说的人当真了");

}

语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略

@Test
public void test4(){
    //语法格式四使用前
    Consumer<String> con1 = (s) -> {
        System.out.println(s);
    };
    con1.accept("一个是听得人当真了,一个是说的人当真了");

    System.out.println("*******************");
    //语法格式四使用后
    Consumer<String> con2 = s -> {
        System.out.println(s);
    };
    con2.accept("一个是听得人当真了,一个是说的人当真了");


}

语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值

@Test
public void test5(){
    //语法格式五使用前
    Comparator<Integer> com1 = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        }
    };

    System.out.println(com1.compare(12,21));
    System.out.println("*****************************");
    //语法格式五使用后
    Comparator<Integer> com2 = (o1,o2) -> {
        System.out.println(o1);
        System.out.println(o2);
        return o1.compareTo(o2);
    };

    System.out.println(com2.compare(12,6));


}

语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略

@Test
public void test6(){
    //语法格式六使用前
    Comparator<Integer> com1 = (o1,o2) -> {
        return o1.compareTo(o2);
    };

    System.out.println(com1.compare(12,6));

    System.out.println("*****************************");
    //语法格式六使用后
    Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);

    System.out.println(com2.compare(12,21));

}

@Test
public void test7(){
    //语法格式六使用前
    Consumer<String> con1 = s -> {
        System.out.println(s);
    };
    con1.accept("一个是听得人当真了,一个是说的人当真了");

    System.out.println("*****************************");
    //语法格式六使用后
    Consumer<String> con2 = s -> System.out.println(s);

    con2.accept("一个是听得人当真了,一个是说的人当真了");

}

2.6 关于类型推断

在语法格式三 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

 举例:

@Test
public void test() {
    //类型推断1
    ArrayList<String> list = new ArrayList<>();
    //类型推断2
    int[] arr = {1, 2, 3};

}

3. Java8新特性:函数式(Functional)接口

3.1 什么是函数式接口

  • 只包含一个抽象方法(Single Abstract Method,简称SAM)的接口,称为函数式接口。当然该接口可以包含其他非抽象方法。

  • 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。

  • 我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

  • 在java.util.function包下定义了Java 8 的丰富的函数式接口

3.2 如何理解函数式接口

  • Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,即Java不但可以支持OOP还可以支持OOF(面向函数编程)

    • Java8引入了Lambda表达式之后,Java也开始支持函数式编程。

    • Lambda表达式不是Java最早使用的。目前C++,C#,Python,Scala等均支持Lambda表达式。

  • 面向对象的思想:

    • 做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情。

  • 函数式编程思想:

    • 只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程。

  • 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。

  • 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。

3.3 举例

举例1:

举例2:

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

3.4 Java 内置函数式接口

3.4.1 之前的函数式接口

之前学过的接口,有些就是函数式接口,比如:

  • java.lang.Runnable

    • public void run()

  • java.lang.Iterable<T>

    • public Iterator<T> iterate()

  • java.lang.Comparable<T>

    • public int compareTo(T t)

  • java.util.Comparator<T>

    • public int compare(T t1, T t2)

3.4.2 四大核心函数式接口
函数式接口称谓参数类型用途
Consumer<T>消费型接口T对类型为T的对象应用操作,包含方法: void accept(T t)
Supplier<T>供给型接口无返回类型为T的对象,包含方法:T get()
Function<T, R>函数型接口T对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)
Predicate<T>判断型接口T确定类型为T的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t)
3.4.3 其它接口

类型1:消费型接口

消费型接口的抽象方法特点:有形参,但是返回值类型是void

接口名抽象方法描述
BiConsumer<T,U>void accept(T t, U u)接收两个对象用于完成功能
DoubleConsumervoid accept(double value)接收一个double值
IntConsumervoid accept(int value)接收一个int值
LongConsumervoid accept(long value)接收一个long值
ObjDoubleConsumer<T>void accept(T t, double value)接收一个对象和一个double值
ObjIntConsumer<T>void accept(T t, int value)接收一个对象和一个int值
ObjLongConsumer<T>void accept(T t, long value)接收一个对象和一个long值

类型2:供给型接口

这类接口的抽象方法特点:无参,但是有返回值

接口名抽象方法描述
BooleanSupplierboolean getAsBoolean()返回一个boolean值
DoubleSupplierdouble getAsDouble()返回一个double值
IntSupplierint getAsInt()返回一个int值
LongSupplierlong getAsLong()返回一个long值

类型3:函数型接口

这类接口的抽象方法特点:既有参数又有返回值

接口名抽象方法描述
UnaryOperator<T>T apply(T t)接收一个T类型对象,返回一个T类型对象结果
DoubleFunction<R>R apply(double value)接收一个double值,返回一个R类型对象
IntFunction<R>R apply(int value)接收一个int值,返回一个R类型对象
LongFunction<R>R apply(long value)接收一个long值,返回一个R类型对象
ToDoubleFunction<T>double applyAsDouble(T value)接收一个T类型对象,返回一个double
ToIntFunction<T>int applyAsInt(T value)接收一个T类型对象,返回一个int
ToLongFunction<T>long applyAsLong(T value)接收一个T类型对象,返回一个long
DoubleToIntFunctionint applyAsInt(double value)接收一个double值,返回一个int结果
DoubleToLongFunctionlong applyAsLong(double value)接收一个double值,返回一个long结果
IntToDoubleFunctiondouble applyAsDouble(int value)接收一个int值,返回一个double结果
IntToLongFunctionlong applyAsLong(int value)接收一个int值,返回一个long结果
LongToDoubleFunctiondouble applyAsDouble(long value)接收一个long值,返回一个double结果
LongToIntFunctionint applyAsInt(long value)接收一个long值,返回一个int结果
DoubleUnaryOperatordouble applyAsDouble(double operand)接收一个double值,返回一个double
IntUnaryOperatorint applyAsInt(int operand)接收一个int值,返回一个int结果
LongUnaryOperatorlong applyAsLong(long operand)接收一个long值,返回一个long结果
BiFunction<T,U,R>R apply(T t, U u)接收一个T类型和一个U类型对象,返回一个R类型对象结果
BinaryOperator<T>T apply(T t, T u)接收两个T类型对象,返回一个T类型对象结果
ToDoubleBiFunction<T,U>double applyAsDouble(T t, U u)接收一个T类型和一个U类型对象,返回一个double
ToIntBiFunction<T,U>int applyAsInt(T t, U u)接收一个T类型和一个U类型对象,返回一个int
ToLongBiFunction<T,U>long applyAsLong(T t, U u)接收一个T类型和一个U类型对象,返回一个long
DoubleBinaryOperatordouble applyAsDouble(double left, double right)接收两个double值,返回一个double结果
IntBinaryOperatorint applyAsInt(int left, int right)接收两个int值,返回一个int结果
LongBinaryOperatorlong applyAsLong(long left, long right)接收两个long值,返回一个long结果

类型4:判断型接口

这类接口的抽象方法特点:有参,但是返回值类型是boolean结果。

接口名抽象方法描述
BiPredicate<T,U>boolean test(T t, U u)接收两个对象
DoublePredicateboolean test(double value)接收一个double值
IntPredicateboolean test(int value)接收一个int值
LongPredicateboolean test(long value)接收一个long值
3.4.4 内置接口代码演示

举例1:

package com.atguigu.four;

import java.util.Arrays;
import java.util.List;

public class TestConsumer {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("java","c","python","c++","VB","C#");
        //遍历Collection集合,并将传递给action参数的操作代码应用在每一个元素上。
        list.forEach(s -> System.out.println(s));
    }
}

举例2:

package com.atguigu.four;

import java.util.function.Supplier;

public class TestSupplier {
    public static void main(String[] args) {
        Supplier<String> supplier = () -> "尚硅谷";
        System.out.println(supplier.get());
    }
}

举例3:

package com.atguigu.four;

import java.util.ArrayList;

public class TestPredicate {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("atguigu");
        list.add("ok");
        list.add("yes");

        System.out.println("删除之前:");
        list.forEach(t-> System.out.println(t));
		
        //用于删除集合中满足filter指定的条件判断的。
        //删除包含o字母的元素
        list.removeIf(s -> s.contains("o"));

        System.out.println("删除包含o字母的元素之后:");
        list.forEach(t-> System.out.println(t));
    }
}

举例4:

package com.atguigu.four;

import java.util.function.Function;

public class TestFunction {
    public static void main(String[] args) {
        //使用Lambda表达式实现Function<T,R>接口,可以实现将一个字符串首字母转为大写的功能。
        Function<String,String> fun = s -> s.substring(0,1).toUpperCase() + s.substring(1);
        System.out.println(fun.apply("hello"));
    }
}
3.4.5 练习

练习1:无参无返回值形式

假如有自定义函数式接口Call如下:

public interface Call {
    void shout();
}

在测试类中声明一个如下方法:

public static void callSomething(Call call){
		call.shout();
}

在测试类的main方法中调用callSomething方法,并用Lambda表达式为形参call赋值,可以喊出任意你想说的话。

public class TestLambda {
	public static void main(String[] args) {
		callSomething(()->System.out.println("回家吃饭"));
		callSomething(()->System.out.println("我爱你"));
		callSomething(()->System.out.println("滚蛋"));
		callSomething(()->System.out.println("回来"));
	}
	public static void callSomething(Call call){
		call.shout();
	}
}
interface Call {
    void shout();
}

练习2:消费型接口

代码示例:Consumer<T>接口

在JDK1.8中Collection集合接口的父接口Iterable接口中增加了一个默认方法:

public default void forEach(Consumer<? super T> action)遍历Collection集合的每个元素,执行“xxx消费型”操作。

在JDK1.8中Map集合接口中增加了一个默认方法:

public default void forEach(BiConsumer<? super K,? super V> action)遍历Map集合的每对映射关系,执行“xxx消费型”操作。

案例:

(1)创建一个Collection系列的集合,添加一些字符串,调用forEach方法遍历查看

(2)创建一个Map系列的集合,添加一些(key,value)键值对,调用forEach方法遍历查看

示例代码:

	@Test
	public void test1(){
		List<String> list = Arrays.asList("hello","java","lambda","atguigu");
		list.forEach(s -> System.out.println(s));
    }
	@Test
	public void test2(){
		HashMap<Integer,String> map = new HashMap<>();
		map.put(1, "hello");
		map.put(2, "java");
		map.put(3, "lambda");
		map.put(4, "atguigu");
		map.forEach((k,v) -> System.out.println(k+"->"+v));
	}

练习3:供给型接口

代码示例:Supplier<T>接口

在JDK1.8中增加了StreamAPI,java.util.stream.Stream<T>是一个数据流。这个类型有一个静态方法:

public static <T> Stream<T> generate(Supplier<T> s)可以创建Stream的对象。而又包含一个forEach方法可以遍历流中的元素:public void forEach(Consumer<? super T> action)。

案例:

现在请调用Stream的generate方法,来产生一个流对象,并调用Math.random()方法来产生数据,为Supplier函数式接口的形参赋值。最后调用forEach方法遍历流中的数据查看结果。

@Test
	public void test2(){
		Stream.generate(() -> Math.random()).forEach(num -> System.out.println(num));
	}

练习4:功能型接口

代码示例:Function<T,R>接口

在JDK1.8时Map接口增加了很多方法,例如:

public default void replaceAll(BiFunction<? super K,? super V,? extends V> function)按照function指定的操作替换map中的value。

public default void forEach(BiConsumer<? super K,? super V> action)遍历Map集合的每对映射关系,执行“xxx消费型”操作。

案例:

(1)声明一个Employee员工类型,包含编号、姓名、薪资。

(2)添加n个员工对象到一个HashMap<Integer,Employee>集合中,其中员工编号为key,员工对象为value。

(3)调用Map的forEach遍历集合

(4)调用Map的replaceAll方法,将其中薪资低于10000元的,薪资设置为10000。

(5)再次调用Map的forEach遍历集合查看结果

Employee类:

class Employee{
	private int id;
	private String name;
	private double salary;
	public Employee(int id, String name, double salary) {
		super();
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	public Employee() {
		super();
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", salary=" + salary + "]";
	}
	
}

测试类:

import java.util.HashMap;

public class TestLambda {
	public static void main(String[] args) {
		HashMap<Integer,Employee> map = new HashMap<>();
		Employee e1 = new Employee(1, "张三", 8000);
		Employee e2 = new Employee(2, "李四", 9000);
		Employee e3 = new Employee(3, "王五", 10000);
		Employee e4 = new Employee(4, "赵六", 11000);
		Employee e5 = new Employee(5, "钱七", 12000);
		
		map.put(e1.getId(), e1);
		map.put(e2.getId(), e2);
		map.put(e3.getId(), e3);
		map.put(e4.getId(), e4);
		map.put(e5.getId(), e5);
		
		map.forEach((k,v) -> System.out.println(k+"="+v));
		System.out.println();
		
		map.replaceAll((k,v)->{
			if(v.getSalary()<10000){
				v.setSalary(10000);
			}
			return v;
		});
		map.forEach((k,v) -> System.out.println(k+"="+v));
	}
}

练习5:判断型接口

代码示例:Predicate<T>接口

JDK1.8时,Collecton<E>接口增加了一下方法,其中一个如下:

public default boolean removeIf(Predicate<? super E> filter) 用于删除集合中满足filter指定的条件判断的。

public default void forEach(Consumer<? super T> action)遍历Collection集合的每个元素,执行“xxx消费型”操作。

案例:

(1)添加一些字符串到一个Collection集合中

(2)调用forEach遍历集合

(3)调用removeIf方法,删除其中字符串的长度<5的

(4)再次调用forEach遍历集合

import java.util.ArrayList;

public class TestLambda {
	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<>();
		list.add("hello");
		list.add("java");
		list.add("atguigu");
		list.add("ok");
		list.add("yes");
		
		list.forEach(str->System.out.println(str));
		System.out.println();
		
		list.removeIf(str->str.length()<5);
		list.forEach(str->System.out.println(str));
	}
}

练习6:判断型接口

案例:

(1)声明一个Employee员工类型,包含编号、姓名、性别,年龄,薪资。

(2)声明一个EmployeeSerice员工管理类,包含一个ArrayList<Employee>集合的属性all,在EmployeeSerice的构造器中,创建一些员工对象,为all集合初始化。

(3)在EmployeeSerice员工管理类中,声明一个方法:ArrayList<Employee> get(Predicate<Employee> p),即将满足p指定的条件的员工,添加到一个新的ArrayList<Employee> 集合中返回。

(4)在测试类中创建EmployeeSerice员工管理类的对象,并调用get方法,分别获取:

  • 所有员工对象

  • 所有年龄超过35的员工

  • 所有薪资高于15000的女员工

  • 所有编号是偶数的员工

  • 名字是“张三”的员工

  • 年龄超过25,薪资低于10000的男员工

示例代码:

Employee类:

public class Employee{
	private int id;
	private String name;
	private char gender;
	private int age;
	private double salary;
	
	public Employee(int id, String name, char gender, int age, double salary) {
		super();
		this.id = id;
		this.name = name;
		this.gender = gender;
		this.age = age;
		this.salary = salary;
	}
	public Employee() {
		super();
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", salary=" + salary
				+ "]";
	}
}

员工管理类

class EmployeeService{
	private ArrayList<Employee> all;
	public EmployeeService(){
		all = new ArrayList<Employee>();
		all.add(new Employee(1, "张三", '男', 33, 8000));
		all.add(new Employee(2, "翠花", '女', 23, 18000));
		all.add(new Employee(3, "无能", '男', 46, 8000));
		all.add(new Employee(4, "李四", '女', 23, 9000));
		all.add(new Employee(5, "老王", '男', 23, 15000));
		all.add(new Employee(6, "大嘴", '男', 23, 11000));
	}
	public ArrayList<Employee> get(Predicate<Employee> p){
		ArrayList<Employee> result = new ArrayList<Employee>();
		for (Employee emp : result) {
			if(p.test(emp)){
				result.add(emp);
			}
		}
		return result;
	}
}

测试类:

public class TestLambda {
	public static void main(String[] args) {
		EmployeeService es = new EmployeeService();
		
		es.get(e -> true).forEach(e->System.out.println(e));
		System.out.println();
		es.get(e -> e.getAge()>35).forEach(e->System.out.println(e));
		System.out.println();
		es.get(e -> e.getSalary()>15000 && e.getGender()=='女').forEach(e->System.out.println(e));
		System.out.println();
		es.get(e -> e.getId()%2==0).forEach(e->System.out.println(e));
		System.out.println();
		es.get(e -> "张三".equals(e.getName())).forEach(e->System.out.println(e));
		System.out.println();
		es.get(e -> e.getAge()>25 && e.getSalary()<10000 && e.getGender()=='男').forEach(e->System.out.println(e));
	}
}

4. Java8新特性:方法引用与构造器引用

Lambda表达式是可以简化函数式接口的变量或形参赋值的语法。而方法引用和构造器引用是为了简化Lambda表达式的。

4.1 方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!

方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。

语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

4.1.1 方法引用格式
  • 格式:使用方法引用操作符 “::” 将类(或对象) 与 方法名分隔开来。

    • 两个:中间不能有空格,而且必须英文状态下半角输入

  • 如下三种主要使用情况:

    • 情况1:对象 :: 实例方法名

    • 情况2:类 :: 静态方法名

    • 情况3:类 :: 实例方法名

4.1.2 方法引用使用前提

要求1:Lambda体只有一句语句,并且是通过调用一个对象的/类现有的方法来完成的

例如:System.out对象,调用println()方法来完成Lambda体

Math类,调用random()静态方法来完成Lambda体

要求2:

针对情况1:函数式接口中的抽象方法a在被重写时使用了某一个对象的方法b。如果方法a的形参列表、返回值类型与方法b的形参列表、返回值类型都相同,则我们可以使用方法b实现对方法a的重写、替换。

针对情况2:函数式接口中的抽象方法a在被重写时使用了某一个类的静态方法b。如果方法a的形参列表、返回值类型与方法b的形参列表、返回值类型都相同,则我们可以使用方法b实现对方法a的重写、替换。

针对情况3:函数式接口中的抽象方法a在被重写时使用了某一个对象的方法b。如果方法a的返回值类型与方法b的返回值类型相同,同时方法a的形参列表中有n个参数,方法b的形参列表有n-1个参数,且方法a的第1个参数作为方法b的调用者,且方法a的后n-1参数与方法b的n-1参数匹配(类型相同或满足多态场景也可以)

例如:t->System.out.println(t)

() -> Math.random() 都是无参

4.1.3 举例
public class MethodRefTest {

	// 情况一:对象 :: 实例方法
	//Consumer中的void accept(T t)
	//PrintStream中的void println(T t)
	@Test
	public void test1() {
		Consumer<String> con1 = str -> System.out.println(str);
		con1.accept("北京");

		System.out.println("*******************");
		PrintStream ps = System.out;
		Consumer<String> con2 = ps::println;
		con2.accept("beijing");
	}
	
	//Supplier中的T get()
	//Employee中的String getName()
	@Test
	public void test2() {
		Employee emp = new Employee(1001,"Tom",23,5600);

		Supplier<String> sup1 = () -> emp.getName();
		System.out.println(sup1.get());

		System.out.println("*******************");
		Supplier<String> sup2 = emp::getName;
		System.out.println(sup2.get());

	}

	// 情况二:类 :: 静态方法
	//Comparator中的int compare(T t1,T t2)
	//Integer中的int compare(T t1,T t2)
	@Test
	public void test3() {
		Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
		System.out.println(com1.compare(12,21));

		System.out.println("*******************");

		Comparator<Integer> com2 = Integer::compare;
		System.out.println(com2.compare(12,3));

	}
	
	//Function中的R apply(T t)
	//Math中的Long round(Double d)
	@Test
	public void test4() {
		Function<Double,Long> func = new Function<Double, Long>() {
			@Override
			public Long apply(Double d) {
				return Math.round(d);
			}
		};

		System.out.println("*******************");

		Function<Double,Long> func1 = d -> Math.round(d);
		System.out.println(func1.apply(12.3));

		System.out.println("*******************");

		Function<Double,Long> func2 = Math::round;
		System.out.println(func2.apply(12.6));
	}

	// 情况三:类 :: 实例方法  (有难度)
	// Comparator中的int comapre(T t1,T t2)
	// String中的int t1.compareTo(t2)
	@Test
	public void test5() {
		Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
		System.out.println(com1.compare("abc","abd"));

		System.out.println("*******************");

		Comparator<String> com2 = String :: compareTo;
		System.out.println(com2.compare("abd","abm"));
	}

	//BiPredicate中的boolean test(T t1, T t2);
	//String中的boolean t1.equals(t2)
	@Test
	public void test6() {
		BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
		System.out.println(pre1.test("abc","abc"));

		System.out.println("*******************");
		BiPredicate<String,String> pre2 = String :: equals;
		System.out.println(pre2.test("abc","abd"));
	}
	
	// Function中的R apply(T t)
	// Employee中的String getName();
	@Test
	public void test7() {
		Employee employee = new Employee(1001, "Jerry", 23, 6000);


		Function<Employee,String> func1 = e -> e.getName();
		System.out.println(func1.apply(employee));

		System.out.println("*******************");
		Function<Employee,String> func2 = Employee::getName;
		System.out.println(func2.apply(employee));
	}

}

4.2 构造器引用

当Lambda表达式是创建一个对象,并且满足Lambda表达式形参,正好是给创建这个对象的构造器的实参列表,就可以使用构造器引用。

格式:类名::new

举例:

public class ConstructorRefTest {
	//构造器引用
    //Supplier中的T get()
    //Employee的空参构造器:Employee()
    @Test
    public void test1(){

        Supplier<Employee> sup = new Supplier<Employee>() {
            @Override
            public Employee get() {
                return new Employee();
            }
        };
        System.out.println("*******************");

        Supplier<Employee>  sup1 = () -> new Employee();
        System.out.println(sup1.get());

        System.out.println("*******************");

        Supplier<Employee>  sup2 = Employee :: new;
        System.out.println(sup2.get());
    }

	//Function中的R apply(T t)
    @Test
    public void test2(){
        Function<Integer,Employee> func1 = id -> new Employee(id);
        Employee employee = func1.apply(1001);
        System.out.println(employee);

        System.out.println("*******************");

        Function<Integer,Employee> func2 = Employee :: new;
        Employee employee1 = func2.apply(1002);
        System.out.println(employee1);

    }

	//BiFunction中的R apply(T t,U u)
    @Test
    public void test3(){
        BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
        System.out.println(func1.apply(1001,"Tom"));

        System.out.println("*******************");

        BiFunction<Integer,String,Employee> func2 = Employee :: new;
        System.out.println(func2.apply(1002,"Tom"));

    }

}
package com.atguigu.java2;

/**
 * @author 尚硅谷-宋红康 邮箱:shkstart@126.com
 */
public class Employee {

	private int id;
	private String name;
	private int age;
	private double salary;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	public Employee() {
		System.out.println("Employee().....");
	}

	public Employee(int id) {
		this.id = id;
		System.out.println("Employee(int id).....");
	}

	public Employee(int id, String name) {
		this.id = id;
		this.name = name;
	}

	public Employee(int id, String name, int age, double salary) {

		this.id = id;
		this.name = name;
		this.age = age;
		this.salary = salary;
	}

	@Override
	public String toString() {
		return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';
	}

}

4.3 数组构造引用

当Lambda表达式是创建一个数组对象,并且满足Lambda表达式形参,正好是给创建这个数组对象的长度,就可以数组构造引用。

格式:数组类型名::new

举例:

//数组引用
//Function中的R apply(T t)
@Test
public void test4(){
    Function<Integer,String[]> func1 = length -> new String[length];
    String[] arr1 = func1.apply(5);
    System.out.println(Arrays.toString(arr1));

    System.out.println("*******************");

    Function<Integer,String[]> func2 = String[] :: new;
    String[] arr2 = func2.apply(10);
    System.out.println(Arrays.toString(arr2));

}

5. Java8新特性:强大的Stream API

5.1 说明

  • Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。

  • Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

  • Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

5.2 为什么要使用Stream API

实际开发中,项目中多数数据源都来自于MySQL、Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理。

5.3 什么是Stream

Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,讲的是数据,而 Stream 是有关计算的,讲的是计算。前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。

注意:

①Stream 自己不会存储元素。

②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。

③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果。

④ Stream一旦执行了终止操作,就不能再调用其它中间操作或终止操作了。

5.4 Stream的操作三个步骤

1- 创建 Stream 一个数据源(如:集合、数组),获取一个流

2- 中间操作 每次处理都会返回一个持有结果的新Stream,即中间操作的方法返回值仍然是Stream类型的对象。因此中间操作可以是个操作链,可对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行。

3- 终止操作(终端操作) 终止操作的方法返回值类型就不再是Stream了,因此一旦执行终止操作,就结束整个Stream操作了。一旦执行终止操作,就执行中间操作链,最终产生结果并结束Stream。

5.4.1 创建Stream实例

方式一:通过集合

Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:

  • default Stream<E> stream() : 返回一个顺序流

  • default Stream<E> parallelStream() : 返回一个并行流

@Test
public void test01(){
    List<Integer> list = Arrays.asList(1,2,3,4,5);

    //JDK1.8中,Collection系列集合增加了方法
    Stream<Integer> stream = list.stream();
}

方式二:通过数组

Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:

  • static <T> Stream<T> stream(T[] array): 返回一个流

  • public static IntStream stream(int[] array)

  • public static LongStream stream(long[] array)

  • public static DoubleStream stream(double[] array)

@Test
public void test02(){
    String[] arr = {"hello","world"};
    Stream<String> stream = Arrays.stream(arr); 
}

@Test
public void test03(){
    int[] arr = {1,2,3,4,5};
    IntStream stream = Arrays.stream(arr);
}

方式三:通过Stream的of()

可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。

  • public static<T> Stream<T> of(T... values) : 返回一个流

@Test
public void test04(){
    Stream<Integer> stream = Stream.of(1,2,3,4,5);
    stream.forEach(System.out::println);
}

方式四:创建无限流(了解)

可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。

  • 迭代 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)

  • 生成 public static<T> Stream<T> generate(Supplier<T> s)

// 方式四:创建无限流
@Test
public void test05() {
	// 迭代
	// public static<T> Stream<T> iterate(final T seed, final
	// UnaryOperator<T> f)
	Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
	stream.limit(10).forEach(System.out::println);

	// 生成
	// public static<T> Stream<T> generate(Supplier<T> s)
	Stream<Double> stream1 = Stream.generate(Math::random);
	stream1.limit(10).forEach(System.out::println);
}
5.4.2 一系列中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

1-筛选与切片

方 法描 述
filter(Predicatep)接收 Lambda , 从流中排除某些元素
distinct()筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前 n 个元素的流。 若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补

2-映 射

方法描述
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

3-排序

方法描述
sorted()产生一个新流,其中按自然顺序排序
sorted(Comparator com)产生一个新流,其中按比较器顺序排序

代码举例:

package com.atguigu.stream;

import org.junit.Test;

import java.util.Arrays;
import java.util.stream.Stream;

public class StreamMiddleOperate {
	@Test
    public void test01(){
        //1、创建Stream
        Stream<Integer> stream = Stream.of(1,2,3,4,5,6);

        //2、加工处理
        //过滤:filter(Predicate p)
        //把里面的偶数拿出来
        /*
         * filter(Predicate p)
         * Predicate是函数式接口,抽象方法:boolean test(T t)
         */
        stream = stream.filter(t -> t%2==0);

        //3、终结操作:例如:遍历
        stream.forEach(System.out::println);
    }
    @Test
    public void test02(){
        Stream.of(1,2,3,4,5,6)
                .filter(t -> t%2==0)
                .forEach(System.out::println);
    }
    @Test
    public void test03(){
        Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
                .distinct()
                .forEach(System.out::println);
    }
    @Test
    public void test04(){
        Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
                .limit(3)
                .forEach(System.out::println);
    }
    @Test
    public void test05(){
        Stream.of(1,2,2,3,3,4,4,5,2,3,4,5,6,7)
                .distinct()  //(1,2,3,4,5,6,7)
                .filter(t -> t%2!=0) //(1,3,5,7)
                .limit(3)
                .forEach(System.out::println);
    }
    @Test
    public void test06(){
        Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
                .skip(5)
                .forEach(System.out::println);
    }
    @Test
    public void test07(){
        Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
                .skip(5)
                .distinct()
                .filter(t -> t%3==0)
                .forEach(System.out::println);
    }
    @Test
    public void test08(){
        long count = Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
                .distinct()
                .peek(System.out::println)  //Consumer接口的抽象方法  void accept(T t)
                .count();
        System.out.println("count="+count);
    }
    @Test
    public void test09(){
        //希望能够找出前三个最大值,前三名最大的,不重复
        Stream.of(11,2,39,4,54,6,2,22,3,3,4,54,54)
                .distinct()
                .sorted((t1,t2) -> -Integer.compare(t1, t2))//Comparator接口  int compare(T t1, T t2)
                .limit(3)
                .forEach(System.out::println);
    }
    @Test
    public void test10(){
        Stream.of(1,2,3,4,5)
                .map(t -> t+=1)//Function<T,R>接口抽象方法 R apply(T t)
                .forEach(System.out::println);
    }
    @Test
    public void test11(){
        String[] arr = {"hello","world","java"};

        Arrays.stream(arr)
                .map(t->t.toUpperCase())
                .forEach(System.out::println);
    }
    @Test
    public void test12(){
        String[] arr = {"hello","world","java"};
        Arrays.stream(arr)
                .flatMap(t -> Stream.of(t.split("|")))//Function<T,R>接口抽象方法 R apply(T t)  现在的R是一个Stream
                .forEach(System.out::println);
    } 
}

5.4.3 终止操作
  • 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。

  • 流进行了终止操作后,不能再次使用。

1-匹配与查找

方法描述
allMatch(Predicate p)检查是否匹配所有元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicate p)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素
count()返回流中元素总数
max(Comparator c)返回流中最大值
min(Comparator c)返回流中最小值
forEach(Consumer c)内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。 相反,Stream API 使用内部迭代——它帮你把迭代做了)

2-归约

方法描述
reduce(T identity, BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回 T
reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回 Optional<T>

备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。

3-收集

方 法描 述
collect(Collector c)将流转换为其他形式。接收一个 Collector接口的实现, 用于给Stream中元素做汇总的方法

Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。

另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

方法返回类型作用
toListCollector<T, ?, List<T>>把流中元素收集到List
 List<Employee> emps= list.stream().collect(Collectors.toList());
方法返回类型作用
toSetCollector<T, ?, Set<T>>把流中元素收集到Set
 Set<Employee> emps= list.stream().collect(Collectors.toSet());
方法返回类型作用
toCollectionCollector<T, ?, C>把流中元素收集到创建的集合
 Collection<Employee> emps =list.stream().collect(Collectors.toCollection(ArrayList::new));
方法返回类型作用
countingCollector<T, ?, Long>计算流中元素的个数
 long count = list.stream().collect(Collectors.counting());
方法返回类型作用
summingIntCollector<T, ?, Integer>对流中元素的整数属性求和
 int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
方法返回类型作用
averagingIntCollector<T, ?, Double>计算流中元素Integer属性的平均值
 double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
方法返回类型作用
summarizingIntCollector<T, ?, IntSummaryStatistics>收集流中Integer属性的统计值。如:平均值
 int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
方法返回类型作用
joiningCollector<CharSequence, ?, String>连接流中每个字符串
 String str= list.stream().map(Employee::getName).collect(Collectors.joining());
方法返回类型作用
maxByCollector<T, ?, Optional<T>>根据比较器选择最大值
 Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
方法返回类型作用
minByCollector<T, ?, Optional<T>>根据比较器选择最小值
 Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
方法返回类型作用
reducingCollector<T, ?, Optional<T>>从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
 int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
方法返回类型作用
collectingAndThenCollector<T,A,RR>包裹另一个收集器,对其结果转换函数
 int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
方法返回类型作用
groupingByCollector<T, ?, Map<K, List<T>>>根据某属性值对流分组,属性为K,结果为V
 Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
方法返回类型作用
partitioningByCollector<T, ?, Map<Boolean, List<T>>>根据true或false进行分区
 Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));

举例:

package com.atguigu.stream;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

public class StreamEndding {
    @Test
    public void test01(){
        Stream.of(1,2,3,4,5)
                .forEach(System.out::println);
    }
    @Test
    public void test02(){
        long count = Stream.of(1,2,3,4,5)
                .count();
        System.out.println("count = " + count);
    }
    @Test
    public void test03(){
        boolean result = Stream.of(1,3,5,7,9)
                .allMatch(t -> t%2!=0);
        System.out.println(result);
    }
	@Test
    public void test04(){
        boolean result = Stream.of(1,3,5,7,9)
                .anyMatch(t -> t%2==0);
        System.out.println(result);
    }
	@Test
    public void test05(){
        Optional<Integer> opt = Stream.of(1,3,5,7,9).findFirst();
        System.out.println(opt);
    }
	@Test
    public void test06(){
        Optional<Integer> opt = Stream.of(1,2,3,4,5,7,9)
                .filter(t -> t%3==0)
                .findFirst();
        System.out.println(opt);
    }
	@Test
    public void test07(){
        Optional<Integer> opt = Stream.of(1,2,4,5,7,8)
                .filter(t -> t%3==0)
                .findFirst();
        System.out.println(opt);
    }
    @Test
    public void test08(){
        Optional<Integer> max = Stream.of(1,2,4,5,7,8)
                .max((t1,t2) -> Integer.compare(t1, t2));
        System.out.println(max);
    }
    @Test
    public void test09(){
        Integer reduce = Stream.of(1,2,4,5,7,8)
                .reduce(0, (t1,t2) -> t1+t2);//BinaryOperator接口   T apply(T t1, T t2)
        System.out.println(reduce);
    }
    @Test
    public void test10(){
        Optional<Integer> max = Stream.of(1,2,4,5,7,8)
                .reduce((t1,t2) -> t1>t2?t1:t2);//BinaryOperator接口   T apply(T t1, T t2)
        System.out.println(max);
    }
    @Test
    public void test11(){
        List<Integer> list = Stream.of(1,2,4,5,7,8)
                .filter(t -> t%2==0)
                .collect(Collectors.toList());

        System.out.println(list);
    }   
}

5.5 Java9新增API

新增1:Stream实例化方法

ofNullable()的使用:

Java 8 中 Stream 不能完全为null,否则会报空指针异常。而 Java 9 中的 ofNullable 方法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可以创建一个空 Stream。

//报NullPointerException
//Stream<Object> stream1 = Stream.of(null);
//System.out.println(stream1.count());

//不报异常,允许通过
Stream<String> stringStream = Stream.of("AA", "BB", null);
System.out.println(stringStream.count());//3

//不报异常,允许通过
List<String> list = new ArrayList<>();
list.add("AA");
list.add(null);
System.out.println(list.stream().count());//2
//ofNullable():允许值为null
Stream<Object> stream1 = Stream.ofNullable(null);
System.out.println(stream1.count());//0

Stream<String> stream = Stream.ofNullable("hello world");
System.out.println(stream.count());//1

iterator()重载的使用:

//原来的控制终止方式:
Stream.iterate(1,i -> i + 1).limit(10).forEach(System.out::println);

//现在的终止方式:
Stream.iterate(1,i -> i < 100,i -> i + 1).forEach(System.out::println);

5.6 练习

现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以 下若干操作步骤:

  1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。

  2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。

  3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。

  4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。

  5. 将两个队伍合并为一个队伍;存储到一个新集合中。

  6. 根据姓名创建 Person 对象;存储到一个新集合中。

  7. 打印整个队伍的Person对象信息。

Person 类的代码为:

public class Person {
    private String name;
    public Person() {}
    public Person(String name) {
        this.name = name;
    }    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Person{name='" + name + "'}";
    }
}

两个队伍(集合)的代码如下:

public static void main(String[] args) {
       //第一支队伍
        ArrayList<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");
        //第二支队伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");
    
		// ....编写代码完成题目要求 
    }

参考答案:

public static void main(String[] args) {
       //第一支队伍
        ArrayList<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");
    
        //第二支队伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");
        
		// 第一个队伍只要名字为3个字的成员姓名;
        // 第一个队伍筛选之后只要前3个人;
        Stream<String> streamOne = one.stream().filter(s ‐> s.length() == 3).limit(3);
    
        // 第二个队伍只要姓张的成员姓名;
        // 第二个队伍筛选之后不要前2个人;
        Stream<String> streamTwo = two.stream().filter(s ‐> s.startsWith("张")).skip(2);
    
        // 将两个队伍合并为一个队伍;
        // 根据姓名创建Person对象;
        // 打印整个队伍的Person对象信息。
        Stream.concat(streamOne, streamTwo).map(Person::new).forEach(System.out::println);
        
}

jdk8新特性 只包含一个抽象方法的接口,称为函数式接口。你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式 抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽 象方法上进行声明)。我们可以在一个接口上使用注解,这样做可以检 查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个 接口是一个函数式接口。在java.util.function包下定义了Java 8 的丰富的函数式接口// 函数式接口中使用泛型:Stream到底是什么呢? 阅读详情

相关推荐

JDK8新特性

JDK8新特性 ​ Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台 内容介绍 了解Java发展史 Lambda表达式 接口的增强 函数式接口 方法引用 Stream API Optional 新时间日期API 其他新特性 一、Java发展历史 1. Java的发展历史 ​ Sun公司在1991年成立了一个称为绿色计划( Gre

小兔子的博客 2225

JDK 8的下载、安装、配置【保姆级教程】

(JDK) 是 Sun 公司(已被 Oracle 收购)针对 Java 开发员的软件开发工具包。自从 Java 推出以来,JDK 已经成为使用最广泛的 Java SDK(Software development kit)。

weixin_62222732的博客 6万+

JAVBus 老司机爬虫.zip

爬虫(Web Crawler)是一种自动化程序,用于从互联网上收集信息。其主要功能是访问网页、提取数据并存储,以便后续分析或展示。爬虫通常由搜索引擎、数据挖掘工具、监测系统等应用于网络数据抓取的场景。 爬虫的工作流程包括以下几个关键步骤: URL收集: 爬虫从一个或多个初始URL开始,递归或迭代地发现新的URL,构建一个URL队列。这些URL可以通过链接分析、站点地图、搜索引擎等方式获取。 请求网页: 爬虫使用HTTP或其他协议向目标URL发起请求,获取网页的HTML内容。这通常通过HTTP请求库实现,如Python中的Requests库。 解析内容: 爬虫对获取的HTML进行解析,提取有用的信息。常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质中,以备后续分析或展示。常用的存储形式包括关系型数据库、NoSQL数据库、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫需要遵守网站的robots.txt协议,限制访问频率和深度,并模拟人类访问行为,如设置User-Agent。 反爬虫应对: 由于爬虫的存在,一些网站采取了反爬虫措施,如验证码、IP封锁等。爬虫工程师需要设计相应的策略来应对这些挑战。 爬虫在各个领域都有广泛的应用,包括搜索引擎索引、数据挖掘、价格监测、新闻聚合等。然而,使用爬虫需要遵守法律和伦理规范,尊重网站的使用政策,并确保对被访问网站的服务器负责。

win10系统安装jdk8全过程

一 下载安装文件    jdk的安装与配置是Java学习的第一步,下面记录一下具体过程。首先根据自己系统下载对应版本。下载地址http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html二 安装    双击exe文件,按照默认设置一步一步安装。三 配置环境变量    右键“此电脑”——“属性”...

destinationnn的博客 9万+

Python金融数据获取工具库之baostock使用详解

在金融数据分析和量化交易中,获取准确及时的市场数据是非常重要的。baostock是一个专门为中国股市数据提供支持的 Python 库,它提供了免费的股票数据接口,用户可以方便地获取股票、指数、基金等各种金融数据。本文将详细介绍baostock库,包括其安装方法、主要特性、基本和高级功能,以及实际应用场景,帮助全面了解并掌握该库的使用。

Rocky006的博客 5340

【全开源】旅游系统源码(Uniapp+FastAdmin+ThinkPHP)

通过源码的灵活配置和定制开发,我们可以根据旅游企业的实际需求,快速构建出符合其业务特点的旅游系统。旅游系统源码,作为这一变革的基石,为旅游业的数字化转型提供了强有力的支撑。通过深入研究和应用旅游系统源码,我们能够构建出更加智能、便捷、个性化的旅游服务平台,满足游客日益增长的旅游需求。国际化发展:随着全球化的深入发展,旅游系统源码也将迎来国际化的发展机遇,为国内外游客提供更加便捷、高效的旅游服务。跨界融合:旅游系统源码将与更多领域进行跨界融合,如与交通、餐饮、购物等行业的结合,为游客提供更加丰富的旅游体验。

2401_84413757的博客 2147

女同事问我JDK8以后有哪些新特性?给我整不会了

整理了JDK9 - JDK16有用的新特性,让你在欢声笑语中掌握它!

陈哈哈的菜园子 1万+

反激电源反馈环路设计:从TL431+PC817原理到动态补偿实战

本文深入解析了反激电源中由TL431和PC817构成的经典反馈环路设计。从静态工作点计算到动态补偿实战,详细阐述了如何通过Type II补偿网络优化环路稳定性与动态响应,并分享了应对光耦CTR离散性、布局噪声等常见调试难题的实用技巧,帮助工程师设计出高性能、高可靠的开关电源。

moon9的博客 697

JDK21 vs JDK8 新特性

摘要: JDK 21作为最新LTS版本,相比JDK 8实现了全面革新。核心差异包括: 语言特性:新增虚拟线程(百万级轻量级并发)、结构化并发、记录模式(深度解构对象)及增强的模式匹配(switch/instanceof); 性能优化:ZGC低延迟垃圾回收、向量API(SIMD加速计算)、紧凑字符串存储; 开发体验:简化单文件程序(无类名main方法)、标准HTTP客户端、字符串模板(类似f-string); 安全性:强制封装内部API、默认TLS 1.3加密; 移除内容:Applet、RMI激活等老旧功能。

csdn_tom_168的博客 3216

AI赋能运筹优化:从参数生成到智能求解的工程实践

运筹学通过线性规划、整数规划等数学模型,为供应链、物流、生产排程等复杂决策问题寻找最优解,其核心价值在于提升资源利用效率与决策科学性。传统方法依赖精确的输入参数与专家经验,面临数据不准、建模门槛高、求解调优难等挑战。人工智能技术的引入,正从原理层面重塑这一流程:通过深度学习模型生成更贴合现实的动态参数(如需求分布、行驶时间),利用自然语言处理降低建模门槛,并借助强化学习对求解器进行智能参数调优。这种AI与运筹学的融合,在工程实践中显著提升了优化系统的自适应能力与求解效率,使其在应对实时交通、柔性生产调度等动

weixin_26913055的博客 595

无人驾驶-2024-09-智能驾驶与机器视觉09:视觉与多传感器融合

从本章开始,我们将详细介绍视觉与多传感器融合技术。自动驾驶中常用的传感器都有其优势和局限性,单一传感器无法应对复杂的驾驶场景。多传感器融合技术旨在综合利用不同的传感器优势,提升感知的精度和鲁棒性。本章首先介绍多传感器融合的相关概念,之后详细介绍典型的融合方法。

u013250861的博客 593

JDK8、JDK17、JDK21新特性

Java 8 使得Java 平台又前进了一大步,尤其是 Stream 流操作,使用的时候非常的爽,整个代码看起来也更加的简洁、直观、舒服!Java21虚拟线程,多线程性能更快、更稳定

longzhutengyue的博客 2207

2025虚幻游戏逆向工程解包尝试

2025虚幻游戏逆向工程解包指南 本文详细介绍了虚幻引擎5游戏逆向解包的完整流程,主要包括获取AES密钥、生成映射文件和导出资源三个关键步骤。作者在实际操作中遇到诸多难题:AES密钥获取工具存在加壳保护问题,映射文件生成器依赖复杂的VS编译环境,DLL注入工具的使用也存在技术门槛。通过多次尝试,作者发现部分游戏可直接搜索现成的AES密钥,而映射文件生成需要额外下载Memcury依赖库才能正常编译。文章提供了相关工具的下载链接和具体操作说明,但同时也指出每个环节都可能出现意外状况,逆向工程实际执行过程远比理论

wsdchong的博客 6145

JDK17新特性深度解析:从 JDK8 到现代 Java 的进化之路

JDK8与JDK17作为Java的长期支持版本,分别代表了不同时代的核心特性。JDK8凭借Lambda、Stream API等革新成为经典,而JDK17通过Record类型、Sealed类、模式匹配、ZGC等新特性,在性能、安全性和开发效率上实现全面提升。

m0_46421522的博客 2452

5.30华为OD机试真题 新系统 - 链表数字游戏 (Java/Py/C/C++/Js/Go)

5.30华为OD机试真题 新系统 - 链表数字游戏 (Java/Py/C/C++/Js/Go)

算法大师! 1114

基于Simulink的电力电子负载特性模拟仿真实例

电阻性负载是最简单的负载形式,其阻抗为纯电阻,电流和电压之间的关系符合欧姆定律。电阻性负载的特点是消耗能量,不会存储能量。本示例将以一个典型的直流电源供电系统为例,展示如何利用Simulink进行不同负载特性的仿真模拟。直流电源:提供固定电压输入。逆变器(可选):根据需要产生PWM调制信号,驱动负载。负载模块:模拟不同类型的负载特性,如电阻性负载、电感性负载和容性负载。测量模块:用于观察负载上的电压和电流波形。系统架构视图plaintext深色版本v| || |

xiaoheshang_123的博客 428

JDK 8 和 JDK 17 的区别和新特性

本文介绍了Java Development Kit (JDK) 8和JDK 17之间的区别和差异,以及它们各自的优势和特点。JDK 8是一种较老的JDK版本,它在性能和稳定性方面得到了广泛的验证和应用。JDK 17是最新版本的JDK,它包括许多新的特性和改进,如基于类的记录(Records)、弱引用垃圾回收器(ZGC)等,这些新特性和改进为Java开发者提供了更多的选择和机会,同时也需要不断学习和更新自己的知识和技能,以适应Java技术的发展和变化。文章提供了部分简单代码使用示例

qq_37686995的博客 1万+

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

JDK17 vs JDK8 新特性

JDK17相比JDK8带来了重大革新:语言特性方面新增模式匹配、密封类、记录类、文本块和Switch表达式,大幅简化代码;API升级包括HTTP Client标准化、集合框架增强和NIO优化;性能上引入ZGC和Shenandoah垃圾回收器,提升JIT编译器;安全方面禁用SHA-1并支持新加密算法。建议新项目直接采用JDK17,存量项目可逐步迁移以利用新特性和性能优势,同时注意移除功能的替代方案。JDK17在代码简洁性、现代化API、性能优化和安全性方面全面超越JDK8。

csdn_tom_168的博客 1838

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

jdk8新特性(jdk8新特性面试)

jdk8.101和jdk8.102的区别 在JDK1.7的新特性方面主要有下面几方面的增强: 1.jdk7语法上 1.1二进制变量的表示,支持将整数类型用二进制来表示,用0b开头。 1.2 Switch语句支持string类型 1.3 Try-with-resource语句 注意:实现java.la...

计算机毕业论文源码,学生个人网页制作html源码。贴近用户做网络推广和互联网优化。 1293

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

JDK8到JDK17都升级了那些新特性?又有哪些能常用好用的?

Java依然是服务器端开发、Android移动应用和大数据处理等领域的首选语言之一。凭借其稳定性、广泛的生态系统及不断的技术改进,Java维持着强大的生命力和竞争力。

Tamate的博客 1129

【JavaSE之JDK8新特性】三万字详文带你了解JDK8新特性

1.了解了什么是lambda 表达式,lambda 表达式和匿名内部类的区别; 2.了解了默认方法是个啥,JDK8使得接口里也可以有静态方法了; 3.介绍了四个函数式接口,Supplier,Consumer,Function,Predicate; 4.方法引用的概念以及其中的语法格式; 5.引入了Stream流(遍历数据集的高级迭代器); 6.介绍了Stream的常用方法,学习了如何更好的处理数据

qq_63691275的博客 2752

探索JDK8的新特性

Java 8这个版本提供了很多实用的新特性,针对接口推出了接口默认方法,接口静态方法以及函数式接口,同时为了简化代码编写,推出了lambda表达式,为了增强对数据的操作,还定义了Stream操作等。这个版本目前是市场上一个应用最广泛,也是最重要的一个版本。JDK8中对接口规范进行了新的定义,允许在接口中定义默认方法(使用default关键字修饰),静态方法,同时还推出了函数式接口(使用@FunctionInterface注解描述)设计。

南 阳 的博客 1861

JDK8升级到JDK17的新特性

随着 AI 时代的浪潮席卷而来,人工智能技术在各行各业的应用愈发广泛,开发领域也迎来了新的变革。为了更好地适配 AI 场景的开发需求,SpringAI 已正式发布,它为开发者提供了构建 AI 应用的强大工具和框架。而要使用 SpringAI,与之配套的 Springboot 需要升级到 3.X 版本,这就对 JDK 的版本提出了新的要求 ——Springboot 3.X 明确要求 JDK 必须在 17 及以上版本。​。

LLLLLiSHI的博客 1184

jdk8新特性_JDK8的十大新特性介绍

Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。JDK8新特性: 1.Lambda表达式 2.新增Stream类 3.引入Optional 4.使用Base64...

weixin_39714191的博客 858
上一篇: Java对象与Json字符串的相互转化
Java100q
博客等级 码龄4年 45粉丝 7原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值