中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

清原招聘網(wǎng)站建設(shè)成crm軟件

清原招聘網(wǎng)站建設(shè),成crm軟件,龍炎電商軟件,吉林做網(wǎng)站JDK8新特性 ? Java 是第一大編程語(yǔ)言和開(kāi)發(fā)平臺(tái)。它有助于企業(yè)降低成本、縮短開(kāi)發(fā)周期、推動(dòng)創(chuàng)新以及改善應(yīng)用服務(wù)。如今全球有數(shù)百萬(wàn)開(kāi)發(fā)人員運(yùn)行著超過(guò) 51 億個(gè) Java 虛擬機(jī),Java 仍是企業(yè)和開(kāi)發(fā)人員的首選開(kāi)發(fā)平臺(tái) 課程內(nèi)容的介紹 了解Java發(fā)展史Lambda表達(dá)式…

JDK8新特性

? Java 是第一大編程語(yǔ)言和開(kāi)發(fā)平臺(tái)。它有助于企業(yè)降低成本、縮短開(kāi)發(fā)周期、推動(dòng)創(chuàng)新以及改善應(yīng)用服務(wù)。如今全球有數(shù)百萬(wàn)開(kāi)發(fā)人員運(yùn)行著超過(guò) 51 億個(gè) Java 虛擬機(jī),Java 仍是企業(yè)和開(kāi)發(fā)人員的首選開(kāi)發(fā)平臺(tái)

課程內(nèi)容的介紹

  1. 了解Java發(fā)展史
  2. Lambda表達(dá)式
  3. 接口的增強(qiáng)
  4. 函數(shù)式接口
  5. 方法引用
  6. Stream API
  7. Optional
  8. 新時(shí)間日期API
  9. 其他新特性

一、Java發(fā)展歷史

1. Java的發(fā)展歷史

? Sun公司在1991年成立了一個(gè)稱為綠色計(jì)劃( Green Project )的項(xiàng)目,由James Gosling(高斯林)博土領(lǐng)導(dǎo),綠色計(jì)劃

的目的是開(kāi)發(fā)一種能夠在各種消費(fèi)性電子產(chǎn)品(機(jī)頂盒、冰箱、收音機(jī)等)上運(yùn)行的程序架構(gòu)。這個(gè)項(xiàng)目的產(chǎn)品就是

Java語(yǔ)言的前身: Oak(橡樹(shù))。Oak當(dāng)時(shí)在消費(fèi)品市場(chǎng)上并不算成功,但隨著1995年互聯(lián)網(wǎng)潮流的興起,Oak迅速找到

了最適合自己發(fā)展的市場(chǎng)定位。

  • JDK Beta - 1995
  • JDK 1.0 - 1996年1月 (真正第一個(gè)穩(wěn)定的版本JDK 1.0.2,被稱作 Java 1 )
  • JDK 1.1 - 1997年2月
  • J2SE 1.2 - 1998年12月
  • J2ME(Java 2 Micro Edition,Java 2平臺(tái)的微型版),應(yīng)用于移動(dòng)、無(wú)線及有限資源的環(huán)境。
  • J2SE(Java 2 Standard Edition,Java 2平臺(tái)的標(biāo)準(zhǔn)版),應(yīng)用于桌面環(huán)境。
  • J2EE(Java 2 Enterprise Edition,Java 2平臺(tái)的企業(yè)版),應(yīng)用于基于Java的應(yīng)用服務(wù)器。
  • J2SE 1.3 - 2000年5月
  • J2SE 1.4 - 2002年2月
  • J2SE 5.0 - 2004年9月
  • Java SE 6 - 2006年12月
  • Java SE 7 - 2011年7月
  • Java SE 8(LTS) - 2014年3月
  • Java SE 9 - 2017年9月
  • Java SE 10(18.3) - 2018年3月
  • **Java SE 11(18.9 LTS) **- 2018年9月
  • Java SE 12(19.3) - 2019年3月
  • Java SE 13(19.9) - 2019年9月
  • Java SE 14(20.3) - 2020年3月
  • Java SE 15(20.9) - 2020年9月

? 我們可以看到Java SE的主要版本大約每?jī)赡臧l(fā)布一次,直到Java SE 6到Java SE 7開(kāi)始花了五年時(shí)間,之后又花了三

年時(shí)間到達(dá)Java SE 8。

2.OpenJDK和OracleJDK

2.1 Open JDK來(lái)源

? Java 由 Sun 公司發(fā)明,Open JDK是Sun在2006年末把Java開(kāi)源而形成的項(xiàng)目。也就是說(shuō)Open JDK是Java SE平臺(tái)版

的開(kāi)源和免費(fèi)實(shí)現(xiàn),它由 SUN 和 Java 社區(qū)提供支持,2009年 Oracle 收購(gòu)了 Sun 公司,自此 Java 的維護(hù)方之一的

SUN 也變成了 Oracle。

2.2 Open JDK 和 Oracle JDK的關(guān)系

? 大多數(shù) JDK 都是在 Open JDK 的基礎(chǔ)上進(jìn)一步編寫(xiě)實(shí)現(xiàn)的,比如 IBM J9, Oracle JDK 和 Azul Zulu, Azul Zing。

Oracle JDK完全由 Oracle 公司開(kāi)發(fā),Oracle JDK是基于Open JDK源代碼的商業(yè)版本。此外,它包含閉源組件。

Oracle JDK根據(jù)二進(jìn)制代碼許可協(xié)議獲得許可,在沒(méi)有商業(yè)許可的情況下,在2019年1月之后發(fā)布的Oracle Java SE 8

的公開(kāi)更新將無(wú)法用于商業(yè)或生產(chǎn)用途。但是 Open JDK是完全開(kāi)源的,可以自由使用。

在這里插入圖片描述

2.3 Open JDK 官網(wǎng)介紹

Open JDK 官網(wǎng): http://openjdk.java.net/ 。

JDK Enhancement Proposals(JDK增強(qiáng)建議)。通俗的講JEP就是JDK的新特性

小結(jié)

Oracle JDK是基于Open JDK源代碼的商業(yè)版本。我們要學(xué)習(xí)Java新技術(shù)可以去Open JDK 官網(wǎng)學(xué)習(xí)。

二、Lambda表達(dá)式

1. 需求分析

? 創(chuàng)建一個(gè)新的線程,指定線程要執(zhí)行的任務(wù)

    public static void main(String[] args) {// 開(kāi)啟一個(gè)新的線程new Thread(new Runnable() {@Overridepublic void run() {System.out.println("新線程中執(zhí)行的代碼 : "+Thread.currentThread().getName());}}).start();System.out.println("主線程中的代碼:" + Thread.currentThread().getName());}

代碼分析:

  1. Thread類需要一個(gè)Runnable接口作為參數(shù),其中的抽象方法run方法是用來(lái)指定線程任務(wù)內(nèi)容的核心
  2. 為了指定run方法體,不得不需要Runnable的實(shí)現(xiàn)類
  3. 為了省去定義一個(gè)Runnable 的實(shí)現(xiàn)類,不得不使用匿名內(nèi)部類
  4. 必須覆蓋重寫(xiě)抽象的run方法,所有的方法名稱,方法參數(shù),方法返回值不得不都重寫(xiě)一遍,而且不能出錯(cuò),
  5. 而實(shí)際上,我們只在乎方法體中的代碼

2.Lambda表達(dá)式初體驗(yàn)

Lambda表達(dá)式是一個(gè)匿名函數(shù),可以理解為一段可以傳遞的代碼

new Thread(() -> { System.out.println("新線程Lambda表達(dá)式..." +Thread.currentThread().getName()); }).start();

Lambda表達(dá)式的優(yōu)點(diǎn):簡(jiǎn)化了匿名內(nèi)部類的使用,語(yǔ)法更加簡(jiǎn)單。

匿名內(nèi)部類語(yǔ)法冗余,體驗(yàn)了Lambda表達(dá)式后,發(fā)現(xiàn)Lambda表達(dá)式是簡(jiǎn)化匿名內(nèi)部類的一種方式。

3. Lambda的語(yǔ)法規(guī)則

Lambda省去了面向?qū)ο蟮臈l條框框,Lambda的標(biāo)準(zhǔn)格式由3個(gè)部分組成:

(參數(shù)類型 參數(shù)名稱) -> {代碼體;
}

格式說(shuō)明:

  • (參數(shù)類型 參數(shù)名稱):參數(shù)列表
  • {代碼體;} :方法體
  • -> : 箭頭,分割參數(shù)列表和方法體

3.1 Lambda練習(xí)1

? 練習(xí)無(wú)參無(wú)返回值的Lambda

定義一個(gè)接口

public interface UserService {void show();
}

然后創(chuàng)建主方法使用

public class Demo03Lambda {public static void main(String[] args) {goShow(new UserService() {@Overridepublic void show() {System.out.println("show 方法執(zhí)行了...");}});System.out.println("----------");goShow(() -> { System.out.println("Lambda show 方法執(zhí)行了..."); });}public static void goShow(UserService userService){userService.show();}
}

輸出:

show 方法執(zhí)行了...
----------
Lambda show 方法執(zhí)行了...

3.2 Lambda練習(xí)2

? 完成一個(gè)有參且有返回值得Lambda表達(dá)式案例

創(chuàng)建一個(gè)Person對(duì)象


@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {private String name;private Integer age;private Integer height;}

然后我們?cè)贚ist集合中保存多個(gè)Person對(duì)象,然后對(duì)這些對(duì)象做根據(jù)age排序操作

    public static void main(String[] args) {List<Person> list = new ArrayList<>();list.add(new Person("周杰倫",33,175));list.add(new Person("劉德華",43,185));list.add(new Person("周星馳",38,177));list.add(new Person("郭富城",23,170));Collections.sort(list, new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return o1.getAge()-o2.getAge();}});for (Person person : list) {System.out.println(person);}}

我們發(fā)現(xiàn)在sort方法的第二個(gè)參數(shù)是一個(gè)Comparator接口的匿名內(nèi)部類,且執(zhí)行的方法有參數(shù)和返回值,那么我們可以改寫(xiě)為L(zhǎng)ambda表達(dá)式

    public static void main(String[] args) {List<Person> list = new ArrayList<>();list.add(new Person("周杰倫",33,175));list.add(new Person("劉德華",43,185));list.add(new Person("周星馳",38,177));list.add(new Person("郭富城",23,170));/*Collections.sort(list, new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return o1.getAge()-o2.getAge();}});for (Person person : list) {System.out.println(person);}*/System.out.println("------");Collections.sort(list,(Person o1,Person o2) -> {return o1.getAge() - o2.getAge();});for (Person person : list) {System.out.println(person);}}

輸出結(jié)果

Person(name=郭富城, age=23, height=170)
Person(name=周杰倫, age=33, height=175)
Person(name=周星馳, age=38, height=177)
Person(name=劉德華, age=43, height=185)

4. @FunctionalInterface注解

/*** @FunctionalInterface*    這是一個(gè)標(biāo)志注解,被該注解修飾的接口只能聲明一個(gè)抽象方法*/
@FunctionalInterface
public interface UserService {void show();}

5. Lambda表達(dá)式的原理

匿名內(nèi)部類的本質(zhì)是在編譯時(shí)生成一個(gè)Class 文件。XXXXX$1.class

