目錄
1. Java版本迭代概述
1.1 發(fā)布特點(diǎn)(小步快跑,快速迭代)
1.2 名詞解釋
1.3 各版本支持時(shí)間路線圖
1.4 各版本介紹
1.5 JDK各版本下載鏈接
1.6 如何學(xué)習(xí)新特性
2. Java8新特性:Lambda表達(dá)式
2.1 關(guān)于Java8新特性簡(jiǎn)介
2.2 冗余的匿名內(nèi)部類
2.3 好用的lambda表達(dá)式
2.4 Lambda 及其使用舉例
2.5 語(yǔ)法
2.6 關(guān)于類型推斷
3. Java8新特性:函數(shù)式(Functional)接口
3.1 什么是函數(shù)式接口
3.2 如何理解函數(shù)式接口
3.3 舉例
3.4 Java 內(nèi)置函數(shù)式接口
3.4.1 之前的函數(shù)式接口
3.4.2 四大核心函數(shù)式接口
3.4.3 其它接口
3.4.4 內(nèi)置接口代碼演示
3.4.5 練習(xí)
4. Java8新特性:方法引用與構(gòu)造器引用
4.1 方法引用
4.1.1 方法引用格式
4.1.2 方法引用使用前提
4.1.3 舉例
4.2 構(gòu)造器引用
4.3 數(shù)組構(gòu)造引用
5. Java8新特性:強(qiáng)大的Stream API
5.1 說(shuō)明
5.2 為什么要使用Stream API
5.3 什么是Stream
5.4 Stream的操作三個(gè)步驟
5.4.1 創(chuàng)建Stream實(shí)例
5.4.2 一系列中間操作
5.4.3 終止操作
5.5 Java9新增API
5.6 練習(xí)
1. Java版本迭代概述
1.1 發(fā)布特點(diǎn)(小步快跑,快速迭代)
從Java 9 這個(gè)版本開(kāi)始,Java 的計(jì)劃發(fā)布周期是 6個(gè)月
。
這意味著Java的更新從傳統(tǒng)的以特性驅(qū)動(dòng)
的發(fā)布周期,轉(zhuǎn)變?yōu)橐?code>時(shí)間驅(qū)動(dòng)的發(fā)布模式,并且承諾不會(huì)跳票。通過(guò)這樣的方式,開(kāi)發(fā)團(tuán)隊(duì)可以把一些關(guān)鍵特性盡早合并到 JDK 之中,以快速得到開(kāi)發(fā)者反饋,在一定程度上避免出現(xiàn)像 Java 9 兩次被迫延遲發(fā)布的窘?jīng)r。
針對(duì)企業(yè)客戶的需求,Oracle 將以三年
為周期發(fā)布長(zhǎng)期支持版本(long term support)。
Oracle 的官方觀點(diǎn)認(rèn)為:與 Java 7->8->9 相比,Java 9->10->11的升級(jí)和 8->8u20->8u40 更相似。
新模式下的 Java 版本發(fā)布都會(huì)包含許多變更,包括語(yǔ)言變更
和 JVM 變更
,這兩者都會(huì)對(duì) IDE、字節(jié)碼庫(kù)和框架產(chǎn)生重大影響。此外,不僅會(huì)新增其他 API,還會(huì)有 API被刪除
(這在 Java 8 之前沒(méi)有發(fā)生過(guò))。
目前看這種發(fā)布策略是非常成功的,解開(kāi)了 Java/JVM 演進(jìn)的許多枷鎖,至關(guān)重要的是,OpenJDK 的權(quán)力中心,正在轉(zhuǎn)移到開(kāi)發(fā)社區(qū)和開(kāi)發(fā)者手中。在新的模式中,既可以利用 LTS 滿足企業(yè)長(zhǎng)期可靠支持的需求,也可以滿足各種開(kāi)發(fā)者對(duì)于新特性迭代的訴求。因?yàn)橛?2-3 年的最小間隔粒度來(lái)試驗(yàn)一個(gè)特性,基本是不現(xiàn)實(shí)的。
1.2 名詞解釋
名詞解釋:Oracle JDK和Open JDK
這兩個(gè)JDK最大不同就是許可證不一樣。但是對(duì)于個(gè)人用戶來(lái)講,沒(méi)區(qū)別。 ?
名詞解釋:JEP
JEP(JDK Enhancement Proposals):jdk 改進(jìn)提案,每當(dāng)需要有新的設(shè)想時(shí)候,JEP可以提出非正式的規(guī)范(specification),被正式認(rèn)可的JEP正式寫(xiě)進(jìn)JDK的發(fā)展路線圖并分配版本號(hào)。
名詞解釋:LTS
LTS(Long-term Support)即長(zhǎng)期支持。Oracle官網(wǎng)提供了對(duì)Oracle JDK個(gè)別版本的長(zhǎng)期支持,即使發(fā)發(fā)行了新版本,比如目前最新的JDK19,在結(jié)束日期前,LTS版本都會(huì)被長(zhǎng)期支持。(出了bug,會(huì)被修復(fù),非LTS則不會(huì)再有補(bǔ)丁發(fā)布)所以,一定要選一個(gè)LTS版本,不然出了漏洞沒(méi)人修復(fù)了。
如果要選擇Oracle JDK,目前可選的LTS版本為8、11、17三個(gè)。
1.3 各版本支持時(shí)間路線圖
1.4 各版本介紹
jdk 9
Java 9 提供了超過(guò)150項(xiàng)
新功能特性,包括備受期待的模塊化系統(tǒng)、可交互的 REPL 工具:jshell,JDK 編譯工具,Java 公共 API 和私有代碼,以及安全增強(qiáng)、擴(kuò)展提升、性能管理改善等。
特性太多,查看鏈接:
JDK 9https://openjdk.java.net/projects/jdk9/ jdk 10
JDK 10https://openjdk.java.net/projects/jdk/10/
286: Local-Variable Type Inference 局部變量類型推斷
296: Consolidate the JDK Forest into a Single Repository JDK庫(kù)的合并
304: Garbage-Collector Interface 統(tǒng)一的垃圾回收接口
307: Parallel Full GC for G1 為G1提供并行的Full GC
310: Application Class-Data Sharing 應(yīng)用程序類數(shù)據(jù)(AppCDS)共享
312: Thread-Local Handshakes ThreadLocal握手交互
313: Remove the Native-Header Generation Tool (javah) 移除JDK中附帶的javah工具
314: Additional Unicode Language-Tag Extensions 使用附加的Unicode語(yǔ)言標(biāo)記擴(kuò)展
316: Heap Allocation on Alternative Memory Devices 能將堆內(nèi)存占用分配給用戶指定的備用內(nèi)存設(shè)備
317: Experimental Java-Based JIT Compiler 使用Graal基于Java的編譯器
319: Root Certificates 根證書(shū)
322: Time-Based Release Versioning 基于時(shí)間定于的發(fā)布版本
jdk 11
JDK 11https://openjdk.java.net/projects/jdk/11/
181: Nest-Based Access Control 基于嵌套的訪問(wèn)控制
309: Dynamic Class-File Constants 動(dòng)態(tài)類文件常量
315: Improve Aarch64 Intrinsics 改進(jìn) Aarch64 Intrinsics
318: Epsilon: A No-Op Garbage Collector Epsilon — 一個(gè)No-Op(無(wú)操作)的垃圾收集器 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 參數(shù)的局部變量語(yǔ)法
324: Key Agreement with Curve25519 and Curve448 Curve25519 和 Curve448 算法的密鑰協(xié)議
327: Unicode 10
328: Flight Recorder 飛行記錄儀
329: ChaCha20 and Poly1305 Cryptographic Algorithms ChaCha20 和 Poly1305 加密算法 330: Launch Single-File Source-Code Programs 啟動(dòng)單一文件的源代碼程序
331: Low-Overhead Heap Profiling 低開(kāi)銷的 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 12https://openjdk.java.net/projects/jdk/12/
189:Shenandoah: A Low-Pause-Time Garbage Collector (Experimental) 低暫停時(shí)間的GC 230: Microbenchmark Suite 微基準(zhǔn)測(cè)試套件
325: Switch Expressions (Preview) switch表達(dá)式
334: JVM Constants API? JVM常量API
340: One AArch64 Port, Not Two 只保留一個(gè)AArch64實(shí)現(xiàn)
341: Default CDS Archives 默認(rèn)類數(shù)據(jù)共享歸檔文件
344: Abortable Mixed Collections for G1 可中止的G1 Mixed GC
346: Promptly Return Unused Committed Memory from G1 G1及時(shí)返回未使用的已分配內(nèi)存
jdk 13
JDK 13https://openjdk.java.net/projects/jdk/13/
350: Dynamic CDS Archives 動(dòng)態(tài)CDS檔案
351: ZGC: Uncommit Unused Memory ZGC:取消使用未使用的內(nèi)存
353: Reimplement the Legacy Socket API 重新實(shí)現(xiàn)舊版套接字API
354: Switch Expressions (Preview) switch表達(dá)式(預(yù)覽)
355: Text Blocks (Preview) 文本塊(預(yù)覽)
jdk 14
JDK 14https://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內(nèi)存分配
349: JFR Event Streaming JFR事件流
352: Non-Volatile Mapped Byte Buffers 非易失性映射字節(jié)緩沖區(qū)
358: Helpful NullPointerExceptions 實(shí)用的NullPointerExceptions
359: Records (Preview) 361: Switch Expressions (Standard) Switch表達(dá)式
362: Deprecate the Solaris and SPARC Ports 棄用Solaris和SPARC端口
363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector 刪除并發(fā)標(biāo)記掃描(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) 外部存儲(chǔ)器訪問(wèn)API
jdk 15
JDK 15https://openjdk.java.net/projects/jdk/15/
339: Edwards-Curve Digital Signature Algorithm (EdDSA) EdDSA 數(shù)字簽名算法
360: Sealed Classes (Preview) 密封類(預(yù)覽)
371: Hidden Classes 隱藏類
372: Remove the Nashorn JavaScript Engine 移除 Nashorn JavaScript 引擎
373: Reimplement the Legacy DatagramSocket API 重新實(shí)現(xiàn) Legacy DatagramSocket API 374: Disable and Deprecate Biased Locking 禁用偏向鎖定
375: Pattern Matching for instanceof (Second Preview) instanceof 模式匹配(第二次預(yù)覽) 377: ZGC: A Scalable Low-Latency Garbage Collector ZGC:一個(gè)可擴(kuò)展的低延遲垃圾收集器
378: Text Blocks 文本塊
379: Shenandoah: A Low-Pause-Time Garbage Collector Shenandoah:低暫停時(shí)間垃圾收集器
381: Remove the Solaris and SPARC Ports 移除 Solaris 和 SPARC 端口
383: Foreign-Memory Access API (Second Incubator) 外部存儲(chǔ)器訪問(wèn) API(第二次孵化版)
384: Records (Second Preview) Records(第二次預(yù)覽)
385: Deprecate RMI Activation for Removal 廢棄 RMI 激活機(jī)制
jdk 16
JDK 16https://openjdk.java.net/projects/jdk/16/
338: Vector API (Incubator) Vector API(孵化器)
347: Enable C++14 Language Features JDK C++的源碼中允許使用C++14的語(yǔ)言特性
357: Migrate from Mercurial to Git OpenJDK源碼的版本控制從Mercurial (hg) 遷移到git
369: Migrate to GitHub OpenJDK源碼的版本控制遷移到github上
376: ZGC: Concurrent Thread-Stack Processing ZGC:并發(fā)線程處理
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來(lái)簡(jiǎn)化native code的調(diào)用
390: Warnings for Value-Based Classes 提供基于值的類的警告
392: Packaging Tool jpackage打包工具轉(zhuǎn)正
393: Foreign-Memory Access API (Third Incubator)
394: Pattern Matching for instanceof Instanceof的模式匹配轉(zhuǎn)正
395: Records Records轉(zhuǎn)正
396: Strongly Encapsulate JDK Internals by Default 默認(rèn)情況下,封裝了JDK內(nèi)部構(gòu)件
397: Sealed Classes (Second Preview) 密封類
jdk 17
JDK 17https://openjdk.java.net/projects/jdk/17/
306: Restore Always-Strict Floating-Point Semantics 恢復(fù)始終嚴(yán)格的浮點(diǎn)語(yǔ)義
356: Enhanced Pseudo-Random Number Generators 增強(qiáng)型偽隨機(jī)數(shù)生成器
382: New macOS Rendering Pipeline 新的macOS渲染管道
391: macOS/AArch64 Port macOS/AArch64端口
398: Deprecate the Applet API for Removal 棄用Applet API后續(xù)將進(jìn)行刪除
403: Strongly Encapsulate JDK Internals 強(qiáng)封裝JDK的內(nèi)部API
406: Pattern Matching for switch (Preview) switch模式匹配(預(yù)覽)
407: Remove RMI Activation 刪除RMI激活機(jī)制
409: Sealed Classes 密封類轉(zhuǎn)正
410: Remove the Experimental AOT and JIT Compiler 刪除實(shí)驗(yàn)性的AOT和JIT編譯器
411: Deprecate the Security Manager for Removal 棄用即將刪除的安全管理器
412: Foreign Function & Memory API (Incubator) 外部函數(shù)和內(nèi)存API(孵化特性)
414: Vector API (Second Incubator) Vector API(第二次孵化特性)
415: Context-Specific Deserialization Filters 上下文特定的反序列化過(guò)濾器
1.5 JDK各版本下載鏈接
?Java Archive | Oraclehttps://www.oracle.com/java/technologies/downloads/archive/
?
1.6 如何學(xué)習(xí)新特性
對(duì)于新特性,我們應(yīng)該從哪幾個(gè)角度學(xué)習(xí)新特性呢?
-
語(yǔ)法層面:
-
比如JDK5中的自動(dòng)拆箱、自動(dòng)裝箱、enum、泛型
-
比如JDK8中的lambda表達(dá)式、接口中的默認(rèn)方法、靜態(tài)方法
-
比如JDK10中局部變量的類型推斷
-
比如JDK12中的switch
-
比如JDK13中的文本塊
-
-
API層面:
-
比如JDK8中的Stream、Optional、新的日期時(shí)間、HashMap的底層結(jié)構(gòu)
-
比如JDK9中String的底層結(jié)構(gòu)
-
新的 / 過(guò)時(shí)的 API
-
-
底層優(yōu)化
-
比如JDK8中永久代被元空間替代、新的JS執(zhí)行引擎
-
比如新的垃圾回收器、GC參數(shù)、JVM的優(yōu)化
-
2. Java8新特性:Lambda表達(dá)式
2.1 關(guān)于Java8新特性簡(jiǎn)介
Java 8 (又稱為 JDK 8或JDK1.8) 是 Java 語(yǔ)言開(kāi)發(fā)的一個(gè)主要版本。 Java 8 是oracle公司于2014年3月發(fā)布,可以看成是自Java 5 以來(lái)最具革命性的版本。Java 8為Java語(yǔ)言、編譯器、類庫(kù)、開(kāi)發(fā)工具與JVM帶來(lái)了大量新特性。
-
速度更快
-
代碼更少(增加了新的語(yǔ)法:Lambda 表達(dá)式)
-
強(qiáng)大的 Stream API
-
便于并行
-
并行流就是把一個(gè)內(nèi)容分成多個(gè)數(shù)據(jù)塊,并用不同的線程分別處理每個(gè)數(shù)據(jù)塊的流。相比較串行的流,并行的流可以很大程度上提高程序的執(zhí)行效率。
-
Java 8 中將并行進(jìn)行了優(yōu)化,我們可以很容易的對(duì)數(shù)據(jù)進(jìn)行并行操作。Stream API 可以聲明性地通過(guò) parallel() 與 sequential() 在并行流與順序流之間進(jìn)行切換。
-
-
最大化減少空指針異常:Optional
-
Nashorn引擎,允許在JVM上運(yùn)行JS應(yīng)用
-
發(fā)音“nass-horn”,是德國(guó)二戰(zhàn)時(shí)一個(gè)坦克的命名
-
javascript運(yùn)行在jvm已經(jīng)不是新鮮事了,Rhino早在jdk6的時(shí)候已經(jīng)存在?,F(xiàn)在替代Rhino,官方的解釋是Rhino相比其他JavaScript引擎(比如google的V8)實(shí)在太慢了,改造Rhino還不如重寫(xiě)。所以Nashorn的性能也是其一個(gè)亮點(diǎn)。
-
Nashorn 項(xiàng)目在 JDK 9 中得到改進(jìn);在JDK11 中
Deprecated
,后續(xù)JDK15版本中remove
。在JDK11中取以代之的是GraalVM。(GraalVM是一個(gè)運(yùn)行時(shí)平臺(tái),它支持Java和其他基于Java字節(jié)碼的語(yǔ)言,但也支持其他語(yǔ)言,如JavaScript,Ruby,Python或LLVM。性能是Nashorn的2倍以上。)
-
2.2 冗余的匿名內(nèi)部類
當(dāng)需要啟動(dòng)一個(gè)線程去完成任務(wù)時(shí),通常會(huì)通過(guò)java.lang.Runnable
接口來(lái)定義任務(wù)內(nèi)容,并使用java.lang.Thread
類來(lái)啟動(dòng)該線程。代碼如下:
public class UseFunctionalProgramming {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多線程任務(wù)執(zhí)行!");
}
}).start(); // 啟動(dòng)線程
}
}
本著“一切皆對(duì)象”的思想,這種做法是無(wú)可厚非的:首先創(chuàng)建一個(gè)Runnable
接口的匿名內(nèi)部類對(duì)象來(lái)指定任務(wù)內(nèi)容,再將其交給一個(gè)線程來(lái)啟動(dòng)。
代碼分析:
對(duì)于Runnable
的匿名內(nèi)部類用法,可以分析出幾點(diǎn)內(nèi)容:
-
Thread
類需要Runnable
接口作為參數(shù),其中的抽象run
方法是用來(lái)指定線程任務(wù)內(nèi)容的核心; -
為了指定
run
的方法體,不得不需要Runnable
接口的實(shí)現(xiàn)類; -
為了省去定義一個(gè)
RunnableImpl
實(shí)現(xiàn)類的麻煩,不得不使用匿名內(nèi)部類; -
必須覆蓋重寫(xiě)抽象
run
方法,所以方法名稱、方法參數(shù)、方法返回值不得不再寫(xiě)一遍,且不能寫(xiě)錯(cuò); -
而實(shí)際上,似乎只有方法體才是關(guān)鍵所在。
2.3 好用的lambda表達(dá)式
2.4 Lambda 及其使用舉例
Lambda 是一個(gè)匿名函數(shù),我們可以把 Lambda 表達(dá)式理解為是一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣進(jìn)行傳遞)。使用它可以寫(xiě)出更簡(jiǎn)潔、更靈活的代碼。作為一種更緊湊的代碼風(fēng)格,使Java的語(yǔ)言表達(dá)能力得到了提升。
-
從匿名類到 Lambda 的轉(zhuǎn)換舉例1
-
?
-
從匿名類到 Lambda 的轉(zhuǎn)換舉例2
-
??
2.5 語(yǔ)法
Lambda 表達(dá)式:在Java 8 語(yǔ)言中引入的一種新的語(yǔ)法元素和操作符。這個(gè)操作符為 “->
” , 該操作符被稱為 Lambda 操作符
或箭頭操作符
。它將 Lambda 分為兩個(gè)部分:
-
左側(cè):指定了 Lambda 表達(dá)式需要的參數(shù)列表,參數(shù)的類型都可以省略(參數(shù)類型推斷),如果形參只有一個(gè),則一對(duì)()也可以省略
-
右側(cè):指定了 Lambda 體,是抽象方法的實(shí)現(xiàn)邏輯,也即 Lambda 表達(dá)式要執(zhí)行的功能。如果方法體中只有一行語(yǔ)句,可以省略一對(duì){},如果有return關(guān)鍵字則可以一并省略
語(yǔ)法格式一:無(wú)參,無(wú)返回值
@Test
public void test1(){
//未使用Lambda表達(dá)式
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我愛(ài)北京天安門");
}
};
r1.run();
System.out.println("***********************");
//使用Lambda表達(dá)式
Runnable r2 = () -> {
System.out.println("我愛(ài)北京故宮");
};
r2.run();
}
?語(yǔ)法格式二:Lambda 需要一個(gè)參數(shù),但是沒(méi)有返回值
@Test
public void test2(){
//未使用Lambda表達(dá)式
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("謊言和誓言的區(qū)別是什么?");
System.out.println("*******************");
//使用Lambda表達(dá)式
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一個(gè)是聽(tīng)得人當(dāng)真了,一個(gè)是說(shuō)的人當(dāng)真了");
}
語(yǔ)法格式三:數(shù)據(jù)類型可以省略,因?yàn)榭捎删幾g器推斷得出,稱為“類型推斷”
@Test
public void test3(){
//語(yǔ)法格式三使用前
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一個(gè)是聽(tīng)得人當(dāng)真了,一個(gè)是說(shuō)的人當(dāng)真了");
System.out.println("*******************");
//語(yǔ)法格式三使用后
Consumer<String> con2 = (s) -> {
System.out.println(s);
};
con2.accept("一個(gè)是聽(tīng)得人當(dāng)真了,一個(gè)是說(shuō)的人當(dāng)真了");
}
語(yǔ)法格式四:Lambda 若只需要一個(gè)參數(shù)時(shí),參數(shù)的小括號(hào)可以省略
@Test
public void test4(){
//語(yǔ)法格式四使用前
Consumer<String> con1 = (s) -> {
System.out.println(s);
};
con1.accept("一個(gè)是聽(tīng)得人當(dāng)真了,一個(gè)是說(shuō)的人當(dāng)真了");
System.out.println("*******************");
//語(yǔ)法格式四使用后
Consumer<String> con2 = s -> {
System.out.println(s);
};
con2.accept("一個(gè)是聽(tīng)得人當(dāng)真了,一個(gè)是說(shuō)的人當(dāng)真了");
}
語(yǔ)法格式五:Lambda 需要兩個(gè)或以上的參數(shù),多條執(zhí)行語(yǔ)句,并且可以有返回值
@Test
public void test5(){
//語(yǔ)法格式五使用前
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("*****************************");
//語(yǔ)法格式五使用后
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(12,6));
}
語(yǔ)法格式六:當(dāng) Lambda 體只有一條語(yǔ)句時(shí),return 與大括號(hào)若有,都可以省略
@Test
public void test6(){
//語(yǔ)法格式六使用前
Comparator<Integer> com1 = (o1,o2) -> {
return o1.compareTo(o2);
};
System.out.println(com1.compare(12,6));
System.out.println("*****************************");
//語(yǔ)法格式六使用后
Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
System.out.println(com2.compare(12,21));
}
@Test
public void test7(){
//語(yǔ)法格式六使用前
Consumer<String> con1 = s -> {
System.out.println(s);
};
con1.accept("一個(gè)是聽(tīng)得人當(dāng)真了,一個(gè)是說(shuō)的人當(dāng)真了");
System.out.println("*****************************");
//語(yǔ)法格式六使用后
Consumer<String> con2 = s -> System.out.println(s);
con2.accept("一個(gè)是聽(tīng)得人當(dāng)真了,一個(gè)是說(shuō)的人當(dāng)真了");
}
2.6 關(guān)于類型推斷
在語(yǔ)法格式三 Lambda 表達(dá)式中的參數(shù)類型都是由編譯器推斷得出的。Lambda 表達(dá)式中無(wú)需指定類型,程序依然可以編譯,這是因?yàn)?javac 根據(jù)程序的上下文,在后臺(tái)推斷出了參數(shù)的類型。Lambda 表達(dá)式的類型依賴于上下文環(huán)境,是由編譯器推斷出來(lái)的。這就是所謂的“類型推斷
”。
?
舉例:
@Test
public void test() {
//類型推斷1
ArrayList<String> list = new ArrayList<>();
//類型推斷2
int[] arr = {1, 2, 3};
}
3. Java8新特性:函數(shù)式(Functional)接口
3.1 什么是函數(shù)式接口
-
只包含
一個(gè)抽象方法
(Single Abstract Method,簡(jiǎn)稱SAM)的接口,稱為函數(shù)式接口。當(dāng)然該接口可以包含其他非抽象方法。 -
你可以通過(guò) Lambda 表達(dá)式來(lái)創(chuàng)建該接口的對(duì)象。(若 Lambda 表達(dá)式拋出一個(gè)受檢異常(即:非運(yùn)行時(shí)異常),那么該異常需要在目標(biāo)接口的抽象方法上進(jìn)行聲明)。
-
我們可以在一個(gè)接口上使用
@FunctionalInterface
注解,這樣做可以檢查它是否是一個(gè)函數(shù)式接口。同時(shí) javadoc 也會(huì)包含一條聲明,說(shuō)明這個(gè)接口是一個(gè)函數(shù)式接口。 -
在
java.util.function
包下定義了Java 8 的豐富的函數(shù)式接口
3.2 如何理解函數(shù)式接口
?
-
Java從誕生日起就是一直倡導(dǎo)“一切皆對(duì)象”,在Java里面面向?qū)ο?OOP)編程是一切。但是隨著python、scala等語(yǔ)言的興起和新技術(shù)的挑戰(zhàn),Java不得不做出調(diào)整以便支持更加廣泛的技術(shù)要求,即Java不但可以支持OOP還可以支持OOF(面向函數(shù)編程)
-
Java8引入了Lambda表達(dá)式之后,Java也開(kāi)始支持函數(shù)式編程。
-
Lambda表達(dá)式不是Java最早使用的。目前C++,C#,Python,Scala等均支持Lambda表達(dá)式。
-
-
面向?qū)ο蟮乃枷耄?/p>
-
做一件事情,找一個(gè)能解決這個(gè)事情的對(duì)象,調(diào)用對(duì)象的方法,完成事情。
-
-
函數(shù)式編程思想:
-
只要能獲取到結(jié)果,誰(shuí)去做的,怎么做的都不重要,重視的是結(jié)果,不重視過(guò)程。
-
-
在函數(shù)式編程語(yǔ)言當(dāng)中,函數(shù)被當(dāng)做一等公民對(duì)待。在將函數(shù)作為一等公民的編程語(yǔ)言中,Lambda表達(dá)式的類型是函數(shù)。但是在Java8中,有所不同。在Java8中,Lambda表達(dá)式是對(duì)象,而不是函數(shù),它們必須依附于一類特別的對(duì)象類型——函數(shù)式接口。
-
簡(jiǎn)單的說(shuō),在Java8中,Lambda表達(dá)式就是一個(gè)函數(shù)式接口的實(shí)例。這就是Lambda表達(dá)式和函數(shù)式接口的關(guān)系。也就是說(shuō),只要一個(gè)對(duì)象是函數(shù)式接口的實(shí)例,那么該對(duì)象就可以用Lambda表達(dá)式來(lái)表示。
3.3 舉例
舉例1:
?
舉例2:
?作為參數(shù)傳遞 Lambda 表達(dá)式:
作為參數(shù)傳遞 Lambda 表達(dá)式:為了將 Lambda 表達(dá)式作為參數(shù)傳遞,接收Lambda 表達(dá)式的參數(shù)類型必須是與該 Lambda 表達(dá)式兼容的函數(shù)式接口的類型。
3.4 Java 內(nèi)置函數(shù)式接口
3.4.1 之前的函數(shù)式接口
之前學(xué)過(guò)的接口,有些就是函數(shù)式接口,比如:
-
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 四大核心函數(shù)式接口
3.4.3 其它接口
類型1:消費(fèi)型接口
消費(fèi)型接口的抽象方法特點(diǎn):有形參,但是返回值類型是void
類型2:供給型接口
這類接口的抽象方法特點(diǎn):無(wú)參,但是有返回值
類型3:函數(shù)型接口
這類接口的抽象方法特點(diǎn):既有參數(shù)又有返回值
類型4:判斷型接口
這類接口的抽象方法特點(diǎn):有參,但是返回值類型是boolean結(jié)果。
3.4.4 內(nèi)置接口代碼演示
舉例1:
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參數(shù)的操作代碼應(yīng)用在每一個(gè)元素上。
list.forEach(s -> System.out.println(s));
}
}
?舉例2:
import java.util.function.Supplier;
public class TestSupplier {
public static void main(String[] args) {
Supplier<String> supplier = () -> "尚硅谷";
System.out.println(supplier.get());
}
}
?舉例3:
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:
import java.util.function.Function;
public class TestFunction {
public static void main(String[] args) {
//使用Lambda表達(dá)式實(shí)現(xiàn)Function<T,R>接口,可以實(shí)現(xiàn)將一個(gè)字符串首字母轉(zhuǎn)為大寫(xiě)的功能。
Function<String,String> fun = s -> s.substring(0,1).toUpperCase() + s.substring(1);
System.out.println(fun.apply("hello"));
}
}
3.4.5 練習(xí)
練習(xí)1:無(wú)參無(wú)返回值形式
假如有自定義函數(shù)式接口Call如下:
public interface Call {
void shout();
}
在測(cè)試類中聲明一個(gè)如下方法:
public static void callSomething(Call call){
call.shout();
}
在測(cè)試類的main方法中調(diào)用callSomething方法,并用Lambda表達(dá)式為形參call賦值,可以喊出任意你想說(shuō)的話。
public class TestLambda {
public static void main(String[] args) {
callSomething(()->System.out.println("回家吃飯"));
callSomething(()->System.out.println("我愛(ài)你"));
callSomething(()->System.out.println("滾蛋"));
callSomething(()->System.out.println("回來(lái)"));
}
public static void callSomething(Call call){
call.shout();
}
}
interface Call {
void shout();
}
練習(xí)2:消費(fèi)型接口
代碼示例:Consumer<T>接口
在JDK1.8中Collection集合接口的父接口Iterable接口中增加了一個(gè)默認(rèn)方法:
public default void forEach(Consumer<? super T> action)
遍歷Collection集合的每個(gè)元素,執(zhí)行“xxx消費(fèi)型”操作。
在JDK1.8中Map集合接口中增加了一個(gè)默認(rèn)方法:
public default void forEach(BiConsumer<? super K,? super V> action)
遍歷Map集合的每對(duì)映射關(guān)系,執(zhí)行“xxx消費(fèi)型”操作。
案例:
(1)創(chuàng)建一個(gè)Collection系列的集合,添加一些字符串,調(diào)用forEach方法遍歷查看
(2)創(chuàng)建一個(gè)Map系列的集合,添加一些(key,value)鍵值對(duì),調(diào)用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));
}
練習(xí)3:供給型接口
代碼示例:Supplier<T>接口
在JDK1.8中增加了StreamAPI,java.util.stream.Stream<T>是一個(gè)數(shù)據(jù)流。這個(gè)類型有一個(gè)靜態(tài)方法:
`public static <T> Stream<T> generate(Supplier<T> s)`可以創(chuàng)建Stream的對(duì)象。而又包含一個(gè)forEach方法可以遍歷流中的元素:`public void forEach(Consumer<? super T> action)`。
案例:
現(xiàn)在請(qǐng)調(diào)用Stream的generate方法,來(lái)產(chǎn)生一個(gè)流對(duì)象,并調(diào)用Math.random()方法來(lái)產(chǎn)生數(shù)據(jù),為Supplier函數(shù)式接口的形參賦值。最后調(diào)用forEach方法遍歷流中的數(shù)據(jù)查看結(jié)果。
@Test
public void test2(){
Stream.generate(() -> Math.random()).forEach(num -> System.out.println(num));
}
練習(xí)4:功能型接口
代碼示例:Function<T,R>接口
在JDK1.8時(shí)Map接口增加了很多方法,例如:
public default void replaceAll(BiFunction<? super K,? super V,? extends V> function)
按照f(shuō)unction指定的操作替換map中的value。
public default void forEach(BiConsumer<? super K,? super V> action)
遍歷Map集合的每對(duì)映射關(guān)系,執(zhí)行“xxx消費(fèi)型”操作。
案例:
(1)聲明一個(gè)Employee員工類型,包含編號(hào)、姓名、薪資。
(2)添加n個(gè)員工對(duì)象到一個(gè)HashMap<Integer,Employee>集合中,其中員工編號(hào)為key,員工對(duì)象為value。
(3)調(diào)用Map的forEach遍歷集合
(4)調(diào)用Map的replaceAll方法,將其中薪資低于10000元的,薪資設(shè)置為10000。
(5)再次調(diào)用Map的forEach遍歷集合查看結(jié)果
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 + "]";
}
}
測(cè)試類:
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));
}
}
練習(xí)5:判斷型接口
代碼示例:Predicate<T>接口
JDK1.8時(shí),Collecton<E>接口增加了一下方法,其中一個(gè)如下:
public default boolean removeIf(Predicate<? super E> filter)
用于刪除集合中滿足filter指定的條件判斷的。
public default void forEach(Consumer<? super T> action)
遍歷Collection集合的每個(gè)元素,執(zhí)行“xxx消費(fèi)型”操作。
案例:
(1)添加一些字符串到一個(gè)Collection集合中
(2)調(diào)用forEach遍歷集合
(3)調(diào)用removeIf方法,刪除其中字符串的長(zhǎng)度<5的
(4)再次調(diào)用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));
}
}
練習(xí)6:判斷型接口
案例:
(1)聲明一個(gè)Employee員工類型,包含編號(hào)、姓名、性別,年齡,薪資。
(2)聲明一個(gè)EmployeeSerice員工管理類,包含一個(gè)ArrayList<Employee>集合的屬性all,在EmployeeSerice的構(gòu)造器中,創(chuàng)建一些員工對(duì)象,為all集合初始化。
(3)在EmployeeSerice員工管理類中,聲明一個(gè)方法:ArrayList<Employee> get(Predicate<Employee> p),即將滿足p指定的條件的員工,添加到一個(gè)新的ArrayList<Employee> 集合中返回。
(4)在測(cè)試類中創(chuàng)建EmployeeSerice員工管理類的對(duì)象,并調(diào)用get方法,分別獲?。?/p>
-
所有員工對(duì)象
-
所有年齡超過(guò)35的員工
-
所有薪資高于15000的女員工
-
所有編號(hào)是偶數(shù)的員工
-
名字是“張三”的員工
-
年齡超過(guò)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, "無(wú)能", '男', 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;
}
}
測(cè)試類:
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新特性:方法引用與構(gòu)造器引用
Lambda表達(dá)式是可以簡(jiǎn)化函數(shù)式接口的變量或形參賦值的語(yǔ)法。而方法引用和構(gòu)造器引用是為了簡(jiǎn)化Lambda表達(dá)式的。
4.1 方法引用
當(dāng)要傳遞給Lambda體的操作,已經(jīng)有實(shí)現(xiàn)的方法了,可以使用方法引用!
方法引用可以看做是Lambda表達(dá)式深層次的表達(dá)。換句話說(shuō),方法引用就是Lambda表達(dá)式,也就是函數(shù)式接口的一個(gè)實(shí)例,通過(guò)方法的名字來(lái)指向一個(gè)方法,可以認(rèn)為是Lambda表達(dá)式的一個(gè)語(yǔ)法糖。
語(yǔ)法糖(Syntactic sugar),也譯為糖衣語(yǔ)法,是由英國(guó)計(jì)算機(jī)科學(xué)家彼得·約翰·蘭達(dá)(Peter J. Landin)發(fā)明的一個(gè)術(shù)語(yǔ),指計(jì)算機(jī)語(yǔ)言中添加的某種語(yǔ)法,這種語(yǔ)法
對(duì)語(yǔ)言的功能并沒(méi)有影響,但是更方便程序員使用
。通常來(lái)說(shuō)使用語(yǔ)法糖能夠增加程序的可讀性,從而減少程序代碼出錯(cuò)的機(jī)會(huì)。
4.1.1 方法引用格式
-
格式:使用方法引用操作符 “
::
” 將類(或?qū)ο? 與 方法名分隔開(kāi)來(lái)。-
兩個(gè):中間不能有空格,而且必須英文狀態(tài)下半角輸入
-
-
如下三種主要使用情況:
-
情況1:
對(duì)象 :: 實(shí)例方法名
-
情況2:
類 :: 靜態(tài)方法名
-
情況3:
類 :: 實(shí)例方法名
-
4.1.2 方法引用使用前提
要求1:Lambda體只有一句語(yǔ)句,并且是通過(guò)調(diào)用一個(gè)對(duì)象的/類現(xiàn)有的方法來(lái)完成的
例如:System.out對(duì)象,調(diào)用println()方法來(lái)完成Lambda體
Math類,調(diào)用random()靜態(tài)方法來(lái)完成Lambda體
要求2:
針對(duì)情況1:函數(shù)式接口中的抽象方法a在被重寫(xiě)時(shí)使用了某一個(gè)對(duì)象的方法b。如果方法a的形參列表、返回值類型與方法b的形參列表、返回值類型都相同,則我們可以使用方法b實(shí)現(xiàn)對(duì)方法a的重寫(xiě)、替換。
針對(duì)情況2:函數(shù)式接口中的抽象方法a在被重寫(xiě)時(shí)使用了某一個(gè)類的靜態(tài)方法b。如果方法a的形參列表、返回值類型與方法b的形參列表、返回值類型都相同,則我們可以使用方法b實(shí)現(xiàn)對(duì)方法a的重寫(xiě)、替換。
針對(duì)情況3:函數(shù)式接口中的抽象方法a在被重寫(xiě)時(shí)使用了某一個(gè)對(duì)象的方法b。如果方法a的返回值類型與方法b的返回值類型相同,同時(shí)方法a的形參列表中有n個(gè)參數(shù),方法b的形參列表有n-1個(gè)參數(shù),且方法a的第1個(gè)參數(shù)作為方法b的調(diào)用者,且方法a的后n-1參數(shù)與方法b的n-1參數(shù)匹配(類型相同或滿足多態(tài)場(chǎng)景也可以)
例如:t->System.out.println(t)
() -> Math.random() 都是無(wú)參
4.1.3 舉例
public class MethodRefTest {
// 情況一:對(duì)象 :: 實(shí)例方法
//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());
}
// 情況二:類 :: 靜態(tài)方法
//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));
}
// 情況三:類 :: 實(shí)例方法 (有難度)
// 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 構(gòu)造器引用
當(dāng)Lambda表達(dá)式是創(chuàng)建一個(gè)對(duì)象,并且滿足Lambda表達(dá)式形參,正好是給創(chuàng)建這個(gè)對(duì)象的構(gòu)造器的實(shí)參列表,就可以使用構(gòu)造器引用。
格式:類名::new
舉例:
public class ConstructorRefTest {
//構(gòu)造器引用
//Supplier中的T get()
//Employee的空參構(gòu)造器: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"));
}
}
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 數(shù)組構(gòu)造引用
當(dāng)Lambda表達(dá)式是創(chuàng)建一個(gè)數(shù)組對(duì)象,并且滿足Lambda表達(dá)式形參,正好是給創(chuàng)建這個(gè)數(shù)組對(duì)象的長(zhǎng)度,就可以數(shù)組構(gòu)造引用。
格式:數(shù)組類型名::new
舉例:
//數(shù)組引用
//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新特性:強(qiáng)大的Stream API
5.1 說(shuō)明
-
Java8中有兩大最為重要的改變。第一個(gè)是 Lambda 表達(dá)式;另外一個(gè)則是 Stream API。
-
Stream API ( java.util.stream) 把真正的函數(shù)式編程風(fēng)格引入到Java中。這是目前為止對(duì)Java類庫(kù)
最好的補(bǔ)充
,因?yàn)镾tream API可以極大提供Java程序員的生產(chǎn)力,讓程序員寫(xiě)出高效率、干凈、簡(jiǎn)潔的代碼。 -
Stream 是 Java8 中處理集合的關(guān)鍵抽象概念,它可以指定你希望對(duì)集合進(jìn)行的操作,可以執(zhí)行非常復(fù)雜的查找、過(guò)濾和映射數(shù)據(jù)等操作。 使用Stream API 對(duì)集合數(shù)據(jù)進(jìn)行操作,就類似于使用 SQL 執(zhí)行的數(shù)據(jù)庫(kù)查詢。也可以使用 Stream API 來(lái)并行執(zhí)行操作。簡(jiǎn)言之,Stream API 提供了一種高效且易于使用的處理數(shù)據(jù)的方式。
5.2 為什么要使用Stream API
實(shí)際開(kāi)發(fā)中,項(xiàng)目中多數(shù)數(shù)據(jù)源都來(lái)自于MySQL、Oracle等。但現(xiàn)在數(shù)據(jù)源可以更多了,有MongDB,Radis等,而這些NoSQL的數(shù)據(jù)就需要Java層面去處理。
5.3 什么是Stream
Stream 是數(shù)據(jù)渠道,用于操作數(shù)據(jù)源(集合、數(shù)組等)所生成的元素序列。
Stream 和 Collection 集合的區(qū)別:Collection 是一種靜態(tài)的內(nèi)存數(shù)據(jù)結(jié)構(gòu),講的是數(shù)據(jù),而 Stream 是有關(guān)計(jì)算的,講的是計(jì)算。前者是主要面向內(nèi)存,存儲(chǔ)在內(nèi)存中,后者主要是面向 CPU,通過(guò) CPU 實(shí)現(xiàn)計(jì)算。
注意:
①Stream 自己不會(huì)存儲(chǔ)元素。
②Stream 不會(huì)改變?cè)磳?duì)象。相反,他們會(huì)返回一個(gè)持有結(jié)果的新Stream。
③Stream 操作是延遲執(zhí)行的。這意味著他們會(huì)等到需要結(jié)果的時(shí)候才執(zhí)行。即一旦執(zhí)行終止操作,就執(zhí)行中間操作鏈,并產(chǎn)生結(jié)果。
④ Stream一旦執(zhí)行了終止操作,就不能再調(diào)用其它中間操作或終止操作了。
5.4 Stream的操作三個(gè)步驟
1 - 創(chuàng)建 Stream 一個(gè)數(shù)據(jù)源(如:集合、數(shù)組),獲取一個(gè)流
2 - 中間操作 每次處理都會(huì)返回一個(gè)持有結(jié)果的新Stream,即中間操作的方法返回值仍然是Stream類型的對(duì)象。因此中間操作可以是個(gè)操作鏈
,可對(duì)數(shù)據(jù)源的數(shù)據(jù)進(jìn)行n次處理,但是在終結(jié)操作前,并不會(huì)真正執(zhí)行。
3 - 終止操作(終端操作) 終止操作的方法返回值類型就不再是Stream了,因此一旦執(zhí)行終止操作,就結(jié)束整個(gè)Stream操作了。一旦執(zhí)行終止操作,就執(zhí)行中間操作鏈,最終產(chǎn)生結(jié)果并結(jié)束Stream。
?
5.4.1 創(chuàng)建Stream實(shí)例
方式一:通過(guò)集合
Java8 中的 Collection 接口被擴(kuò)展,提供了兩個(gè)獲取流的方法:
-
default Stream<E> stream() : 返回一個(gè)順序流
-
default Stream<E> parallelStream() : 返回一個(gè)并行流
@Test
public void test01(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
//JDK1.8中,Collection系列集合增加了方法
Stream<Integer> stream = list.stream();
}
方式二:通過(guò)數(shù)組
Java8 中的 Arrays 的靜態(tài)方法 stream() 可以獲取數(shù)組流:
-
static <T> Stream<T> stream(T[] array): 返回一個(gè)流
-
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);
}
方式三:通過(guò)Stream的of()
可以調(diào)用Stream類靜態(tài)方法 of(), 通過(guò)顯示值創(chuàng)建一個(gè)流。它可以接收任意數(shù)量的參數(shù)。
-
public static<T> Stream<T> of(T... values) : 返回一個(gè)流
@Test
public void test04(){
Stream<Integer> stream = Stream.of(1,2,3,4,5);
stream.forEach(System.out::println);
}
方式四:創(chuàng)建無(wú)限流(了解)
可以使用靜態(tài)方法 Stream.iterate() 和 Stream.generate(), 創(chuàng)建無(wú)限流。
-
迭代 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
-
生成 public static<T> Stream<T> generate(Supplier<T> s)
// 方式四:創(chuàng)建無(wú)限流
@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 一系列中間操作
多個(gè)中間操作可以連接起來(lái)形成一個(gè)流水線,除非流水線上觸發(fā)終止操作,否則中間操作不會(huì)執(zhí)行任何的處理!而在終止操作時(shí)一次性全部處理,稱為“惰性求值”。
1 - 篩選與切片
2 - 映射
3 - 排序
代碼舉例:
import org.junit.Test;
import java.util.Arrays;
import java.util.stream.Stream;
public class StreamMiddleOperate {
@Test
public void test01(){
//1、創(chuàng)建Stream
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
//2、加工處理
//過(guò)濾:filter(Predicate?p)
//把里面的偶數(shù)拿出來(lái)
/*
* filter(Predicate?p)
* Predicate是函數(shù)式接口,抽象方法:boolean test(T t)
*/
stream = stream.filter(t -> t%2==0);
//3、終結(jié)操作:例如:遍歷
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(){
//希望能夠找出前三個(gè)最大值,前三名最大的,不重復(fù)
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) 現(xiàn)在的R是一個(gè)Stream
.forEach(System.out::println);
}
}
5.4.3 終止操作
-
終端操作會(huì)從流的流水線生成結(jié)果。其結(jié)果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
-
流進(jìn)行了終止操作后,不能再次使用。
1 - 匹配與查找
2 - 歸約
3 - 收集
Collector 接口中方法的實(shí)現(xiàn)決定了如何對(duì)流執(zhí)行收集的操作(如收集到 List、Set、Map)。
另外, Collectors 實(shí)用類提供了很多靜態(tài)方法,可以方便地創(chuàng)建常見(jiàn)收集器實(shí)例,具體方法與實(shí)例如下表:
方法 | 返回類型 | 作用 |
---|---|---|
toList | Collector<T, ?, List<T>> | 把流中元素收集到List |
List<Employee> emps = list.stream().collect(Collectors.toList());
方法 | 返回類型 | 作用 |
---|---|---|
toSet | Collector<T, ?, Set<T>> | 把流中元素收集到Set |
Set<Employee> emps= list.stream().collect(Collectors.toSet());
方法 | 返回類型 | 作用 |
---|---|---|
toCollection | Collector<T, ?, C> | 把流中元素收集到創(chuàng)建的集合 |
Collection<Employee> emps =
list.stream().collect(Collectors.toCollection(ArrayList::new));
方法 | 返回類型 | 作用 |
---|---|---|
counting | Collector<T, ?, Long> | 計(jì)算流中元素的個(gè)數(shù) |
long count = list.stream().collect(Collectors.counting());
方法 | 返回類型 | 作用 |
---|---|---|
summingInt | Collector<T, ?, Integer> | 對(duì)流中元素的整數(shù)屬性求和 |
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
方法 | 返回類型 | 作用 |
---|---|---|
averagingInt | Collector<T, ?, Double> | 計(jì)算流中元素Integer屬性的平均值 |
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
方法 | 返回類型 | 作用 |
---|---|---|
summarizingInt | Collector<T, ?, IntSummaryStatistics> | 收集流中Integer屬性的統(tǒng)計(jì)值。如:平均值 |
int SummaryStatisticsiss=
list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
方法 | 返回類型 | 作用 |
---|---|---|
joining | Collector<CharSequence, ?, String> | 連接流中每個(gè)字符串 |
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
方法 | 返回類型 | 作用 |
---|---|---|
maxBy | Collector<T, ?, Optional<T>> | 根據(jù)比較器選擇最大值 |
Optional<Emp>max=
list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
方法 | 返回類型 | 作用 |
---|---|---|
minBy | Collector<T, ?, Optional<T>> | 根據(jù)比較器選擇最小值 |
Optional<Emp> min =
list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
方法 | 返回類型 | 作用 |
---|---|---|
reducing | Collector<T, ?, Optional<T>> | 從一個(gè)作為累加器的初始值開(kāi)始,利用BinaryOperator與流中元素逐個(gè)結(jié)合,從而歸約成單個(gè)值 |
int total=
list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
方法 | 返回類型 | 作用 |
---|---|---|
collectingAndThen | Collector<T,A,RR> | 包裹另一個(gè)收集器,對(duì)其結(jié)果轉(zhuǎn)換函數(shù) |
int how =
list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
方法 | 返回類型 | 作用 |
---|---|---|
groupingBy | Collector<T, ?, Map<K, List<T>>> | 根據(jù)某屬性值對(duì)流分組,屬性為K,結(jié)果為V |
Map<Emp.Status, List<Emp>> map=
list.stream().collect(Collectors.groupingBy(Employee::getStatus));
方法 | 返回類型 | 作用 |
---|---|---|
partitioningBy | Collector<T, ?, Map<Boolean, List<T>>> | 根據(jù)true或false進(jìn)行分區(qū) |
Map<Boolean,List<Emp>> vd =
list.stream().collect(Collectors.partitioningBy(Employee::getManage));
舉例
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實(shí)例化方法
ofNullable()的使用:
Java 8 中 Stream 不能完全為null,否則會(huì)報(bào)空指針異常。而 Java 9 中的 ofNullable 方法允許我們創(chuàng)建一個(gè)單元素 Stream,可以包含一個(gè)非空元素,也可以創(chuàng)建一個(gè)空 Stream。
//報(bào)NullPointerException
//Stream<Object> stream1 = Stream.of(null);
//System.out.println(stream1.count());
//不報(bào)異常,允許通過(guò)
Stream<String> stringStream = Stream.of("AA", "BB", null);
System.out.println(stringStream.count());//3
//不報(bào)異常,允許通過(guò)
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()重載的使用:
//原來(lái)的控制終止方式:
Stream.iterate(1,i -> i + 1).limit(10).forEach(System.out::println);
//現(xiàn)在的終止方式:
Stream.iterate(1,i -> i < 100,i -> i + 1).forEach(System.out::println);
5.6 練習(xí)
現(xiàn)在有兩個(gè) ArrayList 集合存儲(chǔ)隊(duì)伍當(dāng)中的多個(gè)成員姓名,要求使用傳統(tǒng)的for循環(huán)(或增強(qiáng)for循環(huán))依次進(jìn)行以 下若干操作步驟:
-
第一個(gè)隊(duì)伍只要名字為3個(gè)字的成員姓名;存儲(chǔ)到一個(gè)新集合中。
-
第一個(gè)隊(duì)伍篩選之后只要前3個(gè)人;存儲(chǔ)到一個(gè)新集合中。
-
第二個(gè)隊(duì)伍只要姓張的成員姓名;存儲(chǔ)到一個(gè)新集合中。
-
第二個(gè)隊(duì)伍篩選之后不要前2個(gè)人;存儲(chǔ)到一個(gè)新集合中。
-
將兩個(gè)隊(duì)伍合并為一個(gè)隊(duì)伍;存儲(chǔ)到一個(gè)新集合中。
-
根據(jù)姓名創(chuàng)建 Person 對(duì)象;存儲(chǔ)到一個(gè)新集合中。
-
打印整個(gè)隊(duì)伍的Person對(duì)象信息。
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?+?"'}";
????}
}
?兩個(gè)隊(duì)伍(集合)的代碼如下:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-421592.html
public?static?void?main(String[]?args)?{
???????//第一支隊(duì)伍
????????ArrayList<String>?one?=?new?ArrayList<>();
????????one.add("迪麗熱巴");
????????one.add("宋遠(yuǎn)橋");
????????one.add("蘇星河");
????????one.add("石破天");
????????one.add("石中玉");
????????one.add("老子");
????????one.add("莊子");
????????one.add("洪七公");
????????//第二支隊(duì)伍
????????ArrayList<String>?two?=?new?ArrayList<>();
????????two.add("古力娜扎");
????????two.add("張無(wú)忌");
????????two.add("趙麗穎");
????????two.add("張三豐");
????????two.add("尼古拉斯趙四");
????????two.add("張?zhí)鞇?ài)");
????????two.add("張二狗");
//?....編寫(xiě)代碼完成題目要求?
????}
參考答案:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-421592.html
public?static?void?main(String[]?args)?{
???????//第一支隊(duì)伍
????????ArrayList<String>?one?=?new?ArrayList<>();
????????one.add("迪麗熱巴");
????????one.add("宋遠(yuǎn)橋");
????????one.add("蘇星河");
????????one.add("石破天");
????????one.add("石中玉");
????????one.add("老子");
????????one.add("莊子");
????????one.add("洪七公");
????????//第二支隊(duì)伍
????????ArrayList<String>?two?=?new?ArrayList<>();
????????two.add("古力娜扎");
????????two.add("張無(wú)忌");
????????two.add("趙麗穎");
????????two.add("張三豐");
????????two.add("尼古拉斯趙四");
????????two.add("張?zhí)鞇?ài)");
????????two.add("張二狗");
????????
//?第一個(gè)隊(duì)伍只要名字為3個(gè)字的成員姓名;
????????//?第一個(gè)隊(duì)伍篩選之后只要前3個(gè)人;
????????Stream<String>?streamOne?=?one.stream().filter(s?‐>?s.length()?==?3).limit(3);
????????//?第二個(gè)隊(duì)伍只要姓張的成員姓名;
????????//?第二個(gè)隊(duì)伍篩選之后不要前2個(gè)人;
????????Stream<String>?streamTwo?=?two.stream().filter(s?‐>?s.startsWith("張")).skip(2);
????????//?將兩個(gè)隊(duì)伍合并為一個(gè)隊(duì)伍;
????????//?根據(jù)姓名創(chuàng)建Person對(duì)象;
????????//?打印整個(gè)隊(duì)伍的Person對(duì)象信息。
????????Stream.concat(streamOne,?streamTwo).map(Person::new).forEach(System.out::println);
????????
}
到了這里,關(guān)于Java語(yǔ)法理論和面經(jīng)雜疑篇《十一. JDK8新特性》的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!