常州app网站,博客和网站的区别,微信机器人 wordpress 插件,一级消防工程师考试大纲1. Java版本迭代概述
1.1 发布特点#xff08;小步快跑#xff0c;快速迭代#xff09;
发行版本发行时间备注Java 1.01996.01.23Sun公司发布了Java的第一个开发工具包Java 5.02004.09.30①版本号从1.4直接更新至5.0#xff1b;②平台更名为JavaSE、JavaEE、JavaMEJava 8…
1. Java版本迭代概述
1.1 发布特点小步快跑快速迭代
发行版本发行时间备注Java 1.01996.01.23Sun公司发布了Java的第一个开发工具包Java 5.02004.09.30①版本号从1.4直接更新至5.0②平台更名为JavaSE、JavaEE、JavaMEJava 8.02014.03.18此版本是继Java 5.0以来变化最大的版本。是长期支持版本LTSJava 9.02017.09.22此版本开始每半年更新一次Java 10.02018.03.21Java 11.02018.09.25JDK安装包取消独立JRE安装包是长期支持版本LTSJava 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 名词解释
1.2.1 名词解释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月起所有版本免费对语法的支持一致一致
1.2.2 名词解释JEP
JEP(JDK Enhancement Proposals)jdk 改进提案每当需要有新的设想时候JEP可以提出非正式的规范(specification)被正式认可的JEP正式写进JDK的发展路线图并分配版本号。
1.2.3 名词解释LTS
LTSLong-term Support即长期支持。Oracle官网提供了对Oracle JDK个别版本的长期支持即使发发行了新版本比如目前最新的JDK19在结束日期前LTS版本都会被长期支持。出了bug会被修复非LTS则不会再有补丁发布所以一定要选一个LTS版本不然出了漏洞没人修复了。
版本开始日期结束日期延期结束日期7LTS2011年7月2019年7月2022年7月8LTS2014年3月2022年3月2030年12月11LTS2018年9月2023年9月2026年9月17LTS2021年9月2026年9月2029年9月21LTS2023年9月2028年9月2031年9月
如果要选择Oracle JDK目前可选的LTS版本为8、11、17三个。
1.3 各版本支持时间路线图 1.4 各版本介绍 1.4.1 jdk 9
Java 9 提供了超过150项新功能特性包括备受期待的模块化系统、可交互的 REPL 工具jshellJDK 编译工具Java 公共 API 和私有代码以及安全增强、扩展提升、性能管理改善等。
特性太多查看链接https://openjdk.java.net/projects/jdk9/
1.4.2 jdk 10
https://openjdk.java.net/projects/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 基于时间定于的发布版本 1.4.3 jdk 11
https://openjdk.java.net/projects/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 1.4.4 jdk 12
https://openjdk.java.net/projects/jdk/12/ 189Shenandoah: 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及时返回未使用的已分配内存 1.4.5 jdk 13
https://openjdk.java.net/projects/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) 文本块预览 1.4.6 jdk 14
https://openjdk.java.net/projects/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 1.4.7 jdk 15
https://openjdk.java.net/projects/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 激活机制 1.4.8 jdk 16
https://openjdk.java.net/projects/jdk/16/ 338: Vector API (Incubator) Vector API孵化器 347: Enable C14 Language Features JDK C的源码中允许使用C14的语言特性 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) 密封类 1.4.9 jdk 17
https://openjdk.java.net/projects/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各版本下载链接
https://www.oracle.com/java/technologies/downloads/archive/链接https://pan.baidu.com/s/15QrBUOvfE9vjlTzN_EeVLg 提取码yyds 1.6 如何学习新特性
对于新特性我们应该从哪几个角度学习新特性呢
语法层面 比如JDK5中的自动拆箱、自动装箱、enum、泛型比如JDK8中的lambda表达式、接口中的默认方法、静态方法比如JDK10中局部变量的类型推断比如JDK12中的switch比如JDK13中的文本块 API层面 比如JDK8中的Stream、Optional、新的日期时间、HashMap的底层结构比如JDK9中String的底层结构新的 / 过时的 API 底层优化 比如JDK8中永久代被元空间替代、新的JS执行引擎比如新的垃圾回收器、GC参数、JVM的优化
二 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() 在并行流与顺序流之间进行切换。 最大化减少空指针异常OptionalNashorn引擎允许在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字节码的语言但也支持其他语言如JavaScriptRubyPython或LLVM。性能是Nashorn的2倍以上。
2.2 冗余的匿名内部类
当需要启动一个线程去完成任务时通常会通过java.lang.Runnable接口来定义任务内容并使用java.lang.Thread类来启动该线程。代码如下
public class UseFunctionalProgramming {public static void main(String[] args) {new Thread(new Runnable() {Overridepublic void run() {System.out.println(多线程任务执行);}}).start(); // 启动线程}
}本着“一切皆对象”的思想这种做法是无可厚非的首先创建一个Runnable接口的匿名内部类对象来指定任务内容再将其交给一个线程来启动。
代码分析
对于Runnable的匿名内部类用法可以分析出几点内容
Thread类需要Runnable接口作为参数其中的抽象run方法是用来指定线程任务内容的核心为了指定run的方法体不得不需要Runnable接口的实现类为了省去定义一个RunnableImpl实现类的麻烦不得不使用匿名内部类必须覆盖重写抽象run方法所以方法名称、方法参数、方法返回值不得不再写一遍且不能写错而实际上似乎只有方法体才是关键所在。
2.3 Lambda 及其使用举例
Lambda 是一个匿名函数我们可以把 Lambda 表达式理解为是一段可以传递的代码将代码像数据一样进行传递。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格使Java的语言表达能力得到了提升。
2.3.1 从匿名类到 Lambda 的转换 2.4 语法
Lambda 表达式在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 “-” 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分
左侧指定了 Lambda 表达式需要的参数列表右侧指定了 Lambda 体是抽象方法的实现逻辑也即 Lambda 表达式要执行的功能。
2.4.1 语法格式一无参无返回值
Test
public void test1(){//未使用Lambda表达式Runnable r1 new Runnable() {Overridepublic void run() {System.out.println(我爱北京天安门);}};r1.run();System.out.println(***********************);//使用Lambda表达式Runnable r2 () - {System.out.println(我爱北京故宫);};r2.run();
}2.4.2 Lambda 需要一个参数但是没有返回值。
Test
public void test2(){//未使用Lambda表达式ConsumerString con new ConsumerString() {Overridepublic void accept(String s) {System.out.println(s);}};con.accept(谎言和誓言的区别是什么);System.out.println(*******************);//使用Lambda表达式ConsumerString con1 (String s) - {System.out.println(s);};con1.accept(一个是听得人当真了一个是说的人当真了);}2.4.3 数据类型可以省略因为可由编译器推断得出称为“类型推断”
Test
public void test3(){//语法格式三使用前ConsumerString con1 (String s) - {System.out.println(s);};con1.accept(一个是听得人当真了一个是说的人当真了);System.out.println(*******************);//语法格式三使用后ConsumerString con2 (s) - {System.out.println(s);};con2.accept(一个是听得人当真了一个是说的人当真了);}2.4.4 Lambda 若只需要一个参数时参数的小括号可以省略
Test
public void test4(){//语法格式四使用前ConsumerString con1 (s) - {System.out.println(s);};con1.accept(一个是听得人当真了一个是说的人当真了);System.out.println(*******************);//语法格式四使用后ConsumerString con2 s - {System.out.println(s);};con2.accept(一个是听得人当真了一个是说的人当真了);
}2.4.5 Lambda 需要两个或以上的参数多条执行语句并且可以有返回值
Test
public void test5(){//语法格式五使用前ComparatorInteger com1 new ComparatorInteger() {Overridepublic 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(*****************************);//语法格式五使用后ComparatorInteger com2 (o1,o2) - {System.out.println(o1);System.out.println(o2);return o1.compareTo(o2);};System.out.println(com2.compare(12,6));
}2.4.6 当 Lambda 体只有一条语句时return 与大括号若有都可以省略
Test
public void test6(){//语法格式六使用前ComparatorInteger com1 (o1,o2) - {return o1.compareTo(o2);};System.out.println(com1.compare(12,6));System.out.println(*****************************);//语法格式六使用后ComparatorInteger com2 (o1,o2) - o1.compareTo(o2);System.out.println(com2.compare(12,21));}Test
public void test7(){//语法格式六使用前ConsumerString con1 s - {System.out.println(s);};con1.accept(一个是听得人当真了一个是说的人当真了);System.out.println(*****************************);//语法格式六使用后ConsumerString con2 s - System.out.println(s);con2.accept(一个是听得人当真了一个是说的人当真了);}2.5 关于类型推断
在语法格式三 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型程序依然可以编译这是因为 javac 根据程序的上下文在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境是由编译器推断出来的。这就是所谓的“类型推断”。 举例
Test
public void test() {//类型推断1ArrayListString list new ArrayList();//类型推断2int[] arr {1, 2, 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最早使用的。目前CC#PythonScala等均支持Lambda表达式。 面向对象的思想 做一件事情找一个能解决这个事情的对象调用对象的方法完成事情。 函数式编程思想 只要能获取到结果谁去做的怎么做的都不重要重视的是结果不重视过程。 在函数式编程语言当中函数被当做一等公民对待。在将函数作为一等公民的编程语言中Lambda表达式的类型是函数。但是在Java8中有所不同。在Java8中Lambda表达式是对象而不是函数它们必须依附于一类特别的对象类型——函数式接口。简单的说在Java8中Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说只要一个对象是函数式接口的实例那么该对象就可以用Lambda表达式来表示。
3.3 举例
举例1 举例2 作为参数传递 Lambda 表达式 作为参数传递 Lambda 表达式为了将 Lambda 表达式作为参数传递接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。 3.4 Java 内置函数式接口
3.4.1 之前的函数式接口
之前学过的接口有些就是函数式接口比如
java.lang.Runnable public void run() java.lang.Iterable public Iterator iterate() java.lang.Comparable public int compareTo(T t) java.util.Comparator public int compare(T t1, T t2)
3.4.2 四大核心函数式接口
函数式接口称谓参数类型用途ConsumerT 消费型接口T对类型为T的对象应用操作包含方法 void accept(T t) SupplierT 供给型接口无返回类型为T的对象包含方法T get() FunctionT, R 函数型接口T对类型为T的对象应用操作并返回结果。结果是R类型的对象。包含方法R apply(T t) PredicateT 判断型接口T确定类型为T的对象是否满足某约束并返回 boolean 值。包含方法boolean test(T t)
3.4.3 其它接口
类型1消费型接口
消费型接口的抽象方法特点有形参但是返回值类型是void
接口名抽象方法描述BiConsumerT,Uvoid accept(T t, U u)接收两个对象用于完成功能DoubleConsumervoid accept(double value)接收一个double值IntConsumervoid accept(int value)接收一个int值LongConsumervoid accept(long value)接收一个long值ObjDoubleConsumervoid accept(T t, double value)接收一个对象和一个double值ObjIntConsumervoid accept(T t, int value)接收一个对象和一个int值ObjLongConsumervoid accept(T t, long value)接收一个对象和一个long值
类型2供给型接口
这类接口的抽象方法特点无参但是有返回值
接口名抽象方法描述BooleanSupplierboolean getAsBoolean()返回一个boolean值DoubleSupplierdouble getAsDouble()返回一个double值IntSupplierint getAsInt()返回一个int值LongSupplierlong getAsLong()返回一个long值
类型3函数型接口
这类接口的抽象方法特点既有参数又有返回值
接口名抽象方法描述UnaryOperatorT apply(T t)接收一个T类型对象返回一个T类型对象结果DoubleFunctionR apply(double value)接收一个double值返回一个R类型对象IntFunctionR apply(int value)接收一个int值返回一个R类型对象LongFunctionR apply(long value)接收一个long值返回一个R类型对象ToDoubleFunctiondouble applyAsDouble(T value)接收一个T类型对象返回一个doubleToIntFunctionint applyAsInt(T value)接收一个T类型对象返回一个intToLongFunctionlong applyAsLong(T value)接收一个T类型对象返回一个longDoubleToIntFunctionint 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值返回一个doubleIntUnaryOperatorint applyAsInt(int operand)接收一个int值返回一个int结果LongUnaryOperatorlong applyAsLong(long operand)接收一个long值返回一个long结果BiFunctionT,U,RR apply(T t, U u)接收一个T类型和一个U类型对象返回一个R类型对象结果BinaryOperatorT apply(T t, T u)接收两个T类型对象返回一个T类型对象结果ToDoubleBiFunctionT,Udouble applyAsDouble(T t, U u)接收一个T类型和一个U类型对象返回一个doubleToIntBiFunctionT,Uint applyAsInt(T t, U u)接收一个T类型和一个U类型对象返回一个intToLongBiFunctionT,Ulong applyAsLong(T t, U u)接收一个T类型和一个U类型对象返回一个longDoubleBinaryOperatordouble 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结果。
接口名抽象方法描述BiPredicateT,Uboolean test(T t, U u)接收两个对象DoublePredicateboolean test(double value)接收一个double值IntPredicateboolean test(int value)接收一个int值LongPredicateboolean test(long value)接收一个long值
3.4.4 内置接口代码演示
举例1
public class TestConsumer {public static void main(String[] args) {ListString list Arrays.asList(java,c,python,c,VB,C#);//遍历Collection集合并将传递给action参数的操作代码应用在每一个元素上。list.forEach(s - System.out.println(s));}
}举例2
public class TestSupplier {public static void main(String[] args) {SupplierString supplier () - zyy;System.out.println(supplier.get());}
}举例3
public class TestPredicate {public static void main(String[] args) {ArrayListString 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
public class TestFunction {public static void main(String[] args) {//使用Lambda表达式实现FunctionT,R接口可以实现将一个字符串首字母转为大写的功能。FunctionString,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接口
在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方法遍历查看
示例代码
Testpublic void test1(){ListString list Arrays.asList(hello,java,lambda,atguigu);list.forEach(s - System.out.println(s));}Testpublic void test2(){HashMapInteger,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接口
在JDK1.8中增加了StreamAPIjava.util.stream.Stream是一个数据流。这个类型有一个静态方法
public static T StreamT generate(SupplierT s)可以创建Stream的对象。而又包含一个forEach方法可以遍历流中的元素public void forEach(Consumer? super T action)。
案例
现在请调用Stream的generate方法来产生一个流对象并调用Math.random()方法来产生数据为Supplier函数式接口的形参赋值。最后调用forEach方法遍历流中的数据查看结果。
Testpublic void test2(){Stream.generate(() - Math.random()).forEach(num - System.out.println(num));}练习4功能型接口
代码示例FunctionT,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个员工对象到一个HashMapInteger,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;}Overridepublic String toString() {return Employee [id id , name name , salary salary ];}
}测试类
public class TestLambda {public static void main(String[] args) {HashMapInteger,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(kv));System.out.println();map.replaceAll((k,v)-{if(v.getSalary()10000){v.setSalary(10000);}return v;});map.forEach((k,v) - System.out.println(kv));}
}练习5判断型接口
代码示例Predicate接口
JDK1.8时Collecton接口增加了一下方法其中一个如下
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遍历集合
public class TestLambda {public static void main(String[] args) {ArrayListString 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集合的属性all在EmployeeSerice的构造器中创建一些员工对象为all集合初始化。
3在EmployeeSerice员工管理类中声明一个方法ArrayList get(Predicate p)即将满足p指定的条件的员工添加到一个新的ArrayList 集合中返回。
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;}Overridepublic String toString() {return Employee [id id , name name , gender gender , age age , salary salary ];}
}员工管理类
class EmployeeService{private ArrayListEmployee all;public EmployeeService(){all new ArrayListEmployee();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 ArrayListEmployee get(PredicateEmployee p){ArrayListEmployee result new ArrayListEmployee();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()%20).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));}
}四 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)Testpublic void test1() {ConsumerString con1 str - System.out.println(str);con1.accept(北京);System.out.println(*******************);PrintStream ps System.out;ConsumerString con2 ps::println;con2.accept(beijing);}//Supplier中的T get()//Employee中的String getName()Testpublic void test2() {Employee emp new Employee(1001,Tom,23,5600);SupplierString sup1 () - emp.getName();System.out.println(sup1.get());System.out.println(*******************);SupplierString sup2 emp::getName;System.out.println(sup2.get());}// 情况二类 :: 静态方法//Comparator中的int compare(T t1,T t2)//Integer中的int compare(T t1,T t2)Testpublic void test3() {ComparatorInteger com1 (t1,t2) - Integer.compare(t1,t2);System.out.println(com1.compare(12,21));System.out.println(*******************);ComparatorInteger com2 Integer::compare;System.out.println(com2.compare(12,3));}//Function中的R apply(T t)//Math中的Long round(Double d)Testpublic void test4() {FunctionDouble,Long func new FunctionDouble, Long() {Overridepublic Long apply(Double d) {return Math.round(d);}};System.out.println(*******************);FunctionDouble,Long func1 d - Math.round(d);System.out.println(func1.apply(12.3));System.out.println(*******************);FunctionDouble,Long func2 Math::round;System.out.println(func2.apply(12.6));}// 情况三类 :: 实例方法 (有难度)// Comparator中的int comapre(T t1,T t2)// String中的int t1.compareTo(t2)Testpublic void test5() {ComparatorString com1 (s1,s2) - s1.compareTo(s2);System.out.println(com1.compare(abc,abd));System.out.println(*******************);ComparatorString com2 String :: compareTo;System.out.println(com2.compare(abd,abm));}//BiPredicate中的boolean test(T t1, T t2);//String中的boolean t1.equals(t2)Testpublic void test6() {BiPredicateString,String pre1 (s1,s2) - s1.equals(s2);System.out.println(pre1.test(abc,abc));System.out.println(*******************);BiPredicateString,String pre2 String :: equals;System.out.println(pre2.test(abc,abd));}// Function中的R apply(T t)// Employee中的String getName();Testpublic void test7() {Employee employee new Employee(1001, Jerry, 23, 6000);FunctionEmployee,String func1 e - e.getName();System.out.println(func1.apply(employee));System.out.println(*******************);FunctionEmployee,String func2 Employee::getName;System.out.println(func2.apply(employee));}}4.2 构造器引用
当Lambda表达式是创建一个对象并且满足Lambda表达式形参正好是给创建这个对象的构造器的实参列表就可以使用构造器引用。
格式类名::new
举例
public class ConstructorRefTest {//构造器引用//Supplier中的T get()//Employee的空参构造器Employee()Testpublic void test1(){SupplierEmployee sup new SupplierEmployee() {Overridepublic Employee get() {return new Employee();}};System.out.println(*******************);SupplierEmployee sup1 () - new Employee();System.out.println(sup1.get());System.out.println(*******************);SupplierEmployee sup2 Employee :: new;System.out.println(sup2.get());}//Function中的R apply(T t)Testpublic void test2(){FunctionInteger,Employee func1 id - new Employee(id);Employee employee func1.apply(1001);System.out.println(employee);System.out.println(*******************);FunctionInteger,Employee func2 Employee :: new;Employee employee1 func2.apply(1002);System.out.println(employee1);}//BiFunction中的R apply(T t,U u)Testpublic void test3(){BiFunctionInteger,String,Employee func1 (id,name) - new Employee(id,name);System.out.println(func1.apply(1001,Tom));System.out.println(*******************);BiFunctionInteger,String,Employee func2 Employee :: new;System.out.println(func2.apply(1002,Tom));}
}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;}Overridepublic 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(){FunctionInteger,String[] func1 length - new String[length];String[] arr1 func1.apply(5);System.out.println(Arrays.toString(arr1));System.out.println(*******************);FunctionInteger,String[] func2 String[] :: new;String[] arr2 func2.apply(10);System.out.println(Arrays.toString(arr2));}五 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等。但现在数据源可以更多了有MongDBRadis等而这些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 stream() : 返回一个顺序流default Stream parallelStream() : 返回一个并行流
Test
public void test01(){ListInteger list Arrays.asList(1,2,3,4,5);//JDK1.8中Collection系列集合增加了方法StreamInteger stream list.stream();
}方式二通过数组
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流
static Stream 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};StreamString 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 Stream of(T… values) : 返回一个流
Test
public void test04(){StreamInteger stream Stream.of(1,2,3,4,5);stream.forEach(System.out::println);
}方式四创建无限流(了解)
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
迭代 public static Stream iterate(final T seed, final UnaryOperator f)生成 public static Stream generate(Supplier s)
// 方式四创建无限流
Test
public void test05() {// 迭代// public staticT StreamT iterate(final T seed, final// UnaryOperatorT f)StreamInteger stream Stream.iterate(0, x - x 2);stream.limit(10).forEach(System.out::println);// 生成// public staticT StreamT generate(SupplierT s)StreamDouble 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)产生一个新流其中按比较器顺序排序
代码举例
public class StreamMiddleOperate {Testpublic void test01(){//1、创建StreamStreamInteger 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%20);//3、终结操作例如遍历stream.forEach(System.out::println);}Testpublic void test02(){Stream.of(1,2,3,4,5,6).filter(t - t%20).forEach(System.out::println);}Testpublic void test03(){Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5).distinct().forEach(System.out::println);}Testpublic void test04(){Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5).limit(3).forEach(System.out::println);}Testpublic 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);}Testpublic void test06(){Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5).skip(5).forEach(System.out::println);}Testpublic void test07(){Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5).skip(5).distinct().filter(t - t%30).forEach(System.out::println);}Testpublic 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(countcount);}Testpublic 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);}Testpublic void test10(){Stream.of(1,2,3,4,5).map(t - t1)//FunctionT,R接口抽象方法 R apply(T t).forEach(System.out::println);}Testpublic void test11(){String[] arr {hello,world,java};Arrays.stream(arr).map(t-t.toUpperCase()).forEach(System.out::println);}Testpublic void test12(){String[] arr {hello,world,java};Arrays.stream(arr).flatMap(t - Stream.of(t.split(|)))//FunctionT,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)可以将流中元素反复结合起来得到一个值。返回 Treduce(BinaryOperator b)可以将流中元素反复结合起来得到一个值。返回 Optional
备注map 和 reduce 的连接通常称为 map-reduce 模式因 Google 用它来进行网络搜索而出名。
3-收集
方 法描 述collect(Collector c)将流转换为其他形式。接收一个 Collector接口的实现用于给Stream中元素做汇总的方法
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
另外 Collectors 实用类提供了很多静态方法可以方便地创建常见收集器实例具体方法与实例如下表
方法返回类型作用toListCollectorT, ?, List把流中元素收集到List
ListEmployee emps list.stream().collect(Collectors.toList());方法返回类型作用toSetCollectorT, ?, Set把流中元素收集到Set
SetEmployee emps list.stream().collect(Collectors.toSet());方法返回类型作用toCollectionCollectorT, ?, C把流中元素收集到创建的集合
CollectionEmployee emps list.stream().collect(Collectors.toCollection(ArrayList::new));方法返回类型作用countingCollectorT, ?, Long计算流中元素的个数
long count list.stream().collect(Collectors.counting());方法返回类型作用summingIntCollectorT, ?, Integer对流中元素的整数属性求和
int totallist.stream().collect(Collectors.summingInt(Employee::getSalary));方法返回类型作用averagingIntCollectorT, ?, Double计算流中元素Integer属性的平均值
double avg list.stream().collect(Collectors.averagingInt(Employee::getSalary));方法返回类型作用summarizingIntCollectorT, ?, IntSummaryStatistics收集流中Integer属性的统计值。如平均值
int SummaryStatisticsiss list.stream().collect(Collectors.summarizingInt(Employee::getSalary));方法返回类型作用joiningCollectorCharSequence, ?, String连接流中每个字符串
String str list.stream().map(Employee::getName).collect(Collectors.joining());方法返回类型作用maxByCollectorT, ?, Optional根据比较器选择最大值
OptionalEmpmax list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));方法返回类型作用minByCollectorT, ?, Optional根据比较器选择最小值
OptionalEmp min list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));方法返回类型作用reducingCollectorT, ?, Optional从一个作为累加器的初始值开始利用BinaryOperator与流中元素逐个结合从而归约成单个值
int totallist.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));方法返回类型作用collectingAndThenCollectorT,A,RR包裹另一个收集器对其结果转换函数
int how list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));方法返回类型作用groupingByCollectorT, ?, MapK, List根据某属性值对流分组属性为K结果为V
MapEmp.Status, ListEmp map list.stream().collect(Collectors.groupingBy(Employee::getStatus));方法返回类型作用partitioningByCollectorT, ?, MapBoolean, List根据true或false进行分区
MapBoolean,ListEmp vd list.stream().collect(Collectors.partitioningBy(Employee::getManage));举例
public class StreamEndding {Testpublic void test01(){Stream.of(1,2,3,4,5).forEach(System.out::println);}Testpublic void test02(){long count Stream.of(1,2,3,4,5).count();System.out.println(count count);}Testpublic void test03(){boolean result Stream.of(1,3,5,7,9).allMatch(t - t%2!0);System.out.println(result);}Testpublic void test04(){boolean result Stream.of(1,3,5,7,9).anyMatch(t - t%20);System.out.println(result);}Testpublic void test05(){OptionalInteger opt Stream.of(1,3,5,7,9).findFirst();System.out.println(opt);}Testpublic void test06(){OptionalInteger opt Stream.of(1,2,3,4,5,7,9).filter(t - t%30).findFirst();System.out.println(opt);}Testpublic void test07(){OptionalInteger opt Stream.of(1,2,4,5,7,8).filter(t - t%30).findFirst();System.out.println(opt);}Testpublic void test08(){OptionalInteger max Stream.of(1,2,4,5,7,8).max((t1,t2) - Integer.compare(t1, t2));System.out.println(max);}Testpublic void test09(){Integer reduce Stream.of(1,2,4,5,7,8).reduce(0, (t1,t2) - t1t2);//BinaryOperator接口 T apply(T t1, T t2)System.out.println(reduce);}Testpublic void test10(){OptionalInteger max Stream.of(1,2,4,5,7,8).reduce((t1,t2) - t1t2?t1:t2);//BinaryOperator接口 T apply(T t1, T t2)System.out.println(max);}Testpublic void test11(){ListInteger list Stream.of(1,2,4,5,7,8).filter(t - t%20).collect(Collectors.toList());System.out.println(list);}
}5.5 Java9新增API
5.5.1 新增1Stream实例化方法
ofNullable()的使用
Java 8 中 Stream 不能完全为null否则会报空指针异常。而 Java 9 中的 ofNullable 方法允许我们创建一个单元素 Stream可以包含一个非空元素也可以创建一个空 Stream。
//报NullPointerException
//StreamObject stream1 Stream.of(null);
//System.out.println(stream1.count());//不报异常允许通过
StreamString stringStream Stream.of(AA, BB, null);
System.out.println(stringStream.count());//3//不报异常允许通过
ListString list new ArrayList();
list.add(AA);
list.add(null);
System.out.println(list.stream().count());//2
//ofNullable()允许值为null
StreamObject stream1 Stream.ofNullable(null);
System.out.println(stream1.count());//0StreamString stream Stream.ofNullable(hello world);
System.out.println(stream.count());//1iterator()重载的使用
//原来的控制终止方式
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循环依次进行以 下若干操作步骤
第一个队伍只要名字为3个字的成员姓名存储到一个新集合中。第一个队伍筛选之后只要前3个人存储到一个新集合中。第二个队伍只要姓张的成员姓名存储到一个新集合中。第二个队伍筛选之后不要前2个人存储到一个新集合中。将两个队伍合并为一个队伍存储到一个新集合中。根据姓名创建 Person 对象存储到一个新集合中。打印整个队伍的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;}Overridepublic String toString() {return Person{name name };}
}两个队伍集合的代码如下
public static void main(String[] args) {//第一支队伍ArrayListString one new ArrayList();one.add(迪丽热巴);one.add(宋远桥);one.add(苏星河);one.add(石破天);one.add(石中玉);one.add(老子);one.add(庄子);one.add(洪七公);//第二支队伍ArrayListString two new ArrayList();two.add(古力娜扎);two.add(张无忌);two.add(赵丽颖);two.add(张三丰);two.add(尼古拉斯赵四);two.add(张天爱);two.add(张二狗);// ....编写代码完成题目要求 }参考答案
public static void main(String[] args) {//第一支队伍ArrayListString one new ArrayList();one.add(迪丽热巴);one.add(宋远桥);one.add(苏星河);one.add(石破天);one.add(石中玉);one.add(老子);one.add(庄子);one.add(洪七公);//第二支队伍ArrayListString two new ArrayList();two.add(古力娜扎);two.add(张无忌);two.add(赵丽颖);two.add(张三丰);two.add(尼古拉斯赵四);two.add(张天爱);two.add(张二狗);// 第一个队伍只要名字为3个字的成员姓名// 第一个队伍筛选之后只要前3个人StreamString streamOne one.stream().filter(s ‐ s.length() 3).limit(3);// 第二个队伍只要姓张的成员姓名// 第二个队伍筛选之后不要前2个人StreamString streamTwo two.stream().filter(s ‐ s.startsWith(张)).skip(2);// 将两个队伍合并为一个队伍// 根据姓名创建Person对象// 打印整个队伍的Person对象信息。Stream.concat(streamOne, streamTwo).map(Person::new).forEach(System.out::println);}六 新语法结构
新的语法结构为我们勾勒出了 Java 语法进化的一个趋势将开发者从复杂、繁琐的低层次抽象中逐渐解放出来以更高层次、更优雅的抽象既降低代码量又避免意外编程错误的出现进而提高代码质量和开发效率。
6.1 Java的REPL工具 jShell命令
JDK9的新特性
Java 终于拥有了像Python 和 Scala 之类语言的REPL工具交互式编程环境read - evaluate - print - loopjShell。以交互式的方式对语句和表达式进行求值。即写即得、快速运行。
利用jShell在没有创建类的情况下在命令行里直接声明变量计算表达式执行语句。无需跟人解释”public static void main(String[] args)”这句废话。
使用举例
调出jShell 获取帮助 基本使用 导入指定的包 默认已经导入如下的所有包包含java.lang包 只需按下 Tab 键就能自动补全代码 列出当前 session 里所有有效的代码片段 查看当前 session 下所有创建过的变量 查看当前 session 下所有创建过的方法 Tips我们还可以重新定义相同方法名和参数列表的方法即对现有方法的修改或覆盖。 使用外部代码编辑器来编写 Java 代码 从外部文件加载源代码【HelloWorld.java】
/*** Created by songhongkang*/
public void printHello() {System.out.println(马上2023年了尚硅谷祝所有的谷粉元旦快乐);
}
printHello();使用/open命令调用 退出jShell
6.2 异常处理之try-catch资源关闭
在JDK7 之前我们这样处理资源的关闭
Test
public void test01() {FileWriter fw null;BufferedWriter bw null;try {fw new FileWriter(d:/1.txt);bw new BufferedWriter(fw);bw.write(hello);} catch (IOException e) {e.printStackTrace();} finally {try {if (bw ! null) {bw.close();}} catch (IOException e) {e.printStackTrace();}try {if (fw ! null) {fw.close();}} catch (IOException e) {e.printStackTrace();}}
}JDK7的新特性
在try的后面可以增加一个()在括号中可以声明流对象并初始化。try中的代码执行完毕会自动把流对象释放就不用写finally了。
格式
try(资源对象的声明和初始化){业务逻辑代码,可能会产生异常
}catch(异常类型1 e){处理异常代码
}catch(异常类型2 e){处理异常代码
}说明
1、在try()中声明的资源无论是否发生异常无论是否处理异常都会自动关闭资源对象不用手动关闭了。
2、这些资源实现类必须实现AutoCloseable或Closeable接口实现其中的close()方法。Closeable是AutoCloseable的子接口。Java7几乎把所有的“资源类”包括文件IO的各种类、JDBC编程的Connection、Statement等接口…都进行了改写改写后资源类都实现了AutoCloseable或Closeable接口并实现了close()方法。
3、写到try()中的资源类的变量默认是final声明的不能修改。
举例
//举例1
Test
public void test02() {try (FileWriter fw new FileWriter(d:/1.txt);BufferedWriter bw new BufferedWriter(fw);) {bw.write(hello);} catch (IOException e) {e.printStackTrace();}
}//举例2
Test
public void test03() {//从d:/1.txt(utf-8)文件中读取内容写到项目根目录下1.txt(gbk)文件中try (FileInputStream fis new FileInputStream(d:/1.txt);InputStreamReader isr new InputStreamReader(fis, utf-8);BufferedReader br new BufferedReader(isr);FileOutputStream fos new FileOutputStream(1.txt);OutputStreamWriter osw new OutputStreamWriter(fos, gbk);BufferedWriter bw new BufferedWriter(osw);) {String str;while ((str br.readLine()) ! null) {bw.write(str);bw.newLine();}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
}JDK9的新特性
try的前面可以定义流对象try后面的()中可以直接引用流对象的名称。在try代码执行完毕后流对象也可以释放掉也不用写finally了。
格式
A a new A();
B b new B();
try(a;b){可能产生的异常代码
}catch(异常类名 变量名){异常处理的逻辑
}举例
Test
public void test04() {InputStreamReader reader new InputStreamReader(System.in);OutputStreamWriter writer new OutputStreamWriter(System.out);try (reader; writer) {//reader是final的不可再被赋值// reader null;} catch (IOException e) {e.printStackTrace();}
}6.3 局部变量类型推断
JDK 10的新特性
局部变量的显示类型声明常常被认为是不必须的给一个好听的名字反而可以很清楚的表达出下面应该怎样继续。本新特性允许开发人员省略通常不必要的局部变量类型声明以增强Java语言的体验性、可读性。
使用举例
//1.局部变量的实例化
var list new ArrayListString();var set new LinkedHashSetInteger();//2.增强for循环中的索引
for (var v : list) {System.out.println(v);
}//3.传统for循环中
for (var i 0; i 100; i) {System.out.println(i);
}//4. 返回值类型含复杂泛型结构
var iterator set.iterator();
//IteratorMap.EntryInteger, Student iterator set.iterator(); 不适用场景 声明一个成员变量声明一个数组变量并为数组静态初始化省略new的情况下方法的返回值类型方法的参数类型没有初始化的方法内的局部变量声明作为catch块中异常类型Lambda表达式中函数式接口的类型方法引用中函数式接口的类型 代码举例
声明一个成员变量并初始化值为null 声明一个数组变量并为数组静态初始化省略new的情况下 没有初始化的方法内的局部变量声明 方法的返回值类型 方法的参数类型 构造器的参数类型 作为catch块中异常类型
Lambda表达式中函数式接口的类型 方法引用中函数式接口的类型 注意
var不是一个关键字而是一个类型名将它作为变量的类型。不能使用var作为类名。这不是JavaScript。var并不会改变 Java是一门静态类型语言的事实。编译器负责推断出类型并把结果写入字节码文件就好像是开发人员自己敲入类型一样。
6.4 instanceof的模式匹配
JDK14中预览特性
instanceof 模式匹配通过提供更为简便的语法来提高生产力。有了该功能可以减少Java程序中显式强制转换的数量实现更精确、简洁的类型安全的代码。
Java 14之前旧写法
if(obj instanceof String){String str (String)obj; //需要强转.. str.contains(..)..
}else{...
}Java 14新特性写法
if(obj instanceof String str){.. str.contains(..)..
}else{...
}举例
/*** instanceof的模式匹配预览** author shkstart* create 上午 11:32*/
public class Feature01 {Testpublic void test1(){Object obj new String(hello,Java14);obj null;//在使用null 匹配instanceof 时返回都是false.if(obj instanceof String){String str (String) obj;System.out.println(str.contains(Java));}else{System.out.println(非String类型);}//举例1if(obj instanceof String str){ //新特性省去了强制类型转换的过程System.out.println(str.contains(Java));}else{System.out.println(非String类型);}}
}// 举例2
class InstanceOf{String str abc;public void test(Object obj){if(obj instanceof String str){//此时的str的作用域仅限于if结构内。System.out.println(str.toUpperCase());}else{System.out.println(str.toLowerCase());}}}//举例3
class Monitor{private String model;private double price;// public boolean equals(Object o){
// if(o instanceof Monitor other){
// if(model.equals(other.model) price other.price){
// return true;
// }
// }
// return false;
// }public boolean equals(Object o){return o instanceof Monitor other model.equals(other.model) price other.price;}}JDK15中第二次预览
没有任何更改。
JDK16中转正特性
在Java16中转正。
6.5 switch表达式
传统switch声明语句的弊端
匹配是自上而下的如果忘记写break后面的case语句不论匹配与否都会执行 —case穿透所有的case语句共用一个块范围在不同的case语句定义的变量名不能重复不能在一个case里写多个执行结果一致的条件整个switch不能作为表达式返回值
//常见错误实现
switch(month){case 3|4|5://3|4|5 用了位运算符11 | 100 | 101结果是 111是7System.out.println(春季);break;case 6|7|8://6|7|8用了位运算符110 | 111 | 1000结果是1111是15System.out.println(夏季);break;case 9|10|11://9|10|11用了位运算符1001 | 1010 | 1011结果是1011是11System.out.println(秋季);break;case 12|1|2://12|1|2 用了位运算符1100 | 1 | 10 结果是1111是15System.out.println(冬季);break;default:System.out.println(输入有误);
}JDK12中预览特性
Java 12将会对switch声明语句进行扩展使用case L -来替代以前的break;省去了 break 语句避免了因少写 break 而出错。同时将多个 case 合并到一行显得简洁、清晰也更加优雅的表达逻辑分支。为了保持兼容性case 条件语句中依然可以使用字符 : 但是同一个 switch 结构里不能混用 - 和 : 否则编译错误。
举例
Java 12之前
/*** author shkstart* create 下午 4:47*/
public class SwitchTest {public static void main(String[] args) {int numberOfLetters;Fruit fruit Fruit.APPLE;switch (fruit) {case PEAR:numberOfLetters 4;break;case APPLE:case GRAPE:case MANGO:numberOfLetters 5;break;case ORANGE:case PAPAYA:numberOfLetters 6;break;default:throw new IllegalStateException(No Such Fruit: fruit);}System.out.println(numberOfLetters);}
}
enum Fruit {PEAR, APPLE, GRAPE, MANGO, ORANGE, PAPAYA;
}switch 语句如果漏写了一个 break那么逻辑往往就跑偏了这种方式既繁琐又容易出错。
Java 12中
/*** author shkstart* create 下午 10:38*/
public class SwitchTest1 {public static void main(String[] args) {Fruit fruit Fruit.GRAPE;switch(fruit){case PEAR - System.out.println(4);case APPLE,MANGO,GRAPE - System.out.println(5);case ORANGE,PAPAYA - System.out.println(6);default - throw new IllegalStateException(No Such Fruit: fruit);};}
}更进一步
/*** author shkstart* create 2019 下午 10:44*/
public class SwitchTest2 {public static void main(String[] args) {Fruit fruit Fruit.GRAPE;int numberOfLetters switch(fruit){case PEAR - 4;case APPLE,MANGO,GRAPE - 5;case ORANGE,PAPAYA - 6;default - throw new IllegalStateException(No Such Fruit: fruit);};System.out.println(numberOfLetters);}
}JDK13中二次预览特性
JDK13中引入了yield语句用于返回值。这意味着switch表达式(返回值)应该使用yieldswitch语句(不返回值)应该使用break。
yield和return的区别在于return会直接跳出当前循环或者方法而yield只会跳出当前switch块。
在以前
Test
public void testSwitch1(){String x 3;int i;switch (x) {case 1:i1;break;case 2:i2;break;default:i x.length();break;}System.out.println(i);
}在JDK13中
Test
public void testSwitch2(){String x 3;int i switch (x) {case 1 - 1;case 2 - 2;default - {yield 3;}};System.out.println(i);
}或者
Test
public void testSwitch3() {String x 3;int i switch (x) {case 1:yield 1;case 2:yield 2;default:yield 3;};System.out.println(i);
}JDK14中转正特性
这是JDK 12和JDK 13中的预览特性现在是正式特性了。
JDK17的预览特性switch的模式匹配
旧写法
static String formatter(Object o) {String formatted unknown;if (o instanceof Integer i) {formatted String.format(int %d, i);} else if (o instanceof Long l) {formatted String.format(long %d, l);} else if (o instanceof Double d) {formatted String.format(double %f, d);} else if (o instanceof String s) {formatted String.format(String %s, s);}return formatted;
}模式匹配新写法
static String formatterPatternSwitch(Object o) {return switch (o) {case Integer i - String.format(int %d, i);case Long l - String.format(long %d, l);case Double d - String.format(double %f, d);case String s - String.format(String %s, s);default - o.toString();};
}直接在 switch 上支持 Object 类型这就等于同时支持多种类型使用模式匹配得到具体类型大大简化了语法量这个功能很实用。
6.6 文本块
现实问题
在Java中通常需要使用String类型表达HTMLXMLSQL或JSON等格式的字符串在进行字符串赋值时需要进行转义和连接操作然后才能编译该代码这种表达方式难以阅读并且难以维护。
JDK13的新特性
使用作为文本块的开始符和结束符在其中就可以放置多行的字符串不需要进行任何转义。因此文本块将提高Java程序的可读性和可写性。
基本使用 line1
line2
line3相当于
line1\nline2\nline3\n或者一个连接的字符串
line1\n
line2\n
line3\n如果字符串末尾不需要行终止符则结束分隔符可以放在最后一行内容上。例如 line1
line2
line3相当于
line1\nline2\nline3文本块可以表示空字符串但不建议这样做因为它需要两行源代码
String empty
;举例1普通文本
原有写法 String text1 The Sound of silence\n Hello darkness, my old friend\n Ive come to talk with you again\n Because a vision softly creeping\n Left its seeds while I was sleeping\n And the vision that was planted in my brain\n Still remains\n Within the sound of silence;System.out.println(text1);使用新特性
String text2 The Sound of silenceHello darkness, my old friendIve come to talk with you againBecause a vision softly creepingLeft its seeds while I was sleepingAnd the vision that was planted in my brainStill remainsWithin the sound of silence;
System.out.println(text2);举例2HTML语句
htmlbodypHello, zyy/p/body
/html将其复制到Java的字符串中会展示成以下内容
html\n body\n pHello, 尚硅谷/p\n /body\n
/html\n;即被自动进行了转义这样的字符串看起来不是很直观在JDK 13中 htmlbodypHello, world/p/body
/html
;举例3SQL语句
select employee_id,last_name,salary,department_id
from employees
where department_id in (40,50,60)
order by department_id asc原有方式
String sql SELECT id,NAME,email\n FROM customers\n WHERE id 4\n ORDER BY email DESC;使用新特性
String sql1 SELECT id,NAME,emailFROM customersWHERE id 4ORDER BY email DESC;举例4JSON字符串
原有方式
String myJson {\n \name\:\Song Hongkang\,\n \address\:\www.atguigu.com\,\n \email\:\shkstart126.com\\n };
System.out.println(myJson);使用新特性
String myJson1 {name:Song Hongkang,address:www.atguigu.com,email:shkstart126.com};
System.out.println(myJson1);JDK14中二次预览特性
JDK14的版本主要增加了两个escape sequences分别是 \ line-terminator与\s escape sequence。
举例
/*** author shkstart* create 下午 7:13*/
public class Feature05 {//jdk14新特性Testpublic void test5(){String sql1 SELECT id,NAME,emailFROM customersWHERE id 4ORDER BY email DESC;System.out.println(sql1);// \:取消换行操作// \s:表示一个空格String sql2 SELECT id,NAME,email \FROM customers\s\WHERE id 4 \ORDER BY email DESC;System.out.println(sql2);}
}JDK15中功能转正
6.7 Record
背景
早在2019年2月份Java 语言架构师 Brian Goetz曾写文抱怨“Java太啰嗦”或有太多的“繁文缛节”。他提到开发人员想要创建纯数据载体类plain data carriers通常都必须编写大量低价值、重复的、容易出错的代码。如构造函数、getter/setter、equals()、hashCode()以及toString()等。
以至于很多人选择使用IDE的功能来自动生成这些代码。还有一些开发会选择使用一些第三方类库如Lombok等来生成这些方法。
**JDK14中预览特性神说要用record于是就有了。**实现一个简单的数据载体类为了避免编写构造函数访问器equals()hashCode () toString ()等Java 14推出record。
record 是一种全新的类型它本质上是一个 final 类同时所有的属性都是 final 修饰它会自动编译出 public get 、hashcode 、equals、toString、构造器等结构减少了代码编写量。
具体来说当你用record 声明一个类时该类将自动拥有以下功能
获取成员变量的简单方法比如例题中的 name() 和 partner() 。注意区别于我们平常getter()的写法。一个 equals 方法的实现执行比较时会比较该类的所有成员属性。重写 hashCode() 方法。一个可以打印该类所有成员属性的 toString() 方法。只有一个构造方法。
此外
还可以在record声明的类中定义静态字段、静态方法、构造器或实例方法。不能在record声明的类中定义实例字段类不能声明为abstract不能声明显式的父类等。
举例1旧写法
class Point {private final int x;private final int y;Point(int x, int y) {this.x x;this.y y;}int x() {return x;}int y() {return y;}public boolean equals(Object o) {if (!(o instanceof Point)) return false;Point other (Point) o;return other.x x other.y y;}public int hashCode() {return Objects.hash(x, y);}Overridepublic String toString() {return Point{ x x , y y };}
}举例1新写法
record Point(int x, int y) { }举例1
public record Dog(String name, Integer age) {
}public class Java14Record {public static void main(String[] args) {Dog dog1 new Dog(牧羊犬, 1);Dog dog2 new Dog(田园犬, 2);Dog dog3 new Dog(哈士奇, 3);System.out.println(dog1);System.out.println(dog2);System.out.println(dog3);}
}举例2
/*** Record类型的演示** author shkstart* create 下午 6:13*/
public class Feature07 {Testpublic void test1(){//测试构造器Person p1 new Person(罗密欧,new Person(zhuliye,null));//测试toString()System.out.println(p1);//测试equals():Person p2 new Person(罗密欧,new Person(zhuliye,null));System.out.println(p1.equals(p2));//测试hashCode()和equals()HashSetPerson set new HashSet();set.add(p1);set.add(p2);for (Person person : set) {System.out.println(person);}//测试name()和partner():类似于getName()和getPartner()System.out.println(p1.name());System.out.println(p1.partner());}Testpublic void test2(){Person p1 new Person(zhuyingtai);System.out.println(p1.getNameInUpperCase());Person.nation CHN;System.out.println(Person.showNation());}
}/*** author shkstart* create 下午 6:20*/
public record Person(String name,Person partner) {//还可以声明静态的属性、静态的方法、构造器、实例方法public static String nation;public static String showNation(){return nation;}public Person(String name){this(name,null);}public String getNameInUpperCase(){return name.toUpperCase();}//不可以声明非静态的属性
// private int id;//报错
}//不可以将record定义的类声明为abstract的
//abstract record Order(){
//
//}//不可以给record定义的类声明显式的父类非Record类
//record Order() extends Thread{
//
//}JDK15中第二次预览特性
JDK16中转正特性
最终到JDK16中转正。
记录不适合哪些场景
record的设计目标是提供一种将数据建模为数据的好方法。它也不是 JavaBeans 的直接替代品因为record的方法不符合 JavaBeans 的 get 标准。另外 JavaBeans 通常是可变的而记录是不可变的。尽管它们的用途有点像但记录并不会以某种方式取代 JavaBean。
6.8 密封类
背景
在 Java 中如果想让一个类不能被继承和修改这时我们应该使用 final 关键字对类进行修饰。不过这种要么可以继承要么不能继承的机制不够灵活有些时候我们可能想让某个类可以被某些类型继承但是又不能随意继承是做不到的。Java 15 尝试解决这个问题引入了 sealed 类被 sealed 修饰的类可以指定子类。这样这个类就只能被指定的类继承。
JDK15的预览特性
通过密封的类和接口来限制超类的使用密封的类和接口限制其它可能继承或实现它们的其它类或接口。
具体使用 使用修饰符sealed可以将一个类声明为密封类。密封的类使用保留关键字permits列出可以直接扩展即extends它的类。 sealed 修饰的类的机制具有传递性它的子类必须使用指定的关键字进行修饰且只能是 final、sealed、non-sealed 三者之一。
举例
package com.atguigu.java;
public abstract sealed class Shape permits Circle, Rectangle, Square {...}public final class Circle extends Shape {...} //final表示Circle不能再被继承了public sealed class Rectangle extends Shape permits TransparentRectangle, FilledRectangle {...}public final class TransparentRectangle extends Rectangle {...}public final class FilledRectangle extends Rectangle {...}public non-sealed class Square extends Shape {...} //non-sealed表示可以允许任何类继承JDK16二次预览特性
JDK17中转正特性
7. API的变化
7.1 Optional类
JDK8的新特性
到目前为止臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前为了解决空指针异常Google在著名的Guava项目引入了Optional类通过检查空值的方式避免空指针异常。受到Google的启发Optional类已经成为Java 8类库的一部分。
OptionalT 类(java.util.Optional) 是一个容器类它可以保存类型T的值代表这个值存在。或者仅仅保存null表示这个值不存在。如果值存在则isPresent()方法会返回true调用get()方法会返回该对象。
Optional提供很多有用的方法这样我们就不用显式进行空值检测。
创建Optional类对象的方法static Optional empty() 用来创建一个空的Optional实例 static Optional of(T value) 用来创建一个Optional实例value必须非空static T OptionalT ofNullable(T value) 用来创建一个Optional实例value可能是空也可能非空 判断Optional容器中是否包含对象 boolean isPresent() : 判断Optional容器中的值是否存在void ifPresent(Consumer? super T consumer) 判断Optional容器中的值是否存在如果存在就对它进行Consumer指定的操作如果不存在就不做 获取Optional容器的对象T get(): 如果调用对象包含值返回该值。否则抛异常。T get()与of(T value)配合使用T orElse(T other) orElse(T other) 与ofNullable(T value)配合使用如果Optional容器中非空就返回所包装值如果为空就用orElse(T other)other指定的默认值备胎代替T orElseGet(Supplier? extends T other) 如果Optional容器中非空就返回所包装值如果为空就用Supplier接口的Lambda表达式提供的值代替T orElseThrow(Supplier? extends X exceptionSupplier) 如果Optional容器中非空就返回所包装值如果为空就抛出你指定的异常类型代替原来的NoSuchElementException
举例
public class TestOptional {Testpublic void test1(){String str hello;OptionalString opt Optional.of(str);System.out.println(opt);}Testpublic void test2(){OptionalString opt Optional.empty();System.out.println(opt);}Testpublic void test3(){String str null;OptionalString opt Optional.ofNullable(str);System.out.println(opt);}Testpublic void test4(){String str hello;OptionalString opt Optional.of(str);String string opt.get();System.out.println(string);}Testpublic void test5(){String str null;OptionalString opt Optional.ofNullable(str);
// System.out.println(opt.get());//java.util.NoSuchElementException: No value present}Testpublic void test6(){String str hello;OptionalString opt Optional.ofNullable(str);String string opt.orElse(atguigu);System.out.println(string);}Testpublic void test7(){String str null;OptionalString opt Optional.ofNullable(str);String string opt.orElseGet(String::new);System.out.println(string);}Testpublic void test8(){String str null;OptionalString opt Optional.ofNullable(str);String string opt.orElseThrow(()-new RuntimeException(值不存在));System.out.println(string);}Testpublic void test9(){String str Hello1;OptionalString opt Optional.ofNullable(str);//判断是否是纯字母单词如果是转为大写否则保持不变String result opt.filter(s-s.matches([a-zA-Z])).map(s - s.toUpperCase()).orElse(str);System.out.println(result);}
}这是JDK9-11的新特性
新增方法描述新增的版本boolean isEmpty()判断value是否为空JDK 11ifPresentOrElse(Consumer? super T action, Runnable emptyAction)value非空执行参数1功能如果value为空执行参数2功能JDK 9Optional or(Supplier? extends Optional? extends T supplier)value非空返回对应的Optionalvalue为空返回形参封装的OptionalJDK 9Stream stream()value非空返回仅包含此value的Stream否则返回一个空的StreamJDK 9T orElseThrow()value非空返回value否则抛异常NoSuchElementExceptionJDK 10
7.2 String存储结构和API变更
这是JDK9的新特性。
产生背景 Motivation The current implementation of the String class stores characters in a char array, using two bytes (sixteen bits) for each character. Data gathered from many different applications indicates that strings are a major component of heap usage and, moreover, that most String objects contain only Latin-1 characters. Such characters require only one byte of storage, hence half of the space in the internal char arrays of such String objects is going unused. 使用说明 Description We propose to change the internal representation of the String class from a UTF-16 char array to a byte array plus an encoding-flag field. The new String class will store characters encoded either as ISO-8859-1/Latin-1 (one byte per character), or as UTF-16 (two bytes per character), based upon the contents of the string. The encoding flag will indicate which encoding is used. 结论String 再也不用 char[] 来存储啦改成了 byte[] 加上编码标记节约了一些空间。
public final class Stringimplements java.io.Serializable, ComparableString, CharSequence {Stableprivate final byte[] value;...
}拓展StringBuffer 与 StringBuilder
那StringBuffer 和 StringBuilder 是否仍无动于衷呢 String-related classes such as AbstractStringBuilder, StringBuilder, and StringBuffer will be updated to use the same representation, as will the HotSpot VM’s intrinsic string operations. JDK11新特性新增了一系列字符串处理方法
描述举例判断字符串是否为空白 .isBlank(); // true去除首尾空白 Javastack .strip(); // “Javastack”去除尾部空格 Javastack “.stripTrailing(); // Javastack”去除首部空格 Javastack .stripLeading(); // Javastack 复制字符串“Java”.repeat(3);// “JavaJavaJava”行数统计“A\nB\nC”.lines().count(); // 3
JDK12新特性String 实现了 Constable 接口
String源码
public final class String implements java.io.Serializable, ComparableString, CharSequence,Constable, ConstantDesc {java.lang.constant.Constable接口定义了抽象方法
public interface Constable {Optional? extends ConstantDesc describeConstable();
}Java 12 String 的实现源码
/*** Returns an {link Optional} containing the nominal descriptor for this* instance, which is the instance itself.** return an {link Optional} describing the {linkplain String} instance* since 12*/
Override
public OptionalString describeConstable() {return Optional.of(this);
}很简单其实就是调用 Optional.of 方法返回一个 Optional 类型。
举例
private static void testDescribeConstable() {String name 尚硅谷Java高级工程师;OptionalString optional name.describeConstable();System.out.println(optional.get());
}结果输出
尚硅谷Java高级工程师JDK12新特性String新增方法
String的transform(Function)
var result foo.transform(input - input bar);
System.out.println(result); //foo bar或者
var result foo.transform(input - input bar).transform(String::toUpperCase)
System.out.println(result); //FOO BAR对应的源码
/**
* This method allows the application of a function to {code this}
* string. The function should expect a single String argument
* and produce an {code R} result.
* since 12
*/
public R R transform(Function? super String, ? extends R f) {return f.apply(this);
}在某种情况下该方法应该被称为map()。
举例
private static void testTransform() {System.out.println(test java 12 transform);ListString list1 List.of(Java, Python, C );ListString list2 new ArrayList();list1.forEach(element - list2.add(element.transform(String::strip).transform(String::toUpperCase).transform((e) - Hi, e)));list2.forEach(System.out::println);
}结果输出
test java 12 transform
Hi,JAVA
Hi,PYTHON
Hi,C如果使用Java 8的Stream特性可以如下实现
private static void testTransform1() {System.out.println(test before java 12 );ListString list1 List.of(Java , Python, C );StreamString stringStream list1.stream().map(element - element.strip()).map(String::toUpperCase).map(element - Hello, element);ListString list2 stringStream.collect(Collectors.toList());list2.forEach(System.out::println);}7.3 JDK17标记删除Applet API
Applet API 提供了一种将 Java AWT/Swing 控件嵌入到浏览器网页中的方法。不过目前 Applet 已经被淘汰。大部分人可能压根就没有用过 Applet。
Applet API 实际上是无用的因为所有 Web 浏览器供应商都已删除或透露计划放弃对 Java 浏览器插件的支持。Java 9 的时候Applet API 已经被标记为过时Java 17 的时候终于标记为删除了。
具体如下
java.applet.Applet
java.applet.AppletStub
java.applet.AppletContext
java.applet.AudioClip
javax.swing.JApplet
java.beans.AppletInitializer8. 其它结构变化
8.1 JDK9UnderScore(下划线)使用的限制
在java 8 中标识符可以独立使用“_”来命名
String _ hello;
System.out.println(_);但是在java 9 中规定“_”不再可以单独命名标识符了如果使用会报错
8.2 JDK11更简化的编译运行程序
看下面的代码。
// 编译
javac JavaStack.java// 运行
java JavaStack我们的认知里要运行一个 Java 源代码必须先编译再运行。而在 Java 11 版本中通过一个 java 命令就直接搞定了如下所示
java JavaStack.java注意点
执行源文件中的第一个类第一个类必须包含主方法。
8.3 GC方面新特性
GC是Java主要优势之一。 然而当GC停顿太长就会开始影响应用的响应时间。随着现代系统中内存不断增长用户和程序员希望JVM能够以高效的方式充分利用这些内存 并且无需长时间的GC暂停时间。
8.3.1 G1 GC
JDK9以后默认的垃圾回收器是G1GC。
JDK10 : 为G1提供并行的Full GC
G1最大的亮点就是可以尽量的避免full gc。但毕竟是“尽量”在有些情况下G1就要进行full gc了比如如果它无法足够快的回收内存的时候它就会强制停止所有的应用线程然后清理。
在Java10之前一个单线程版的标记-清除-压缩算法被用于full gc。为了尽量减少full gc带来的影响在Java10中就把之前的那个单线程版的标记-清除-压缩的full gc算法改成了支持多个线程同时full gc。这样也算是减少了full gc所带来的停顿从而提高性能。
你可以通过-XX:ParallelGCThreads参数来指定用于并行GC的线程数。
JDK12可中断的 G1 Mixed GC
JDK12增强G1自动返回未用堆内存给操作系统
8.3.2 Shenandoah GC
JDK12Shenandoah GC低停顿时间的GC Shenandoah 垃圾回收器是 Red Hat 在 2014 年宣布进行的一项垃圾收集器研究项目 Pauseless GC 的实现旨在针对 JVM 上的内存收回实现低停顿的需求。
据 Red Hat 研发 Shenandoah 团队对外宣称Shenandoah 垃圾回收器的暂停时间与堆大小无关这意味着无论将堆设置为 200 MB 还是 200 GB都将拥有一致的系统暂停时间不过实际使用性能将取决于实际工作堆的大小和工作负载。
Shenandoah GC 主要目标是 99.9% 的暂停小于 10ms暂停与堆大小无关等。
这是一个实验性功能不包含在默认Oracle的OpenJDK版本中。
Shenandoah开发团队在实际应用中的测试数据 JDK15Shenandoah垃圾回收算法转正
Shenandoah垃圾回收算法终于从实验特性转变为产品特性这是一个从 JDK 12 引入的回收算法该算法通过与正在运行的 Java 线程同时进行疏散工作来减少 GC 暂停时间。Shenandoah 的暂停时间与堆大小无关无论堆栈是 200 MB 还是 200 GB都具有相同的一致暂停时间。
Shenandoah在JDK12被作为experimental引入在JDK15变为Production之前需要通过-XX:UnlockExperimentalVMOptions -XX:UseShenandoahGC来启用现在只需要-XX:UseShenandoahGC即可启用
8.3.3 革命性的 ZGC
JDK11引入革命性的 ZGC
ZGC这应该是JDK11最为瞩目的特性没有之一。
ZGC是一个并发、基于region、压缩型的垃圾收集器。
ZGC的设计目标是支持TB级内存容量暂停时间低10ms对整个程序吞吐量的影响小于15%。 将来还可以扩展实现机制以支持不少令人兴奋的功能例如多层堆即热对象置于DRAM和冷对象置于NVMe闪存或压缩堆。
JDK13ZGC:将未使用的堆内存归还给操作系统
JDK14ZGC on macOS和windows
JDK14之前ZGC仅Linux才支持。现在mac或Windows上也能使用ZGC了示例如下
-XX:UnlockExperimentalVMOptions -XX:UseZGCZGC与Shenandoah目标高度相似在尽可能对吞吐量影响不大的前提下实现在任意堆内存大小下都可以把垃圾收集的停顿时间限制在十毫秒以内的低延迟。 JDK15ZGC 功能转正
ZGC是Java 11引入的新的垃圾收集器经过了多个实验阶段自此终于成为正式特性。
但是这并不是替换默认的GC默认的GC仍然还是G1之前需要通过-XX:UnlockExperimentalVMOptions、 -XX:UseZGC来启用ZGC现在只需要-XX:UseZGC就可以。相信不久的将来它必将成为默认的垃圾回收器。 ZGC的性能已经相当亮眼用“令人震惊、革命性”来形容不为过。未来将成为服务端、大内存、低延迟应用的首选垃圾收集器。 怎么形容Shenandoah和ZGC的关系呢异同点大概如下
相同点性能几乎可认为是相同的不同点ZGC是Oracle JDK的根正苗红。而Shenandoah只存在于OpenJDK中因此使用时需注意你的JDK版本
JDK16ZGC 并发线程处理
在线程的堆栈处理过程中总有一个制约因素就是safepoints。在safepoints这个点Java的线程是要暂停执行的从而限制了GC的效率。 回顾 我们都知道在之前需要 GC 的时候为了进行垃圾回收需要所有的线程都暂停下来这个暂停的时间我们称为 Stop The World。 而为了实现 STW 这个操作 JVM 需要为每个线程选择一个点停止运行这个点就叫做安全点Safepoints。 而ZGC的并发线程堆栈处理可以保证Java线程可以在GC safepoints的同时可以并发执行。它有助于提高所开发的Java软件应用程序的性能和效率。
9. 小结与展望
随着云计算和 AI 等技术浪潮当前的计算模式和场景正在发生翻天覆地的变化不仅对 Java 的发展速度提出了更高要求也深刻影响着 Java 技术的发展方向。传统的大型企业或互联网应用正在被云端、容器化应用、模块化的微服务甚至是函数(FaaS Function-as-a-Service)所替代。
**Java 需要在新的计算场景下改进开发效率。**比如Java 代码虽然进行了一些类型推断等改进更易用的集合 API 等但仍然给开发者留下了过于刻板、形式主义的印象这是一个长期的改进方向。
Java虽然标榜面向对象编程却毫不顾忌的加入面向接口编程思想又扯出匿名对象的概念每增加一个新的东西对Java的根本面向对象思想的一次冲击。 士不可不弘毅任重而道远。