public class Demo01Lambda {public static void main(String[] args) {// 開(kāi)啟一個(gè)新的線程new Thread(new Runnable() {@Overridepublic void run() {System.out.println("新線程中執(zhí)行的代碼 : "+Thread.currentThread().getName());}}).start();System.out.println("主線程中的代碼:" + Thread.currentThread().getName());System.out.println("---------------");/*new Thread(() -> { System.out.println("新線程Lambda表達(dá)式..." +Thread.currentThread().getName()); }).start();*/}
}

在這里插入圖片描述

還可以通過(guò)反編譯工具來(lái)查看生成的代碼 XJad 工具來(lái)查看

static class Demo01Lambda$1implements Runnable
{public void run(){System.out.println((new StringBuilder()).append("新線程中執(zhí)行的代碼 : " ).append(Thread.currentThread().getName()).toString());}Demo01Lambda$1(){}
}

那么Lambda表達(dá)式的原理是什么呢?我們也通過(guò)反編譯工具來(lái)查看

在這里插入圖片描述

寫(xiě)的有Lambda表達(dá)式的class文件,我們通過(guò)XJad查看報(bào)錯(cuò)。這時(shí)我們可以通過(guò)JDK自帶的一個(gè)工具:javap 對(duì)字節(jié)碼進(jìn)行反匯編操作。

javap -c -p 文件名.class-c:表示對(duì)代碼進(jìn)行反匯編
-p:顯示所有的類和成員

反匯編的結(jié)果:

E:\workspace\OpenClassWorkSpace\JDK8Demo\target\classes\com\bobo\jdk\lambda>javap -c -p Demo03Lambda.class
Compiled from "Demo03Lambda.java"
public class com.bobo.jdk.lambda.Demo03Lambda {public com.bobo.jdk.lambda.Demo03Lambda();Code:0: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: invokedynamic #2,  0              // InvokeDynamic #0:show:()Lcom/bobo/jdk/lambda/service/UserService;5: invokestatic  #3                  // Method goShow:(Lcom/bobo/jdk/lambda/service/UserService;)V8: returnpublic static void goShow(com.bobo.jdk.lambda.service.UserService);Code:0: aload_01: invokeinterface #4,  1            // InterfaceMethod com/bobo/jdk/lambda/service/UserService.show:()V6: returnprivate static void lambda$main$0();Code:0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #6                  // String Lambda show 方法執(zhí)行了...5: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: return
}

在這個(gè)反編譯的源碼中我們看到了一個(gè)靜態(tài)方法 lambda$main$0(),這個(gè)方法里面做了什么事情呢?我們通過(guò)debug的方式來(lái)查看下:

在這里插入圖片描述

上面的效果可以理解為如下:

public class Demo03Lambda {public static void main(String[] args) {....}private static void lambda$main$0();System.out.println("Lambda show 方法執(zhí)行了...");}
}

為了更加直觀的理解這個(gè)內(nèi)容,我們可以在運(yùn)行的時(shí)候添加 -Djdk.internal.lambda.dumpProxyClasses, 加上這個(gè)參數(shù)會(huì)將內(nèi)部class碼輸出到一個(gè)文件中

java -Djdk.internal.lambda.dumpProxyClasses 要運(yùn)行的包名.類名

命令執(zhí)行

E:\workspace\OpenClassWorkSpace\JDK8Demo\target\classes>java -Djdk.internal.lambda.dumpProxyClasses com.bobo.jdk.lambda.Demo03Lambda
Lambda show 方法執(zhí)行了...

在這里插入圖片描述

反編譯后的內(nèi)容:

在這里插入圖片描述

可以看到這個(gè)匿名的內(nèi)部類實(shí)現(xiàn)了UserService接口,并重寫(xiě)了show()方法。在show方法中調(diào)用了Demo03Lambda.lambda$main$0(),也就是調(diào)用了Lambda中的內(nèi)容。

public class Demo03Lambda {public static void main(String[] args) {goShow(new UserService() {@Overridepublic void show() {Demo03Lambda.lambda$main$0();}});System.out.println("----------");}public static void goShow(UserService userService){userService.show();}private static void lambda$main$0();System.out.println("Lambda show 方法執(zhí)行了...");}
}

小結(jié):

匿名內(nèi)部類在編譯的時(shí)候會(huì)產(chǎn)生一個(gè)class文件。

Lambda表達(dá)式在程序運(yùn)行的時(shí)候會(huì)形成一個(gè)類。

  1. 在類中新增了一個(gè)方法,這個(gè)方法的方法體就是Lambda表達(dá)式中的代碼
  2. 還會(huì)形成一個(gè)匿名內(nèi)部類,實(shí)現(xiàn)接口,重寫(xiě)抽象方法
  3. 在接口中重寫(xiě)方法會(huì)調(diào)用新生成的方法

6.Lambda表達(dá)式的省略寫(xiě)法

在lambda表達(dá)式的標(biāo)準(zhǔn)寫(xiě)法基礎(chǔ)上,可以使用省略寫(xiě)法的規(guī)則為:

  1. 小括號(hào)內(nèi)的參數(shù)類型可以省略
  2. 如果小括號(hào)內(nèi)有且僅有一個(gè)參數(shù),則小括號(hào)可以省略
  3. 如果大括號(hào)內(nèi)有且僅有一個(gè)語(yǔ)句,可以同時(shí)省略大括號(hào),return 關(guān)鍵字及語(yǔ)句分號(hào)。
public class Demo05Lambda {public static void main(String[] args) {goStudent((String name,Integer age)->{return name+age+" 6666 ...";});// 省略寫(xiě)法goStudent((name,age)-> name+age+" 6666 ...");System.out.println("------");goOrder((String name)->{System.out.println("--->" + name);return 666;});// 省略寫(xiě)法goOrder(name -> {System.out.println("--->" + name);return 666;});goOrder(name ->  666);}public static void goStudent(StudentService studentService){studentService.show("張三",22);}public static void goOrder(OrderService orderService){orderService.show("李四");}}

7.Lambda表達(dá)式的使用前提

Lambda表達(dá)式的語(yǔ)法是非常簡(jiǎn)潔的,但是Lambda表達(dá)式不是隨便使用的,使用時(shí)有幾個(gè)條件要特別注意

  1. 方法的參數(shù)或局部變量類型必須為接口才能使用Lambda
  2. 接口中有且僅有一個(gè)抽象方法(@FunctionalInterface)

8.Lambda和匿名內(nèi)部類的對(duì)比

Lambda和匿名內(nèi)部類的對(duì)比

  1. 所需類型不一樣

    • 匿名內(nèi)部類的類型可以是 類,抽象類,接口
    • Lambda表達(dá)式需要的類型必須是接口
  2. 抽象方法的數(shù)量不一樣

    • 匿名內(nèi)部類所需的接口中的抽象方法的數(shù)量是隨意的
    • Lambda表達(dá)式所需的接口中只能有一個(gè)抽象方法
  3. 實(shí)現(xiàn)原理不一樣

    • 匿名內(nèi)部類是在編譯后形成一個(gè)class
    • Lambda表達(dá)式是在程序運(yùn)行的時(shí)候動(dòng)態(tài)生成class

三、接口中新增的方法

1. JDK8中接口的新增

在JDK8中針對(duì)接口有做增強(qiáng),在JDK8之前

interface 接口名{靜態(tài)常量;抽象方法;
}

JDK8之后對(duì)接口做了增加,接口中可以有默認(rèn)方法靜態(tài)方法

interface 接口名{靜態(tài)常量;抽象方法;默認(rèn)方法;靜態(tài)方法;
}

2.默認(rèn)方法

2.1 為什么要增加默認(rèn)方法

在JDK8以前接口中只能有抽象方法和靜態(tài)常量,會(huì)存在以下的問(wèn)題:

如果接口中新增抽象方法,那么實(shí)現(xiàn)類都必須要抽象這個(gè)抽象方法,非常不利于接口的擴(kuò)展的

package com.bobo.jdk.inter;public class Demo01Interface {public static void main(String[] args) {A a = new B();A c = new C();}
}interface A{void test1();// 接口中新增抽象方法,所有實(shí)現(xiàn)類都需要重寫(xiě)這個(gè)方法,不利于接口的擴(kuò)展void test2();
}class B implements  A{@Overridepublic void test1() {}@Overridepublic void test2() {}
}class C implements A{@Overridepublic void test1() {}@Overridepublic void test2() {}
}

2.2 接口默認(rèn)方法的格式

? 接口中默認(rèn)方法的語(yǔ)法格式是

interface 接口名{修飾符 default 返回值類型 方法名{方法體;}
}
package com.bobo.jdk.inter;public class Demo01Interface {public static void main(String[] args) {A a = new B();a.test3();A c = new C();c.test3();}
}interface A{void test1();// 接口中新增抽象方法,所有實(shí)現(xiàn)類都需要重寫(xiě)這個(gè)方法,不利于接口的擴(kuò)展void test2();/*** 接口中定義的默認(rèn)方法* @return*/public default String  test3(){System.out.println("接口中的默認(rèn)方法執(zhí)行了...");return "hello";}
}class B implements  A{@Overridepublic void test1() {}@Overridepublic void test2() {}@Overridepublic String test3() {System.out.println("B 實(shí)現(xiàn)類中重寫(xiě)了默認(rèn)方法...");return "ok ...";}
}class C implements A{@Overridepublic void test1() {}@Overridepublic void test2() {}
}

2.3 接口中默認(rèn)方法的使用

接口中的默認(rèn)方法有兩種使用方式

  1. 實(shí)現(xiàn)類直接調(diào)用接口的默認(rèn)方法
  2. 實(shí)現(xiàn)類重寫(xiě)接口的默認(rèn)方法

3. 靜態(tài)方法

JDK8中為接口新增了靜態(tài)方法,作用也是為了接口的擴(kuò)展

3.1 語(yǔ)法規(guī)則

interface 接口名{修飾符 static 返回值類型 方法名{方法體;}
}
package com.bobo.jdk.inter;public class Demo01Interface {public static void main(String[] args) {A a = new B();a.test3();A c = new C();c.test3();A.test4();}
}interface A{void test1();// 接口中新增抽象方法,所有實(shí)現(xiàn)類都需要重寫(xiě)這個(gè)方法,不利于接口的擴(kuò)展void test2();/*** 接口中定義的默認(rèn)方法* @return*/public default String  test3(){System.out.println("接口中的默認(rèn)方法執(zhí)行了...");return "hello";}/*** 接口中的靜態(tài)方法* @return*/public static String test4(){System.out.println("接口中的靜態(tài)方法....");return "Hello";}
}class B implements  A{@Overridepublic void test1() {}@Overridepublic void test2() {}@Overridepublic String test3() {System.out.println("B 實(shí)現(xiàn)類中重寫(xiě)了默認(rèn)方法...");return "ok ...";}}class C implements A{@Overridepublic void test1() {}@Overridepublic void test2() {}
}

3.2 靜態(tài)方法的使用

接口中的靜態(tài)方法在實(shí)現(xiàn)類中是不能被重寫(xiě)的,調(diào)用的話只能通過(guò)接口類型來(lái)實(shí)現(xiàn): 接口名.靜態(tài)方法名();

在這里插入圖片描述

4. 兩者的區(qū)別介紹

  1. 默認(rèn)方法通過(guò)實(shí)例調(diào)用,靜態(tài)方法通過(guò)接口名調(diào)用
  2. 默認(rèn)方法可以被繼承,實(shí)現(xiàn)類可以直接調(diào)用接口默認(rèn)方法,也可以重寫(xiě)接口默認(rèn)方法
  3. 靜態(tài)方法不能被繼承,實(shí)現(xiàn)類不能重寫(xiě)接口的靜態(tài)方法,只能使用接口名調(diào)用

四、函數(shù)式接口

1. 函數(shù)式接口的由來(lái)

? 我們知道使用Lambda表達(dá)式的前提是需要有函數(shù)式接口,而Lambda表達(dá)式使用時(shí)不關(guān)心接口名,抽象方法名。只關(guān)心抽象方法的參數(shù)列表和返回值類型。因此為了讓我們使用Lambda表達(dá)式更加的方法,在JDK中提供了大量常用的函數(shù)式接口

package com.bobo.jdk.fun;public class Demo01Fun {public static void main(String[] args) {fun1((arr)->{int sum = 0 ;for (int i : arr) {sum += i;}return sum;});}public static void fun1(Operator operator){int[] arr = {1,2,3,4};int sum = operator.getSum(arr);System.out.println("sum = " + sum);}
}/*** 函數(shù)式接口*/
@FunctionalInterface
interface Operator{int getSum(int[] arr);
}

2. 函數(shù)式接口介紹

在JDK中幫我們提供的有函數(shù)式接口,主要是在 java.util.function 包中。

2.1 Supplier

? 無(wú)參有返回值的接口,對(duì)于的Lambda表達(dá)式需要提供一個(gè)返回?cái)?shù)據(jù)的類型。

@FunctionalInterface
public interface Supplier<T> {/*** Gets a result.** @return a result*/T get();
}

使用:

/*** Supplier 函數(shù)式接口的使用*/
public class SupplierTest {public static void main(String[] args) {fun1(()->{int arr[] = {22,33,55,66,44,99,10};// 計(jì)算出數(shù)組中的最大值Arrays.sort(arr);return arr[arr.length-1];});}private static void fun1(Supplier<Integer> supplier){// get() 是一個(gè)無(wú)參的有返回值的 抽象方法Integer max = supplier.get();System.out.println("max = " + max);}
}

2.2 Consumer

? 有參無(wú)返回值得接口,前面介紹的Supplier接口是用來(lái)生產(chǎn)數(shù)據(jù)的,而Consumer接口是用來(lái)消費(fèi)數(shù)據(jù)的,使用的時(shí)候需要指定一個(gè)泛型來(lái)定義參數(shù)類型

@FunctionalInterface
public interface Consumer<T> {/*** Performs this operation on the given argument.** @param t the input argument*/void accept(T t);
}

使用:將輸入的數(shù)據(jù)統(tǒng)一轉(zhuǎn)換為小寫(xiě)輸出

public class ConsumerTest {public static void main(String[] args) {test(msg -> {System.out.println(msg + "-> 轉(zhuǎn)換為小寫(xiě):" + msg.toLowerCase());});}public static void test(Consumer<String> consumer){consumer.accept("Hello World");}
}

默認(rèn)方法:andThen

? 如果一個(gè)方法的參數(shù)和返回值全部是Consumer類型,那么就可以實(shí)現(xiàn)效果,消費(fèi)一個(gè)數(shù)據(jù)的時(shí)候,首先做一個(gè)操作,然后再做一個(gè)操作,實(shí)現(xiàn)組合,而這個(gè)方法就是Consumer接口中的default方法 andThen方法

    default Consumer<T> andThen(Consumer<? super T> after) {Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };}

具體的操作

public class ConsumerAndThenTest {public static void main(String[] args) {test2(msg1->{System.out.println(msg1 + "-> 轉(zhuǎn)換為小寫(xiě):" + msg1.toLowerCase());},msg2->{System.out.println(msg2 + "-> 轉(zhuǎn)換為大寫(xiě):" + msg2.toUpperCase());});}public static void test2(Consumer<String> c1,Consumer<String> c2){String str = "Hello World";//c1.accept(str); // 轉(zhuǎn)小寫(xiě)//c2.accept(str); // 轉(zhuǎn)大寫(xiě)//c1.andThen(c2).accept(str);c2.andThen(c1).accept(str);}
}

2.3 Function

? 有參有返回值的接口,Function接口是根據(jù)一個(gè)類型的數(shù)據(jù)得到另一個(gè)類型的數(shù)據(jù),前者稱為前置條件,后者稱為后置條件。有參數(shù)有返回值。

@FunctionalInterface
public interface Function<T, R> {/*** Applies this function to the given argument.** @param t the function argument* @return the function result*/R apply(T t);
}

使用:傳遞進(jìn)入一個(gè)字符串返回一個(gè)數(shù)字

public class FunctionTest {public static void main(String[] args) {test(msg ->{return Integer.parseInt(msg);});}public static void test(Function<String,Integer> function){Integer apply = function.apply("666");System.out.println("apply = " + apply);}
}

默認(rèn)方法:andThen,也是用來(lái)進(jìn)行組合操作,

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}
public class FunctionAndThenTest {public static void main(String[] args) {test(msg ->{return Integer.parseInt(msg);},msg2->{return msg2 * 10;});}public static void test(Function<String,Integer> f1,Function<Integer,Integer> f2){/*Integer i1 = f1.apply("666");Integer i2 = f2.apply(i1);*/Integer i2 = f1.andThen(f2).apply("666");System.out.println("i2:" + i2);}
}

默認(rèn)的compose方法的作用順序和andThen方法剛好相反

而靜態(tài)方法identity則是,輸入什么參數(shù)就返回什么參數(shù)

2.4 Predicate

有參且返回值為Boolean的接口

@FunctionalInterface
public interface Predicate<T> {/*** Evaluates this predicate on the given argument.** @param t the input argument* @return {@code true} if the input argument matches the predicate,* otherwise {@code false}*/boolean test(T t);
}

使用:

public class PredicateTest {public static void main(String[] args) {test(msg -> {return msg.length() > 3;},"HelloWorld");}private static void test(Predicate<String> predicate,String msg){boolean b = predicate.test(msg);System.out.println("b:" + b);}
}

在Predicate中的默認(rèn)方法提供了邏輯關(guān)系操作 and or negate isEquals方法

package com.bobo.jdk.fun;import java.util.function.Predicate;public class PredicateDefaultTest {public static void main(String[] args) {test(msg1 -> {return msg1.contains("H");},msg2 -> {return msg2.contains("W");});}private static void test(Predicate<String> p1,Predicate<String> p2){/*boolean b1 = predicate.test(msg);boolean b2 = predicate.test("Hello");*/// b1 包含H b2 包含W// p1 包含H 同時(shí) p2 包含Wboolean bb1 = p1.and(p2).test("Hello");// p1 包含H 或者 p2 包含Wboolean bb2 = p1.or(p2).test("Hello");// p1 不包含Hboolean bb3 = p1.negate().test("Hello");System.out.println(bb1); // FALSESystem.out.println(bb2); // TRUESystem.out.println(bb3); // FALSE}
}

五、方法引用

1. 為什么要用方法引用

1.1 lambda表達(dá)式冗余

在使用Lambda表達(dá)式的時(shí)候,也會(huì)出現(xiàn)代碼冗余的情況,比如:用Lambda表達(dá)式求一個(gè)數(shù)組的和

package com.bobo.jdk.funref;import java.util.function.Consumer;public class FunctionRefTest01 {public static void main(String[] args) {printMax(a->{// Lambda表達(dá)式中的代碼和 getTotal中的代碼冗余了int sum = 0;for (int i : a) {sum += i;}System.out.println("數(shù)組之和:" + sum);});}/*** 求數(shù)組中的所有元素的和* @param a*/public void getTotal(int a[]){int sum = 0;for (int i : a) {sum += i;}System.out.println("數(shù)組之和:" + sum);}private static void printMax(Consumer<int[]> consumer){int[] a= {10,20,30,40,50,60};consumer.accept(a);}
}

1.2 解決方案

? 因?yàn)樵贚ambda表達(dá)式中要執(zhí)行的代碼和我們另一個(gè)方法中的代碼是一樣的,這時(shí)就沒(méi)有必要重寫(xiě)一份邏輯了,這時(shí)我們就可以“引用”重復(fù)代碼

package com.bobo.jdk.funref;import java.util.function.Consumer;public class FunctionRefTest02 {public static void main(String[] args) {// :: 方法引用 也是JDK8中的新的語(yǔ)法printMax(FunctionRefTest02::getTotal);}/*** 求數(shù)組中的所有元素的和* @param a*/public static void getTotal(int a[]){int sum = 0;for (int i : a) {sum += i;}System.out.println("數(shù)組之和:" + sum);}private static void printMax(Consumer<int[]> consumer){int[] a= {10,20,30,40,50,60};consumer.accept(a);}
}

:: 方法引用 也是JDK8中的新的語(yǔ)法

2. 方法引用的格式

符號(hào)表示:::

符號(hào)說(shuō)明:雙冒號(hào)為方法引用運(yùn)算符,而它所在的表達(dá)式被稱為方法引用

應(yīng)用場(chǎng)景:如果Lambda表達(dá)式所要實(shí)現(xiàn)的方案,已經(jīng)有其他方法存在相同的方案,那么則可以使用方法引用。

常見(jiàn)的引用方式:

方法引用在JDK8中使用是相當(dāng)靈活的,有以下幾種形式:

  1. instanceName::methodName 對(duì)象::方法名

  2. ClassName::staticMethodName 類名::靜態(tài)方法

  3. ClassName::methodName 類名::普通方法

  4. ClassName::new 類名::new 調(diào)用的構(gòu)造器

  5. TypeName[]::new String[]::new 調(diào)用數(shù)組的構(gòu)造器

2.1 對(duì)象名::方法名

這是最常見(jiàn)的一種用法。如果一個(gè)類中的已經(jīng)存在了一個(gè)成員方法,則可以通過(guò)對(duì)象名引用成員方法

    public static void main(String[] args) {Date now = new Date();Supplier<Long> supplier = ()->{return now.getTime();};System.out.println(supplier.get());// 然后我們通過(guò) 方法引用 的方式來(lái)處理Supplier<Long> supplier1 = now::getTime;System.out.println(supplier1.get());}

方法引用的注意事項(xiàng):

  1. 被引用的方法,參數(shù)要和接口中的抽象方法的參數(shù)一樣
  2. 當(dāng)接口抽象方法有返回值時(shí),被引用的方法也必須有返回值

2.2 類名::靜態(tài)方法名

也是比較常用的方式:

public class FunctionRefTest04 {public static void main(String[] args) {Supplier<Long> supplier1 = ()->{return System.currentTimeMillis();};System.out.println(supplier1.get());// 通過(guò) 方法引用 來(lái)實(shí)現(xiàn)Supplier<Long> supplier2 = System::currentTimeMillis;System.out.println(supplier2.get());}
}

2.3 類名::引用實(shí)例方法

? Java面向?qū)ο笾?#xff0c;類名只能調(diào)用靜態(tài)方法,類名引用實(shí)例方法是用前提的,實(shí)際上是拿第一個(gè)參數(shù)作為方法的調(diào)用者

package com.bobo.jdk.funref;import java.util.Date;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;public class FunctionRefTest05 {public static void main(String[] args) {Function<String,Integer> function = (s)->{return s.length();};System.out.println(function.apply("hello"));// 通過(guò)方法引用來(lái)實(shí)現(xiàn)Function<String,Integer> function1 = String::length;System.out.println(function1.apply("hahahaha"));BiFunction<String,Integer,String> function2 = String::substring;String msg = function2.apply("HelloWorld", 3);System.out.println(msg);}
}

2.4 類名::構(gòu)造器

? 由于構(gòu)造器的名稱和類名完全一致,所以構(gòu)造器引用使用::new的格式使用,

public class FunctionRefTest06 {public static void main(String[] args) {Supplier<Person> sup = ()->{return new Person();};System.out.println(sup.get());// 然后通過(guò) 方法引用來(lái)實(shí)現(xiàn)Supplier<Person> sup1 = Person::new;System.out.println(sup1.get());BiFunction<String,Integer,Person> function = Person::new;System.out.println(function.apply("張三",22));}
}

2.5 數(shù)組::構(gòu)造器

? 數(shù)組是怎么構(gòu)造出來(lái)的呢?

    public static void main(String[] args) {Function<Integer,String[]> fun1 = (len)->{return new String[len];};String[] a1 = fun1.apply(3);System.out.println("數(shù)組的長(zhǎng)度是:" + a1.length);// 方法引用 的方式來(lái)調(diào)用數(shù)組的構(gòu)造器Function<Integer,String[]> fun2 = String[]::new;String[] a2 = fun2.apply(5);System.out.println("數(shù)組的長(zhǎng)度是:" + a2.length);}

小結(jié):方法引用是對(duì)Lambda表達(dá)式符合特定情況下的一種縮寫(xiě)方式,它使得我們的Lambda表達(dá)式更加的精簡(jiǎn),也可以理解為lambda表達(dá)式的縮寫(xiě)形式,不過(guò)要注意的是方法引用只能引用已經(jīng)存在的方法。

六、Stream API

1.集合處理數(shù)據(jù)的弊端

? 當(dāng)我們?cè)谛枰獙?duì)集合中的元素進(jìn)行操作的時(shí)候,除了必需的添加,刪除,獲取外,最典型的操作就是集合遍歷,

package com.bobo.jdk.stream;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;public class StreamTest01 {public static void main(String[] args) {// 定義一個(gè)List集合List<String> list = Arrays.asList("張三","張三豐","成龍","周星馳");// 1.獲取所有 姓張的信息List<String> list1 = new ArrayList<>();for (String s : list) {if(s.startsWith("張")){list1.add(s);}}// 2.獲取名稱長(zhǎng)度為3的用戶List<String> list2 = new ArrayList<>();for (String s : list1) {if(s.length() == 3){list2.add(s);}}// 3. 輸出所有的用戶信息for (String s : list2) {System.out.println(s);}}
}

上面的代碼針對(duì)與我們不同的需求總是一次次的循環(huán)循環(huán)循環(huán).這時(shí)我們希望有更加高效的處理方式,這時(shí)我們就可以通過(guò)JDK8中提供的Stream API來(lái)解決這個(gè)問(wèn)題了。

Stream更加優(yōu)雅的解決方案:

package com.bobo.jdk.stream;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;public class StreamTest02 {public static void main(String[] args) {// 定義一個(gè)List集合List<String> list = Arrays.asList("張三","張三豐","成龍","周星馳");// 1.獲取所有 姓張的信息// 2.獲取名稱長(zhǎng)度為3的用戶// 3. 輸出所有的用戶信息list.stream().filter(s->s.startsWith("張")).filter(s->s.length() == 3).forEach(s->{System.out.println(s);});System.out.println("----------");list.stream().filter(s->s.startsWith("張")).filter(s->s.length() == 3).forEach(System.out::println);}
}

上面的SteamAPI代碼的含義:獲取流,過(guò)濾張,過(guò)濾長(zhǎng)度,逐一打印。代碼相比于上面的案例更加的簡(jiǎn)潔直觀

2. Steam流式思想概述

注意:Stream和IO流(InputStream/OutputStream)沒(méi)有任何關(guān)系,請(qǐng)暫時(shí)忘記對(duì)傳統(tǒng)IO流的固有印象!
Stream流式思想類似于工廠車間的“生產(chǎn)流水線”,Stream流不是一種數(shù)據(jù)結(jié)構(gòu),不保存數(shù)據(jù),而是對(duì)數(shù)據(jù)進(jìn)行加工
處理。Stream可以看作是流水線上的一個(gè)工序。在流水線上,通過(guò)多個(gè)工序讓一個(gè)原材料加工成一個(gè)商品。

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

Stream API能讓我們快速完成許多復(fù)雜的操作,如篩選、切片、映射、查找、去除重復(fù),統(tǒng)計(jì),匹配和歸約。

3. Stream流的獲取方式

3.1 根據(jù)Collection獲取

? 首先,java.util.Collection 接口中加入了default方法 stream,也就是說(shuō)Collection接口下的所有的實(shí)現(xiàn)都可以通過(guò)steam方法來(lái)獲取Stream流。

    public static void main(String[] args) {List<String> list = new ArrayList<>();list.stream();Set<String> set = new HashSet<>();set.stream();Vector vector = new Vector();vector.stream();}

? 但是Map接口別沒(méi)有實(shí)現(xiàn)Collection接口,那這時(shí)怎么辦呢?這時(shí)我們可以根據(jù)Map獲取對(duì)應(yīng)的key value的集合。

    public static void main(String[] args) {Map<String,Object> map = new HashMap<>();Stream<String> stream = map.keySet().stream(); // keyStream<Object> stream1 = map.values().stream(); // valueStream<Map.Entry<String, Object>> stream2 = map.entrySet().stream(); // entry}

3.1 通過(guò)Stream的of方法

? 在實(shí)際開(kāi)發(fā)中我們不可避免的還是會(huì)操作到數(shù)組中的數(shù)據(jù),由于數(shù)組對(duì)象不可能添加默認(rèn)方法,所有Stream接口中提供了靜態(tài)方法of

public class StreamTest05 {public static void main(String[] args) {Stream<String> a1 = Stream.of("a1", "a2", "a3");String[] arr1 = {"aa","bb","cc"};Stream<String> arr11 = Stream.of(arr1);Integer[] arr2 = {1,2,3,4};Stream<Integer> arr21 = Stream.of(arr2);arr21.forEach(System.out::println);// 注意:基本數(shù)據(jù)類型的數(shù)組是不行的int[] arr3 = {1,2,3,4};Stream.of(arr3).forEach(System.out::println);}
}

4.Stream常用方法介紹

Stream常用方法
Stream流模型的操作很豐富,這里介紹一些常用的API。這些方法可以被分成兩種:

方法名方法作用返回值類型方法種類
count統(tǒng)計(jì)個(gè)數(shù)long終結(jié)
forEach逐一處理void終結(jié)
filter過(guò)濾Stream函數(shù)拼接
limit取用前幾個(gè)Stream函數(shù)拼接
skip跳過(guò)前幾個(gè)Stream函數(shù)拼接
map映射Stream函數(shù)拼接
concat組合Stream函數(shù)拼接

終結(jié)方法:返回值類型不再是 Stream 類型的方法,不再支持鏈?zhǔn)秸{(diào)用。本小節(jié)中,終結(jié)方法包括 count 和

forEach 方法。

非終結(jié)方法:返回值類型仍然是 Stream 類型的方法,支持鏈?zhǔn)秸{(diào)用。(除了終結(jié)方法外,其余方法均為非終結(jié)

方法。)

Stream注意事項(xiàng)(重要)

  1. Stream只能操作一次

  2. Stream方法返回的是新的流

  3. Stream不調(diào)用終結(jié)方法,中間的操作不會(huì)執(zhí)行

4.1 forEach

forEach用來(lái)遍歷流中的數(shù)據(jù)的

void forEach(Consumer<? super T> action);

該方法接受一個(gè)Consumer接口,會(huì)將每一個(gè)流元素交給函數(shù)處理

    public static void main(String[] args) {Stream.of("a1", "a2", "a3").forEach(System.out::println);;}

4.2 count

Stream流中的count方法用來(lái)統(tǒng)計(jì)其中的元素個(gè)數(shù)的

long count();

該方法返回一個(gè)long值,代表元素的個(gè)數(shù)。

    public static void main(String[] args) {long count = Stream.of("a1", "a2", "a3").count();System.out.println(count);}

4.3 filter

filter方法的作用是用來(lái)過(guò)濾數(shù)據(jù)的。返回符合條件的數(shù)據(jù)

在這里插入圖片描述

可以通過(guò)filter方法將一個(gè)流轉(zhuǎn)換成另一個(gè)子集流

Stream<T> filter(Predicate<? super T> predicate);

該接口接收一個(gè)Predicate函數(shù)式接口參數(shù)作為篩選條件

    public static void main(String[] args) {Stream.of("a1", "a2", "a3","bb","cc","aa","dd").filter((s)->s.contains("a")).forEach(System.out::println);}

輸出:

a1
a2
a3
aa

4.4 limit

在這里插入圖片描述

limit方法可以對(duì)流進(jìn)行截取處理,支取前n個(gè)數(shù)據(jù),

Stream<T> limit(long maxSize);

參數(shù)是一個(gè)long類型的數(shù)值,如果集合當(dāng)前長(zhǎng)度大于參數(shù)就進(jìn)行截取,否則不操作:

    public static void main(String[] args) {Stream.of("a1", "a2", "a3","bb","cc","aa","dd").limit(3).forEach(System.out::println);}

輸出:

a1
a2
a3

4.5 skip

在這里插入圖片描述

如果希望跳過(guò)前面幾個(gè)元素,可以使用skip方法獲取一個(gè)截取之后的新流:

   Stream<T> skip(long n);

操作:

    public static void main(String[] args) {Stream.of("a1", "a2", "a3","bb","cc","aa","dd").skip(3).forEach(System.out::println);}

輸出:

bb
cc
aa
dd

4.6 map

如果我們需要將流中的元素映射到另一個(gè)流中,可以使用map方法:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

在這里插入圖片描述

該接口需要一個(gè)Function函數(shù)式接口參數(shù),可以將當(dāng)前流中的T類型數(shù)據(jù)轉(zhuǎn)換為另一種R類型的數(shù)據(jù)

    public static void main(String[] args) {Stream.of("1", "2", "3","4","5","6","7")//.map(msg->Integer.parseInt(msg)).map(Integer::parseInt).forEach(System.out::println);}

4.7 sorted

如果需要將數(shù)據(jù)排序,可以使用sorted方法:

    Stream<T> sorted();

在使用的時(shí)候可以根據(jù)自然規(guī)則排序,也可以通過(guò)比較強(qiáng)來(lái)指定對(duì)應(yīng)的排序規(guī)則

    public static void main(String[] args) {Stream.of("1", "3", "2","4","0","9","7")//.map(msg->Integer.parseInt(msg)).map(Integer::parseInt)//.sorted() // 根據(jù)數(shù)據(jù)的自然順序排序.sorted((o1,o2)->o2-o1) // 根據(jù)比較強(qiáng)指定排序規(guī)則.forEach(System.out::println);}

4.8 distinct

如果要去掉重復(fù)數(shù)據(jù),可以使用distinct方法:

    Stream<T> distinct();

在這里插入圖片描述

使用:

    public static void main(String[] args) {Stream.of("1", "3", "3","4","0","1","7")//.map(msg->Integer.parseInt(msg)).map(Integer::parseInt)//.sorted() // 根據(jù)數(shù)據(jù)的自然順序排序.sorted((o1,o2)->o2-o1) // 根據(jù)比較強(qiáng)指定排序規(guī)則.distinct() // 去掉重復(fù)的記錄.forEach(System.out::println);System.out.println("--------");Stream.of(new Person("張三",18),new Person("李四",22),new Person("張三",18)).distinct().forEach(System.out::println);}

? Stream流中的distinct方法對(duì)于基本數(shù)據(jù)類型是可以直接出重的,但是對(duì)于自定義類型,我們是需要重寫(xiě)hashCode和equals方法來(lái)移除重復(fù)元素。

4.9 match

如果需要判斷數(shù)據(jù)是否匹配指定的條件,可以使用match相關(guān)的方法

boolean anyMatch(Predicate<? super T> predicate); // 元素是否有任意一個(gè)滿足條件
boolean allMatch(Predicate<? super T> predicate); // 元素是否都滿足條件
boolean noneMatch(Predicate<? super T> predicate); // 元素是否都不滿足條件

使用

    public static void main(String[] args) {boolean b = Stream.of("1", "3", "3", "4", "5", "1", "7").map(Integer::parseInt)//.allMatch(s -> s > 0)//.anyMatch(s -> s >4).noneMatch(s -> s > 4);System.out.println(b);}

注意match是一個(gè)終結(jié)方法

4.10 find

如果我們需要找到某些數(shù)據(jù),可以使用find方法來(lái)實(shí)現(xiàn)

    Optional<T> findFirst();Optional<T> findAny();

在這里插入圖片描述

使用:

    public static void main(String[] args) {Optional<String> first = Stream.of("1", "3", "3", "4", "5", "1", "7").findFirst();System.out.println(first.get());Optional<String> any = Stream.of("1", "3", "3", "4", "5", "1", "7").findAny();System.out.println(any.get());}

4.11 max和min

在這里插入圖片描述

如果我們想要獲取最大值和最小值,那么可以使用max和min方法

Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);

使用

    public static void main(String[] args) {Optional<Integer> max = Stream.of("1", "3", "3", "4", "5", "1", "7").map(Integer::parseInt).max((o1,o2)->o1-o2);System.out.println(max.get());Optional<Integer> min = Stream.of("1", "3", "3", "4", "5", "1", "7").map(Integer::parseInt).min((o1,o2)->o1-o2);System.out.println(min.get());}

4.12 reduce方法

在這里插入圖片描述

如果需要將所有數(shù)據(jù)歸納得到一個(gè)數(shù)據(jù),可以使用reduce方法

T reduce(T identity, BinaryOperator<T> accumulator);

使用:

    public static void main(String[] args) {Integer sum = Stream.of(4, 5, 3, 9)// identity默認(rèn)值// 第一次的時(shí)候會(huì)將默認(rèn)值賦值給x// 之后每次會(huì)將 上一次的操作結(jié)果賦值給x y就是每次從數(shù)據(jù)中獲取的元素.reduce(0, (x, y) -> {System.out.println("x="+x+",y="+y);return x + y;});System.out.println(sum);// 獲取 最大值Integer max = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {return x > y ? x : y;});System.out.println(max);}

4.13 map和reduce的組合

? 在實(shí)際開(kāi)發(fā)中我們經(jīng)常會(huì)將map和reduce一塊來(lái)使用

    public static void main(String[] args) {// 1.求出所有年齡的總和Integer sumAge = Stream.of(new Person("張三", 18), new Person("李四", 22), new Person("張三", 13), new Person("王五", 15), new Person("張三", 19)).map(Person::getAge) // 實(shí)現(xiàn)數(shù)據(jù)類型的轉(zhuǎn)換.reduce(0, Integer::sum);System.out.println(sumAge);// 2.求出所有年齡中的最大值Integer maxAge = Stream.of(new Person("張三", 18), new Person("李四", 22), new Person("張三", 13), new Person("王五", 15), new Person("張三", 19)).map(Person::getAge) // 實(shí)現(xiàn)數(shù)據(jù)類型的轉(zhuǎn)換,符合reduce對(duì)數(shù)據(jù)的要求.reduce(0, Math::max); // reduce實(shí)現(xiàn)數(shù)據(jù)的處理System.out.println(maxAge);// 3.統(tǒng)計(jì) 字符 a 出現(xiàn)的次數(shù)Integer count = Stream.of("a", "b", "c", "d", "a", "c", "a").map(ch -> "a".equals(ch) ? 1 : 0).reduce(0, Integer::sum);System.out.println(count);}

輸出結(jié)果

87
22
3

4.14 mapToInt

如果需要將Stream中的Integer類型轉(zhuǎn)換成int類型,可以使用mapToInt方法來(lái)實(shí)現(xiàn)

在這里插入圖片描述

使用

    public static void main(String[] args) {// Integer占用的內(nèi)存比int多很多,在Stream流操作中會(huì)自動(dòng)裝修和拆箱操作Integer arr[] = {1,2,3,5,6,8};Stream.of(arr).filter(i->i>0).forEach(System.out::println);System.out.println("---------");// 為了提高程序代碼的效率,我們可以先將流中Integer數(shù)據(jù)轉(zhuǎn)換為int數(shù)據(jù),然后再操作IntStream intStream = Stream.of(arr).mapToInt(Integer::intValue);intStream.filter(i->i>3).forEach(System.out::println);}

4.15 concat

? 如果有兩個(gè)流,希望合并成為一個(gè)流,那么可以使用Stream接口的靜態(tài)方法concat

    public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {Objects.requireNonNull(a);Objects.requireNonNull(b);@SuppressWarnings("unchecked")Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>((Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());return stream.onClose(Streams.composedClose(a, b));}

使用:

    public static void main(String[] args) {Stream<String> stream1 = Stream.of("a","b","c");Stream<String> stream2 = Stream.of("x", "y", "z");// 通過(guò)concat方法將兩個(gè)流合并為一個(gè)新的流Stream.concat(stream1,stream2).forEach(System.out::println);}

4.16 綜合案例

定義兩個(gè)集合,然后在集合中存儲(chǔ)多個(gè)用戶名稱。然后完成如下的操作:

  1. 第一個(gè)隊(duì)伍只保留姓名長(zhǎng)度為3的成員
  2. 第一個(gè)隊(duì)伍篩選之后只要前3個(gè)人
  3. 第二個(gè)隊(duì)伍只要姓張的成員
  4. 第二個(gè)隊(duì)伍篩選之后不要前兩個(gè)人
  5. 將兩個(gè)隊(duì)伍合并為一個(gè)隊(duì)伍
  6. 根據(jù)姓名創(chuàng)建Person對(duì)象
  7. 打印整個(gè)隊(duì)伍的Person信息
package com.bobo.jdk.stream;import com.bobo.jdk.lambda.domain.Person;import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;public class StreamTest21Demo {/*** 1. 第一個(gè)隊(duì)伍只保留姓名長(zhǎng)度為3的成員* 2. 第一個(gè)隊(duì)伍篩選之后只要前3個(gè)人* 3. 第二個(gè)隊(duì)伍只要姓張的成員* 4. 第二個(gè)隊(duì)伍篩選之后不要前兩個(gè)人* 5. 將兩個(gè)隊(duì)伍合并為一個(gè)隊(duì)伍* 6. 根據(jù)姓名創(chuàng)建Person對(duì)象* 7. 打印整個(gè)隊(duì)伍的Person信息* @param args*/public static void main(String[] args) {List<String> list1 = Arrays.asList("迪麗熱巴", "宋遠(yuǎn)橋", "蘇星河", "老子", "莊子", "孫子", "洪七 公");List<String> list2 = Arrays.asList("古力娜扎", "張無(wú)忌", "張三豐", "趙麗穎", "張二狗", "張?zhí)鞇?ài)", "張三");// 1. 第一個(gè)隊(duì)伍只保留姓名長(zhǎng)度為3的成員// 2. 第一個(gè)隊(duì)伍篩選之后只要前3個(gè)人Stream<String> stream1 = list1.stream().filter(s ->  s.length() == 3).limit(3);// 3. 第二個(gè)隊(duì)伍只要姓張的成員// 4. 第二個(gè)隊(duì)伍篩選之后不要前兩個(gè)人Stream<String> stream2 = list2.stream().filter(s -> s.startsWith("張")).skip(2);// 5. 將兩個(gè)隊(duì)伍合并為一個(gè)隊(duì)伍// 6. 根據(jù)姓名創(chuàng)建Person對(duì)象// 7. 打印整個(gè)隊(duì)伍的Person信息Stream.concat(stream1,stream2)//.map(n-> new Person(n)).map(Person::new).forEach(System.out::println);}
}

輸出結(jié)果:

Person{name='宋遠(yuǎn)橋', age=null, height=null}
Person{name='蘇星河', age=null, height=null}
Person{name='張二狗', age=null, height=null}
Person{name='張?zhí)鞇?ài)', age=null, height=null}
Person{name='張三', age=null, height=null}

5.Stream結(jié)果收集

5.1 結(jié)果收集到集合中

    /*** Stream結(jié)果收集*    收集到集合中*/@Testpublic void test01(){// Stream<String> stream = Stream.of("aa", "bb", "cc");List<String> list = Stream.of("aa", "bb", "cc","aa").collect(Collectors.toList());System.out.println(list);// 收集到 Set集合中Set<String> set = Stream.of("aa", "bb", "cc", "aa").collect(Collectors.toSet());System.out.println(set);// 如果需要獲取的類型為具體的實(shí)現(xiàn),比如:ArrayList HashSetArrayList<String> arrayList = Stream.of("aa", "bb", "cc", "aa")//.collect(Collectors.toCollection(() -> new ArrayList<>()));.collect(Collectors.toCollection(ArrayList::new));System.out.println(arrayList);HashSet<String> hashSet = Stream.of("aa", "bb", "cc", "aa").collect(Collectors.toCollection(HashSet::new));System.out.println(hashSet);}

輸出:

[aa, bb, cc, aa]
[aa, bb, cc]
[aa, bb, cc, aa]
[aa, bb, cc]

5.2 結(jié)果收集到數(shù)組中

Stream中提供了toArray方法來(lái)將結(jié)果放到一個(gè)數(shù)組中,返回值類型是Object[],如果我們要指定返回的類型,那么可以使用另一個(gè)重載的toArray(IntFunction f)方法

    /*** Stream結(jié)果收集到數(shù)組中*/@Testpublic void test02(){Object[] objects = Stream.of("aa", "bb", "cc", "aa").toArray(); // 返回的數(shù)組中的元素是 Object類型System.out.println(Arrays.toString(objects));// 如果我們需要指定返回的數(shù)組中的元素類型String[] strings = Stream.of("aa", "bb", "cc", "aa").toArray(String[]::new);System.out.println(Arrays.toString(strings));}

5.3 對(duì)流中的數(shù)據(jù)做聚合計(jì)算

? 當(dāng)我們使用Stream流處理數(shù)據(jù)后,可以像數(shù)據(jù)庫(kù)的聚合函數(shù)一樣對(duì)某個(gè)字段進(jìn)行操作,比如獲得最大值,最小值,求和,平均值,統(tǒng)計(jì)數(shù)量。

    /*** Stream流中數(shù)據(jù)的聚合計(jì)算*/@Testpublic void test03(){// 獲取年齡的最大值Optional<Person> maxAge = Stream.of(new Person("張三", 18), new Person("李四", 22), new Person("張三", 13), new Person("王五", 15), new Person("張三", 19)).collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge()));System.out.println("最大年齡:" + maxAge.get());// 獲取年齡的最小值Optional<Person> minAge = Stream.of(new Person("張三", 18), new Person("李四", 22), new Person("張三", 13), new Person("王五", 15), new Person("張三", 19)).collect(Collectors.minBy((p1, p2) -> p1.getAge() - p2.getAge()));System.out.println("最新年齡:" + minAge.get());// 求所有人的年齡之和Integer sumAge = Stream.of(new Person("張三", 18), new Person("李四", 22), new Person("張三", 13), new Person("王五", 15), new Person("張三", 19))//.collect(Collectors.summingInt(s -> s.getAge())).collect(Collectors.summingInt(Person::getAge));System.out.println("年齡總和:" + sumAge);// 年齡的平均值Double avgAge = Stream.of(new Person("張三", 18), new Person("李四", 22), new Person("張三", 13), new Person("王五", 15), new Person("張三", 19)).collect(Collectors.averagingInt(Person::getAge));System.out.println("年齡的平均值:" + avgAge);// 統(tǒng)計(jì)數(shù)量Long count = Stream.of(new Person("張三", 18), new Person("李四", 22), new Person("張三", 13), new Person("王五", 15), new Person("張三", 19)).filter(p->p.getAge() > 18).collect(Collectors.counting());System.out.println("滿足條件的記錄數(shù):" + count);}

5.4 對(duì)流中數(shù)據(jù)做分組操作

? 當(dāng)我們使用Stream流處理數(shù)據(jù)后,可以根據(jù)某個(gè)屬性將數(shù)據(jù)分組

    /*** 分組計(jì)算*/@Testpublic void test04(){// 根據(jù)賬號(hào)對(duì)數(shù)據(jù)進(jìn)行分組Map<String, List<Person>> map1 = Stream.of(new Person("張三", 18, 175), new Person("李四", 22, 177), new Person("張三", 14, 165), new Person("李四", 15, 166), new Person("張三", 19, 182)).collect(Collectors.groupingBy(Person::getName));map1.forEach((k,v)-> System.out.println("k=" + k +"\t"+ "v=" + v));System.out.println("-----------");// 根據(jù)年齡分組 如果大于等于18 成年否則未成年Map<String, List<Person>> map2 = Stream.of(new Person("張三", 18, 175), new Person("李四", 22, 177), new Person("張三", 14, 165), new Person("李四", 15, 166), new Person("張三", 19, 182)).collect(Collectors.groupingBy(p -> p.getAge() >= 18 ? "成年" : "未成年"));map2.forEach((k,v)-> System.out.println("k=" + k +"\t"+ "v=" + v));}

輸出結(jié)果:

k=李四	v=[Person{name='李四', age=22, height=177}, Person{name='李四', age=15, height=166}]
k=張三	v=[Person{name='張三', age=18, height=175}, Person{name='張三', age=14, height=165}, Person{name='張三', age=19, height=182}]
-----------
k=未成年	v=[Person{name='張三', age=14, height=165}, Person{name='李四', age=15, height=166}]
k=成年	v=[Person{name='張三', age=18, height=175}, Person{name='李四', age=22, height=177}, Person{name='張三', age=19, height=182}]

多級(jí)分組: 先根據(jù)name分組然后根據(jù)年齡分組

    /*** 分組計(jì)算--多級(jí)分組*/@Testpublic void test05(){// 先根據(jù)name分組,然后根據(jù)age(成年和未成年)分組Map<String,Map<Object,List<Person>>> map =  Stream.of(new Person("張三", 18, 175), new Person("李四", 22, 177), new Person("張三", 14, 165), new Person("李四", 15, 166), new Person("張三", 19, 182)).collect(Collectors.groupingBy(Person::getName,Collectors.groupingBy(p->p.getAge()>=18?"成年":"未成年")));map.forEach((k,v)->{System.out.println(k);v.forEach((k1,v1)->{System.out.println("\t"+k1 + "=" + v1);});});}

輸出結(jié)果:

李四未成年=[Person{name='李四', age=15, height=166}]成年=[Person{name='李四', age=22, height=177}]
張三未成年=[Person{name='張三', age=14, height=165}]成年=[Person{name='張三', age=18, height=175}, Person{name='張三', age=19, height=182}]

5.5 對(duì)流中的數(shù)據(jù)做分區(qū)操作

Collectors.partitioningBy會(huì)根據(jù)值是否為true,把集合中的數(shù)據(jù)分割為兩個(gè)列表,一個(gè)true列表,一個(gè)false列表

在這里插入圖片描述

    /*** 分區(qū)操作*/@Testpublic void test06(){Map<Boolean, List<Person>> map = Stream.of(new Person("張三", 18, 175), new Person("李四", 22, 177), new Person("張三", 14, 165), new Person("李四", 15, 166), new Person("張三", 19, 182)).collect(Collectors.partitioningBy(p -> p.getAge() > 18));map.forEach((k,v)-> System.out.println(k+"\t" + v));}

輸出結(jié)果:

false	[Person{name='張三', age=18, height=175}, Person{name='張三', age=14, height=165}, Person{name='李四', age=15, height=166}]
true	[Person{name='李四', age=22, height=177}, Person{name='張三', age=19, height=182}]

5.6 對(duì)流中的數(shù)據(jù)做拼接

Collectors.joining會(huì)根據(jù)指定的連接符,將所有的元素連接成一個(gè)字符串

    /*** 對(duì)流中的數(shù)據(jù)做拼接操作*/@Testpublic void test07(){String s1 = Stream.of(new Person("張三", 18, 175), new Person("李四", 22, 177), new Person("張三", 14, 165), new Person("李四", 15, 166), new Person("張三", 19, 182)).map(Person::getName).collect(Collectors.joining());// 張三李四張三李四張三System.out.println(s1);String s2 = Stream.of(new Person("張三", 18, 175), new Person("李四", 22, 177), new Person("張三", 14, 165), new Person("李四", 15, 166), new Person("張三", 19, 182)).map(Person::getName).collect(Collectors.joining("_"));// 張三_李四_張三_李四_張三System.out.println(s2);String s3 = Stream.of(new Person("張三", 18, 175), new Person("李四", 22, 177), new Person("張三", 14, 165), new Person("李四", 15, 166), new Person("張三", 19, 182)).map(Person::getName).collect(Collectors.joining("_", "###", "$$$"));// ###張三_李四_張三_李四_張三$$$System.out.println(s3);}

6. 并行的Stream流

6.1 串行的Stream流

我們前面使用的Stream流都是串行,也就是在一個(gè)線程上面執(zhí)行。

    /*** 串行流*/@Testpublic void test01(){Stream.of(5,6,8,3,1,6).filter(s->{System.out.println(Thread.currentThread() + "" + s);return s > 3;}).count();}

輸出:

Thread[main,5,main]5
Thread[main,5,main]6
Thread[main,5,main]8
Thread[main,5,main]3
Thread[main,5,main]1
Thread[main,5,main]6

6.2 并行流

parallelStream其實(shí)就是一個(gè)并行執(zhí)行的流,它通過(guò)默認(rèn)的ForkJoinPool,可以提高多線程任務(wù)的速度。

6.2.1 獲取并行流

我們可以通過(guò)兩種方式來(lái)獲取并行流。

  1. 通過(guò)List接口中的parallelStream方法來(lái)獲取
  2. 通過(guò)已有的串行流轉(zhuǎn)換為并行流(parallel)

實(shí)現(xiàn):

    /*** 獲取并行流的兩種方式*/@Testpublic void test02(){List<Integer> list = new ArrayList<>();// 通過(guò)List 接口 直接獲取并行流Stream<Integer> integerStream = list.parallelStream();// 將已有的串行流轉(zhuǎn)換為并行流Stream<Integer> parallel = Stream.of(1, 2, 3).parallel();}

6.2.2 并行流操作

    /*** 并行流操作*/@Testpublic void test03(){Stream.of(1,4,2,6,1,5,9).parallel() // 將流轉(zhuǎn)換為并發(fā)流,Stream處理的時(shí)候就會(huì)通過(guò)多線程處理.filter(s->{System.out.println(Thread.currentThread() + " s=" +s);return s > 2;}).count();}

效果

Thread[main,5,main] s=1
Thread[ForkJoinPool.commonPool-worker-2,5,main] s=9
Thread[ForkJoinPool.commonPool-worker-6,5,main] s=6
Thread[ForkJoinPool.commonPool-worker-13,5,main] s=2
Thread[ForkJoinPool.commonPool-worker-9,5,main] s=4
Thread[ForkJoinPool.commonPool-worker-4,5,main] s=5
Thread[ForkJoinPool.commonPool-worker-11,5,main] s=1

6.3 并行流和串行流對(duì)比

我們通過(guò)for循環(huán),串行Stream流,并行Stream流來(lái)對(duì)500000000億個(gè)數(shù)字求和。來(lái)看消耗時(shí)間

package com.bobo.jdk.res;import org.junit.After;
import org.junit.Before;
import org.junit.Test;import java.util.stream.LongStream;public class Test03 {private static long times = 500000000;private  long start;@Beforepublic void befor(){start = System.currentTimeMillis();}@Afterpublic void end(){long end = System.currentTimeMillis();System.out.println("消耗時(shí)間:" + (end - start));}/*** 普通for循環(huán) 消耗時(shí)間:138*/@Testpublic void test01(){System.out.println("普通for循環(huán):");long res = 0;for (int i = 0; i < times; i++) {res += i;}}/*** 串行流處理*   消耗時(shí)間:203*/@Testpublic void test02(){System.out.println("串行流:serialStream");LongStream.rangeClosed(0,times).reduce(0,Long::sum);}/*** 并行流處理 消耗時(shí)間:84*/@Testpublic void test03(){LongStream.rangeClosed(0,times).parallel().reduce(0,Long::sum);}
}

通過(guò)案例我們可以看到parallelStream的效率是最高的。

Stream并行處理的過(guò)程會(huì)分而治之,也就是將一個(gè)大的任務(wù)切分成了多個(gè)小任務(wù),這表示每個(gè)任務(wù)都是一個(gè)線程操作。

6.4 線程安全問(wèn)題

在多線程的處理下,肯定會(huì)出現(xiàn)數(shù)據(jù)安全問(wèn)題。如下:

    @Testpublic void test01(){List<Integer> list = new ArrayList<>();for (int i = 0; i < 1000; i++) {list.add(i);}System.out.println(list.size());List<Integer> listNew = new ArrayList<>();// 使用并行流來(lái)向集合中添加數(shù)據(jù)list.parallelStream()//.forEach(s->listNew.add(s));.forEach(listNew::add);System.out.println(listNew.size());}

運(yùn)行效果:

839

或者直接拋異常

java.lang.ArrayIndexOutOfBoundsExceptionat sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)at java.lang.reflect.Constructor.newInstance(Constructor.java:423)at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:598)
....
Caused by: java.lang.ArrayIndexOutOfBoundsException: 366at java.util.ArrayList.add(ArrayList.java:463)

針對(duì)這個(gè)問(wèn)題,我們的解決方案有哪些呢?

  1. 加同步鎖
  2. 使用線程安全的容器
  3. 通過(guò)Stream中的toArray/collect操作

實(shí)現(xiàn):

    /*** 加同步鎖*/@Testpublic void test02(){List<Integer> listNew = new ArrayList<>();Object obj = new Object();IntStream.rangeClosed(1,1000).parallel().forEach(i->{synchronized (obj){listNew.add(i);}});System.out.println(listNew.size());}/*** 使用線程安全的容器*/@Testpublic void test03(){Vector v = new Vector();Object obj = new Object();IntStream.rangeClosed(1,1000).parallel().forEach(i->{synchronized (obj){v.add(i);}});System.out.println(v.size());}/*** 將線程不安全的容器轉(zhuǎn)換為線程安全的容器*/@Testpublic void test04(){List<Integer> listNew = new ArrayList<>();// 將線程不安全的容器包裝為線程安全的容器List<Integer> synchronizedList = Collections.synchronizedList(listNew);Object obj = new Object();IntStream.rangeClosed(1,1000).parallel().forEach(i->{synchronizedList.add(i);});System.out.println(synchronizedList.size());}/*** 我們還可以通過(guò)Stream中的 toArray方法或者 collect方法來(lái)操作* 就是滿足線程安全的要求*/@Testpublic void test05(){List<Integer> listNew = new ArrayList<>();Object obj = new Object();List<Integer> list = IntStream.rangeClosed(1, 1000).parallel().boxed().collect(Collectors.toList());System.out.println(list.size());}

七、Optional類

這個(gè)Optional類注意是解決空指針的問(wèn)題

1. 以前對(duì)null 的處理

    @Testpublic void test01(){//String userName = "張三";String userName = null;if(userName != null){System.out.println("字符串的長(zhǎng)度:" + userName.length());}else{System.out.println("字符串為空");}}

2. Optional類

Optional是一個(gè)沒(méi)有子類的工具類,Optional是一個(gè)可以為null的容器對(duì)象,它的主要作用就是為了避免Null檢查,防止NullpointerException,

在這里插入圖片描述

3. Optional的基本使用

Optional對(duì)象的創(chuàng)建方式

    /*** Optional對(duì)象的創(chuàng)建方式*/@Testpublic void test02(){// 第一種方式 通過(guò)of方法  of方法是不支持null的Optional<String> op1 = Optional.of("zhangsan");//Optional<Object> op2 = Optional.of(null);// 第二種方式通過(guò) ofNullable方法 支持nullOptional<String> op3 = Optional.ofNullable("lisi");Optional<Object> op4 = Optional.ofNullable(null);// 第三種方式 通過(guò)empty方法直接創(chuàng)建一個(gè)空的Optional對(duì)象Optional<Object> op5 = Optional.empty();}

4. Optional的常用方法

/*** Optional中的常用方法介紹*   get(): 如果Optional有值則返回,否則拋出NoSuchElementException異常*          get()通常和isPresent方法一塊使用*   isPresent():判斷是否包含值,包含值返回true,不包含值返回false*   orElse(T t):如果調(diào)用對(duì)象包含值,就返回該值,否則返回t*   orElseGet(Supplier s):如果調(diào)用對(duì)象包含值,就返回該值,否則返回 Lambda表達(dá)式的返回值*/@Testpublic void test03(){Optional<String> op1 = Optional.of("zhangsan");Optional<String> op2 = Optional.empty();// 獲取Optional中的值if(op1.isPresent()){String s1 = op1.get();System.out.println("用戶名稱:" +s1);}if(op2.isPresent()){System.out.println(op2.get());}else{System.out.println("op2是一個(gè)空Optional對(duì)象");}String s3 = op1.orElse("李四");System.out.println(s3);String s4 = op2.orElse("王五");System.out.println(s4);String s5 = op2.orElseGet(()->{return "Hello";});System.out.println(s5);}
    @Testpublic void test04(){Optional<String> op1 = Optional.of("zhangsan");Optional<String> op2 = Optional.empty();// 如果存在值 就做什么op1.ifPresent(s-> System.out.println("有值:" +s));op1.ifPresent(System.out::println);}/*** 自定義一個(gè)方法,將Person對(duì)象中的 name 轉(zhuǎn)換為大寫(xiě) 并返回*/@Testpublic void test05(){Person p = new Person("zhangsan",18);Optional<Person> op = Optional.of(p);String name = getNameForOptional(op);System.out.println("name="+name);}/*** 根據(jù)Person對(duì)象 將name轉(zhuǎn)換為大寫(xiě)并返回*    通過(guò)Optional方式實(shí)現(xiàn)* @param op* @return*/public String getNameForOptional(Optional<Person> op){if(op.isPresent()){String msg = //op.map(p -> p.getName())op.map(Person::getName)//.map(p -> p.toUpperCase()).map(String::toUpperCase).orElse("空值");return msg;}return null;}/*** 根據(jù)Person對(duì)象 將name轉(zhuǎn)換為大寫(xiě)并返回* @param person* @return*/public String getName(Person person){if(person != null){String name = person.getName();if(name != null){return name.toUpperCase();}else{return null;}}else{return null;}}

八、新時(shí)間日期API

1.舊版日期時(shí)間的問(wèn)題

? 在舊版本中JDK對(duì)于日期和時(shí)間這塊的時(shí)間是非常差的。

    /*** 舊版日期時(shí)間設(shè)計(jì)的問(wèn)題*/@Testpublic void test01() throws Exception{// 1.設(shè)計(jì)不合理Date date = new Date(2021,05,05);System.out.println(date);// 2.時(shí)間格式化和解析操作是線程不安全的SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");for (int i = 0; i < 50; i++) {new Thread(()->{// System.out.println(sdf.format(date));try {System.out.println(sdf.parse("2021-05-06"));} catch (ParseException e) {e.printStackTrace();}}).start();}}
  1. 設(shè)計(jì)不合理,在java.util和java.sql的包中都有日期類,java.util.Date同時(shí)包含日期和時(shí)間的,而java.sql.Date僅僅包含日期,此外用于格式化和解析的類在java.text包下。
  2. 非線程安全,java.util.Date是非線程安全的,所有的日期類都是可變的,這是java日期類最大的問(wèn)題之一。
  3. 時(shí)區(qū)處理麻煩,日期類并不提供國(guó)際化,沒(méi)有時(shí)區(qū)支持。

2. 新日期時(shí)間API介紹

JDK 8中增加了一套全新的日期時(shí)間API,這套API設(shè)計(jì)合理,是線程安全的。新的日期及時(shí)間API位于 java.time 包
中,下面是一些關(guān)鍵類。

  • LocalDate :表示日期,包含年月日,格式為 2019-10-16
  • LocalTime :表示時(shí)間,包含時(shí)分秒,格式為 16:38:54.158549300
  • LocalDateTime :表示日期時(shí)間,包含年月日,時(shí)分秒,格式為 2018-09-06T15:33:56.750
  • DateTimeFormatter :日期時(shí)間格式化類。
  • Instant:時(shí)間戳,表示一個(gè)特定的時(shí)間瞬間。
  • Duration:用于計(jì)算2個(gè)時(shí)間(LocalTime,時(shí)分秒)的距離
  • Period:用于計(jì)算2個(gè)日期(LocalDate,年月日)的距離
  • ZonedDateTime :包含時(shí)區(qū)的時(shí)間

Java中使用的歷法是ISO 8601日歷系統(tǒng),它是世界民用歷法,也就是我們所說(shuō)的公歷。平年有365天,閏年是366
天。此外Java 8還提供了4套其他歷法,分別是:

  • ThaiBuddhistDate:泰國(guó)佛教歷
  • MinguoDate:中華民國(guó)歷
  • JapaneseDate:日本歷
  • HijrahDate:伊斯蘭歷

2.1 日期時(shí)間的常見(jiàn)操作

? LocalDate,LocalTime以及LocalDateTime的操作。

    /*** JDK8 日期時(shí)間操作*/@Testpublic void test01(){// 1.創(chuàng)建指定的日期LocalDate date1 = LocalDate.of(2021, 05, 06);System.out.println("date1 = "+date1);// 2.得到當(dāng)前的日期LocalDate now = LocalDate.now();System.out.println("now = "+now);// 3.根據(jù)LocalDate對(duì)象獲取對(duì)應(yīng)的日期信息System.out.println("年:" + now.getYear());System.out.println("月:" + now.getMonth().getValue());System.out.println("日:" + now.getDayOfMonth());System.out.println("星期:" + now.getDayOfWeek().getValue());}/*** 時(shí)間操作*/@Testpublic void test02(){// 1.得到指定的時(shí)間LocalTime time = LocalTime.of(5,26,33,23145);System.out.println(time);// 2.獲取當(dāng)前的時(shí)間LocalTime now = LocalTime.now();System.out.println(now);// 3.獲取時(shí)間信息System.out.println(now.getHour());System.out.println(now.getMinute());System.out.println(now.getSecond());System.out.println(now.getNano());}/*** 日期時(shí)間類型  LocalDateTime*/@Testpublic void test03(){// 獲取指定的日期時(shí)間LocalDateTime dateTime =LocalDateTime.of(2020, 06, 01, 12, 12, 33, 213);System.out.println(dateTime);// 獲取當(dāng)前的日期時(shí)間LocalDateTime now = LocalDateTime.now();System.out.println(now);// 獲取日期時(shí)間信息System.out.println(now.getYear());System.out.println(now.getMonth().getValue());System.out.println(now.getDayOfMonth());System.out.println(now.getDayOfWeek().getValue());System.out.println(now.getHour());System.out.println(now.getMinute());System.out.println(now.getSecond());System.out.println(now.getNano());}

2.2 日期時(shí)間的修改和比較

    /*** 日期時(shí)間的修改*/@Testpublic void test01(){LocalDateTime now = LocalDateTime.now();System.out.println("now = "+now);// 修改日期時(shí)間  對(duì)日期時(shí)間的修改,對(duì)已存在的LocalDate對(duì)象,創(chuàng)建了它模板// 并不會(huì)修改原來(lái)的信息LocalDateTime localDateTime = now.withYear(1998);System.out.println("now :"+now);System.out.println("修改后的:" + localDateTime);System.out.println("月份:" + now.withMonth(10));System.out.println("天:" + now.withDayOfMonth(6));System.out.println("小時(shí):" + now.withHour(8));System.out.println("分鐘:" + now.withMinute(15));// 在當(dāng)前日期時(shí)間的基礎(chǔ)上 加上或者減去指定的時(shí)間System.out.println("兩天后:" + now.plusDays(2));System.out.println("10年后:"+now.plusYears(10));System.out.println("6個(gè)月后 = " + now.plusMonths(6));System.out.println("10年前 = " + now.minusYears(10));System.out.println("半年前 = " + now.minusMonths(6));System.out.println("一周前 = " + now.minusDays(7));}/*** 日期時(shí)間的比較*/@Testpublic void test02(){LocalDate now = LocalDate.now();LocalDate date = LocalDate.of(2020, 1, 3);// 在JDK8中要實(shí)現(xiàn) 日期的比較 isAfter  isBefore isEqual 通過(guò)這幾個(gè)方法來(lái)直接比較System.out.println(now.isAfter(date)); // trueSystem.out.println(now.isBefore(date)); // falseSystem.out.println(now.isEqual(date)); // false}

注意:在進(jìn)行日期時(shí)間修改的時(shí)候,原來(lái)的LocalDate對(duì)象是不會(huì)被修改,每次操作都是返回了一個(gè)新的LocalDate對(duì)象,所以在多線程場(chǎng)景下是數(shù)據(jù)安全的。

2.3 格式化和解析操作

在JDK8中我們可以通過(guò)java.time.format.DateTimeFormatter類可以進(jìn)行日期的解析和格式化操作

    /*** 日期格式化*/@Testpublic void test01(){LocalDateTime now = LocalDateTime.now();// 指定格式  使用系統(tǒng)默認(rèn)的格式 2021-05-27T16:16:38.139DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;// 將日期時(shí)間轉(zhuǎn)換為字符串String format = now.format(isoLocalDateTime);System.out.println("format = " + format);// 通過(guò) ofPattern 方法來(lái)指定特定的格式DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");String format1 = now.format(dateTimeFormatter);// 2021-05-27 16:16:38System.out.println("format1 = " + format1);// 將字符串解析為一個(gè) 日期時(shí)間類型LocalDateTime parse = LocalDateTime.parse("1997-05-06 22:45:16", dateTimeFormatter);// parse = 1997-05-06T22:45:16System.out.println("parse = " + parse);}

2.4 Instant類

在JDK8中給我們新增一個(gè)Instant類(時(shí)間戳/時(shí)間線),內(nèi)部保存了從1970年1月1日 00:00:00以來(lái)的秒和納秒

    /*** Instant 時(shí)間戳*    可以用來(lái)統(tǒng)計(jì)時(shí)間消耗*/@Testpublic void test01() throws Exception{Instant now = Instant.now();System.out.println("now = " + now);// 獲取從1970年一月一日 00:00:00 到現(xiàn)在的 納秒System.out.println(now.getNano());Thread.sleep(5);Instant now1 = Instant.now();System.out.println("耗時(shí):" + (now1.getNano() - now.getNano()));}

2.5 計(jì)算日期時(shí)間差

JDK8中提供了兩個(gè)工具類Duration/Period:計(jì)算日期時(shí)間差

  1. Duration:用來(lái)計(jì)算兩個(gè)時(shí)間差(LocalTime)
  2. Period:用來(lái)計(jì)算兩個(gè)日期差(LocalDate)
    /*** 計(jì)算日期時(shí)間差*/@Testpublic void test01(){// 計(jì)算時(shí)間差LocalTime now = LocalTime.now();LocalTime time = LocalTime.of(22, 48, 59);System.out.println("now = " + now);// 通過(guò)Duration來(lái)計(jì)算時(shí)間差Duration duration = Duration.between(now, time);System.out.println(duration.toDays()); // 0System.out.println(duration.toHours()); // 6System.out.println(duration.toMinutes()); // 368System.out.println(duration.toMillis()); // 22124240// 計(jì)算日期差LocalDate nowDate = LocalDate.now();LocalDate date = LocalDate.of(1997, 12, 5);Period period = Period.between(date, nowDate);System.out.println(period.getYears()); // 23System.out.println(period.getMonths()); // 5System.out.println(period.getDays()); // 22}

2.6 時(shí)間校正器

有時(shí)候我們可以需要如下調(diào)整:將日期調(diào)整到"下個(gè)月的第一天"等操作。這時(shí)我們通過(guò)時(shí)間校正器效果可能會(huì)更好。

  • TemporalAdjuster:時(shí)間校正器
  • TemporalAdjusters:通過(guò)該類靜態(tài)方法提供了大量的常用TemporalAdjuster的實(shí)現(xiàn)。
    /*** 時(shí)間校正器*/@Testpublic void test02(){LocalDateTime now = LocalDateTime.now();// 將當(dāng)前的日期調(diào)整到下個(gè)月的一號(hào)TemporalAdjuster adJuster = (temporal)->{LocalDateTime dateTime = (LocalDateTime) temporal;LocalDateTime nextMonth = dateTime.plusMonths(1).withDayOfMonth(1);System.out.println("nextMonth = " + nextMonth);return nextMonth;};// 我們可以通過(guò)TemporalAdjusters 來(lái)實(shí)現(xiàn)// LocalDateTime nextMonth = now.with(adJuster);LocalDateTime nextMonth = now.with(TemporalAdjusters.firstDayOfNextMonth());System.out.println("nextMonth = " + nextMonth);}

2.7 日期時(shí)間的時(shí)區(qū)

? Java8 中加入了對(duì)時(shí)區(qū)的支持,LocalDate、LocalTime、LocalDateTime是不帶時(shí)區(qū)的,帶時(shí)區(qū)的日期時(shí)間類分別為:ZonedDate、ZonedTime、ZonedDateTime。
其中每個(gè)時(shí)區(qū)都對(duì)應(yīng)著 ID,ID的格式為 “區(qū)域/城市” 。例如 :Asia/Shanghai 等。
ZoneId:該類中包含了所有的時(shí)區(qū)信息

    /*** 時(shí)區(qū)操作*/@Testpublic void test01(){// 1.獲取所有的時(shí)區(qū)id// ZoneId.getAvailableZoneIds().forEach(System.out::println);// 獲取當(dāng)前時(shí)間 中國(guó)使用的 東八區(qū)的時(shí)區(qū),比標(biāo)準(zhǔn)時(shí)間早8個(gè)小時(shí)LocalDateTime now = LocalDateTime.now();System.out.println("now = " + now); // 2021-05-27T17:17:06.951// 獲取標(biāo)準(zhǔn)時(shí)間ZonedDateTime bz = ZonedDateTime.now(Clock.systemUTC());System.out.println("bz = " + bz); // 2021-05-27T09:17:06.952Z// 使用計(jì)算機(jī)默認(rèn)的時(shí)區(qū),創(chuàng)建日期時(shí)間ZonedDateTime now1 = ZonedDateTime.now();System.out.println("now1 = " + now1); //2021-05-27T17:17:06.952+08:00[Asia/Shanghai]// 使用指定的時(shí)區(qū)創(chuàng)建日期時(shí)間ZonedDateTime now2 = ZonedDateTime.now(ZoneId.of("America/Marigot"));System.out.println("now2 = " + now2);}

JDK新的日期和時(shí)間API的優(yōu)勢(shì):

  1. 新版日期時(shí)間API中,日期和時(shí)間對(duì)象是不可變,操作日期不會(huì)影響原來(lái)的值,而是生成一個(gè)新的實(shí)例
  2. 提供不同的兩種方式,有效的區(qū)分了人和機(jī)器的操作
  3. TemporalAdjuster可以更精確的操作日期,還可以自定義日期調(diào)整期
  4. 線程安全

九、其他新特性

1.重復(fù)注解

? 自從Java 5中引入 注解 以來(lái),注解開(kāi)始變得非常流行,并在各個(gè)框架和項(xiàng)目中被廣泛使用。不過(guò)注解有一個(gè)很大的限

制是:在同一個(gè)地方不能多次使用同一個(gè)注解。JDK 8引入了重復(fù)注解的概念,允許在同一個(gè)地方多次使用同一個(gè)注

解。在JDK 8中使用**@Repeatable**注解定義重復(fù)注解。

1.1 定義一個(gè)重復(fù)注解的容器

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {MyAnnotation[] value();
}

1.2 定義一個(gè)可以重復(fù)的注解

@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {String value();
}

1.3 配置多個(gè)重復(fù)的注解

@MyAnnotation("test1")
@MyAnnotation("test2")
@MyAnnotation("test3")
public class AnnoTest01 {@MyAnnotation("fun1")@MyAnnotation("fun2")public void test01(){}}

1.4 解析得到指定的注解

    /*** 解析重復(fù)注解* @param args*/public static void main(String[] args) throws NoSuchMethodException {// 獲取類中標(biāo)注的重復(fù)注解MyAnnotation[] annotationsByType = AnnoTest01.class.getAnnotationsByType(MyAnnotation.class);for (MyAnnotation myAnnotation : annotationsByType) {System.out.println(myAnnotation.value());}// 獲取方法上標(biāo)注的重復(fù)注解MyAnnotation[] test01s = AnnoTest01.class.getMethod("test01").getAnnotationsByType(MyAnnotation.class);for (MyAnnotation test01 : test01s) {System.out.println(test01.value());}}

2.類型注解

JDK 8為@Target元注解新增了兩種類型: TYPE_PARAMETER , TYPE_USE 。

  • TYPE_PARAMETER :表示該注解能寫(xiě)在類型參數(shù)的聲明語(yǔ)句中。 類型參數(shù)聲明如: 、
  • TYPE_USE :表示注解可以再任何用到類型的地方使用。

TYPE_PARAMETER

@Target(ElementType.TYPE_PARAMETER)
public @interface TypeParam {
}

使用:

public class TypeDemo01 <@TypeParam T> {public <@TypeParam K extends Object> K test01(){return null;}
}

TYPE_USE

@Target(ElementType.TYPE_USE)
public @interface NotNull {
}

使用

public class TypeUseDemo01 {public @NotNull Integer age = 10;public Integer sum(@NotNull Integer a,@NotNull Integer b){return a + b;}
}
http://www.risenshineclean.com/news/26869.html

相關(guān)文章:

  • 給手機(jī)做網(wǎng)站的公司優(yōu)化設(shè)計(jì)答案五年級(jí)上冊(cè)
  • ps 做網(wǎng)站切圖線上營(yíng)銷活動(dòng)主要有哪些
  • 做網(wǎng)站版頭圖片網(wǎng)頁(yè)設(shè)計(jì)與制作步驟
  • 免費(fèi)做外貿(mào)的網(wǎng)站平臺(tái)國(guó)內(nèi)最新新聞事件
  • 搜索網(wǎng)站不顯示圖片品牌整合營(yíng)銷推廣
  • 建設(shè)維護(hù)網(wǎng)站未簽訂合同網(wǎng)絡(luò)軟文寫(xiě)作
  • 群暉nas做網(wǎng)站怎么可以讓百度快速收錄視頻
  • 在web服務(wù)器做網(wǎng)站高端網(wǎng)站公司
  • 網(wǎng)站建設(shè)推廣優(yōu)化有哪些基本方法seo課程多少錢
  • 濰坊網(wǎng)站建設(shè)哪家好高端定制網(wǎng)站建設(shè)
  • 全企網(wǎng)建站怎么樣網(wǎng)站流量數(shù)據(jù)
  • 石家莊哪個(gè)公司做網(wǎng)站好百度競(jìng)價(jià)代運(yùn)營(yíng)
  • 開(kāi)發(fā)一個(gè)網(wǎng)站需要多少時(shí)間代寫(xiě)文章接單平臺(tái)
  • 珠海自適應(yīng)網(wǎng)站設(shè)計(jì)做網(wǎng)站哪家好
  • 個(gè)人買域名有什么用徐州seo培訓(xùn)
  • 做網(wǎng)站需要多大的內(nèi)存品牌推廣公司
  • 學(xué)生做網(wǎng)站賺錢網(wǎng)站推廣的常用方法有哪些?
  • 厚街鎮(zhèn)網(wǎng)站仿做seo關(guān)鍵詞排名優(yōu)化制作
  • 泉州網(wǎng)站建設(shè)推廣賺錢的軟件排行
  • 名詞解釋 網(wǎng)站內(nèi)容軟文推廣文章案例
  • 國(guó)內(nèi)vps做網(wǎng)站要備案嗎太原seo排名公司
  • 淘寶網(wǎng)站的論壇做的怎么樣寧波seo托管公司
  • 國(guó)家企業(yè)信息公示官網(wǎng)入口seo推廣系統(tǒng)排名榜
  • 上海城鄉(xiāng)住房建設(shè)部網(wǎng)站首頁(yè)近期的重大新聞
  • 網(wǎng)站建設(shè)有利于優(yōu)化seo教程技術(shù)
  • 鹽城公司做網(wǎng)站自己建站的網(wǎng)站
  • 泗水做網(wǎng)站普通話的順口溜6句
  • 做網(wǎng)站開(kāi)發(fā)需要的英語(yǔ)水平推廣賺錢軟件
  • 蘭州網(wǎng)站建站搜索引擎優(yōu)化論文3000字
  • 軟件網(wǎng)站開(kāi)發(fā)實(shí)訓(xùn)報(bào)告東莞網(wǎng)站排名提升