Java(老白再次入門) - Java8的其它新特性

本系列文章均爲尚硅谷資源!如有侵權,我將立即刪除!

Java基礎知識圖解

在這裏插入圖片描述

Java 8新特性簡介

Java 8 (又稱爲 jdk 1.8) 是 Java 語言開發的一個主要版本。Java 8 是oracle公司於2014年3月發佈,可以看成是自Java 5 以來最具革命性的版本。Java 8爲Java語言、編譯器、類庫、開發工具與JVM帶來了大量新特性。
在這裏插入圖片描述

  • 速度更快
  • 代碼更少(增加了新的語法:Lambda 表達式)  強大的 Stream API
  • 便於並行
  • 最大化減少空指針異常:Optional
  • Nashorn引擎,允許在JVM上運行JS應用
並行流與串行流

並行流就是把一個內容分成多個數據塊,並用不同的線程分別處理每個數據塊的流。相比較串行的流,並行的流可以很大程度上提高程序的執行效率。

Java 8 中將並行進行了優化,我們可以很容易的對數據進行並行操作。Stream API 可以聲明性地通過 parallel() 與 sequential() 在並行流與順序流之間進行切換。

1.Lambda表達式

爲什麼使用 Lambda 表達式

Lambda 是一個匿名函數,我們可以把 Lambda 表達式理解爲是一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)。使用它可以寫出更簡潔、更靈活的代碼。作爲一種更緊湊的代碼風格,使Java的語言表達能力得到了提升。

從匿名類到 Lambda 的轉換舉例1
在這裏插入圖片描述
從匿名類到 Lambda 的轉換舉例2
在這裏插入圖片描述

Lambda 表達式:在Java 8 語言中引入的一種新的語法元素和操作符。這個操作符爲 “->” , 該操作符被稱爲 Lambda 操作符箭頭操作符。它將 Lambda 分爲兩個部分:

左側:指定了 Lambda 表達式需要的參數列表
右側:指定了 Lambda 體,是抽象方法的實現邏輯,也即Lambda 表達式要執行的功能。

語法

在這裏插入圖片描述

在這裏插入圖片描述

類型推斷

上述 Lambda 表達式中的參數類型都是由編譯器推斷得出的。Lambda 表達式中無需指定類型,程序依然可以編譯,這是因爲 javac 根據程序的上下文,在後臺推斷出了參數的類型。Lambda 表達式的類型依賴於上下文環境,是由編譯器推斷出來的。這就是所謂的“類型推斷”。

在這裏插入圖片描述

2.函數式(Functional)接口

什麼是函數式(Functional)接口
  • 只包含一個抽象方法的接口,稱爲函數式接口。
  • 你可以通過 Lambda 表達式來創建該接口的對象。(若 Lambda 表達式拋出一個受檢異常(即:非運行時異常),那麼該異常需要在目標接口的抽象方法上進行聲明)。
  • 我們可以在一個接口上使用 @FunctionalInterface 註解,這樣做可以檢查它是否是一個函數式接口。同時 javadoc 也會包含一條聲明,說明這個接口是一個函數式接口。
  • 在java.util.function包下定義了Java 8 的豐富的函數式接口
如何理解函數式接口
  • Java從誕生日起就是一直倡導“一切皆對象”,在Java裏面面向對象**(OOP)編程是一切。但是隨着python、scala等語言的興起和新技術的挑戰,Java不得不做出調整以便支持更加廣泛的技術要求,也即java不但可以支持OOP還可以支持OOF(面向函數編程)**
  • 在函數式編程語言當中,函數被當做一等公民對待。在將函數作爲一等公民的編程語言中,Lambda表達式的類型是函數。但是在Java8中,有所不同。在Java8中,Lambda表達式是對象,而不是函數,它們必須依附於一類特別的對象類型——函數式接口
  • 簡單的說,在Java8中,Lambda表達式就是一個函數式接口的實例。這就是Lambda表達式和函數式接口的關係。也就是說,只要一個對象是函數式接口的實例,那麼該對象就可以用Lambda表達式來表示。
  • 所以以前用匿名實現類表示的現在都可以用Lambda表達式來寫。
函數式接口舉例

在這裏插入圖片描述

自定義函數式接口

在這裏插入圖片描述

作爲參數傳遞 Lambda 表達式

在這裏插入圖片描述
作爲參數傳遞 Lambda 表達式:爲了將 Lambda 表達式作爲參數傳遞,接收Lambda 表達式的參數類型必須是與該 Lambda 表達式兼容的函數式接口的類型。


Java 內置四大核心函數式接口
函數式接口 參數類型 返回類型 用途
Consumer消費型接口 T void 對類型爲T的對象應用操作,包含方法:void accept(T t)
Supplier供給型接口 T 返回類型爲T的對象,包含方法:T get()
Function<T, R>函數型接口 T R 對類型爲T的對象應用操作,並返回結果。結果是R類型的對象。包含方法:R apply(T t)
Predicate斷定型接口 T boolean 確定類型爲T的對象是否滿足某約束,並返回boolean 值。包含方法:boolean test(T t)
其他接口
函數式接口 參數類型 返回類型 用途
BiFunction<T, U, R> T, U R 對類型爲 T, U 參數應用操作,返回 R 類型的結果。包含方法爲: R apply(T t, U u);
UnaryOperator(Function子接口) T T 對類型爲T的對象進行一元運算,並返回T類型的結果。包含方法爲:T apply(T t);
BinaryOperator(BiFunction 子接口) T, T T 對類型爲T的對象進行二元運算,並返回T類型的結果。包含方法爲: T apply(T t1, T t2);
BiConsumer<T, U> T, U void 對類型爲T, U 參數應用操作。包含方法爲: void accept(T t, U u)
BiPredicate<T,U> T,U boolean 包含方法爲: boolean test(T t,U u)
ToIntFunction ToLongFunction ToDoubleFunction T int long double 分別計算int、long、double值的函數
IntFunction LongFunction DoubleFunction int long double R 參數分別爲int、long、double 類型的函數

3.方法引用與構造器引用

方法引用(Method References)

  • 當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!
  • 方法引用可以看做是Lambda表達式深層次的表達。換句話說,方法引用就是Lambda表達式,也就是函數式接口的一個實例,通過方法的名字來指向一個方法,可以認爲是Lambda表達式的一個語法糖。
  • 要求:實現接口的抽象方法的參數列表和返回值類型,必須與方法引用的方法的參數列表和返回值類型保持一致!
  • 格式:使用操作符 “::” 將類(或對象) 與 方法名分隔開來。
  • 如下三種主要使用情況:
    對象::實例方法名
    類::靜態方法名
    類::實例方法名
方法引用

在這裏插入圖片描述
在這裏插入圖片描述

注意:當函數式接口方法的第一個參數是需要引用方法的調用者,並且第二個參數是需要引用方法的參數(或無參數)時:ClassName::methodName

構造器引用

格式: ClassName::new
與函數式接口相結合,自動與函數式接口中方法兼容。
可以把構造器引用賦值給定義的方法,要求構造器參數列表要與接口中抽象方法的參數列表一致!且方法的返回值即爲構造器對應類的對象。
在這裏插入圖片描述

數組引用

格式: type[] :: new
在這裏插入圖片描述

4.強大的Stream API

Stream API說明
  • Java8中有兩大最爲重要的改變。第一個是 Lambda 表達式;另外一個則 是 Stream API
  • Stream API ( java.util.stream) 把真正的函數式編程風格引入到Java中。這 是目前爲止對Java類庫最好的補充,因爲Stream API可以極大提供Java程 序員的生產力,讓程序員寫出高效率、乾淨、簡潔的代碼。
  • StreamJava8中處理集合的關鍵抽象概念,它可以指定你希望對集合進 行的操作,可以執行非常複雜的查找、過濾和映射數據等操作。 使用 Stream API 對集合數據進行操作,就類似於使用 SQL 執行的數據庫查詢。 也可以使用 Stream API 來並行執行操作。簡言之,Stream API 提供了一種 高效且易於使用的處理數據的方式。
爲什麼要使用Stream API
  • 實際開發中,項目中多數數據源都來自於Mysql,Oracle等。但現在數據源可以更多了,有MongDB,Radis等,而這些NoSQL的數據就需要Java層面去處理。
  • StreamCollection 集合的區別:Collection 是一種靜態的內存數據結構,而 Stream 是有關計算的。前者是主要面向內存,存儲在內存中,後者主要是面向 CPU,通過 CPU 實現計算。
什麼是 Stream

Stream到底是什麼呢?
是數據渠道,用於操作數據源(集合、數組等)所生成的元素序列。
“集合講的是數據,Stream講的是計算!”

注意:
Stream 自己不會存儲元素。
Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream。
Stream 操作是延遲執行的。這意味着他們會等到需要結果的時候才執行。

Stream 的操作三個步驟

1- 創建 Stream
一個數據源(如:集合、數組),獲取一個流
2- 中間操作
一箇中間操作鏈,對數據源的數據進行處理
3- 終止操作(終端操作)
旦執行終止操作,就執行中間操作鏈,併產生結果。之後,不會再被使用

在這裏插入圖片描述

創建 Stream方式一:通過集合

Java8 中的 Collection 接口被擴展,提供了兩個獲取流的方法:

  • default Stream stream() : 返回一個順序流
  • default Stream parallelStream() : 返回一個並行流
創建 Stream方式二:通過數組

Java8 中的 Arrays 的靜態方法 stream() 可以獲取數組流:

  • static Stream stream(T[] array): 返回一個流

重載形式,能夠處理對應基本類型的數組:

  • public static IntStream stream(int[] array)
  • public static LongStream stream(long[] array)
  • public static DoubleStream stream(double[] array)
創建 Stream方式三:通過Stream的of()

可以調用Stream類靜態方法 of(), 通過顯示值創建一個流。它可以接收任意數量的參數。

  • public static Stream of(T… values) : 返回一個流
創建 Stream方式四:創建無限流

可以使用靜態方法 Stream.iterate() 和 Stream.generate(), 創建無限流。

  • 迭代
    public static Stream iterate(final T seed, final UnaryOperator f)
  • 生成
    public static Stream generate(Supplier s)
Stream 的中間操作

多個中間操作可以連接起來形成一個流水線,除非流水線上觸發終止操作,否則中間操作不會執行任何的處理!而在終止操作時一次性全部處理,稱爲“惰性求值”。
1-篩選與切片

方 法 描 述
filter(Predicate p) 接收 Lambda , 從流中排除某些元素
distinct() 篩選,通過流所生成元素的 hashCode() 和 equals() 去除重複元素
limit(long maxSize) 截斷流,使其元素不超過給定數量
skip(long n) 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補

2-映 射

方法 描述
map(Function f) 接收一個函數作爲參數,該函數會被應用到每個元素上,並將其映射成一個新的元素。
mapToDouble(ToDoubleFunction f) 接收一個函數作爲參數,該函數會被應用到每個元素上,產生一個新的 DoubleStream。
mapToInt(ToIntFunction f) 接收一個函數作爲參數,該函數會被應用到每個元素上,產生一個新的 IntStream。
mapToLong(ToLongFunction f) 接收一個函數作爲參數,該函數會被應用到每個元素上,產生一個新的 LongStream。
flatMap(Function f) 接收一個函數作爲參數,將流中的每個值都換成另一個流,然後把所有流連接成一個流

3-排序

方法 描述
sorted() 產生一個新流,其中按自然順序排序
sorted(Comparator com) 產生一個新流,其中按比較器順序排序
Stream 的終止操作

終端操作會從流的流水線生成結果。其結果可以是任何不是流的值,例如:List、Integer,甚至是 void
流進行了終止操作後,不能再次使用。

1-匹配與查找

方法 描述
allMatch(Predicate p) 檢查是否匹配所有元素
anyMatch(Predicate p) 檢查是否至少匹配一個元素
noneMatch(Predicate p) 檢查是否沒有匹配所有元素
findFirst() 返回第一個元素
findAny() 返回當前流中的任意元素
count() 返回流中元素總數
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 內部迭代(使用 Collection 接口需要用戶去做迭代,稱爲外部迭代。相反,Stream API 使用內部迭代——它幫你把迭代做了)

2-歸約

方法 描述
reduce(T iden, BinaryOperator b) 可以將流中元素反覆結合起來,得到一個值。返回 T
reduce(BinaryOperator b) 可以將流中元素反覆結合起來,得到一個值。返回 Optional

備註:map 和 reduce 的連接通常稱爲 map-reduce 模式,因 Google 用它來進行網絡搜索而出名。
3-收集

方 法 描 述
collect(Collector c) 將流轉換爲其他形式。接收一個 Collector接口的實現,用於給Stream中元素做彙總的方法

Collector 接口中方法的實現決定了如何對流執行收集的操作(如收集到 List、Set、Map)。


另外, Collectors 實用類提供了很多靜態方法,可以方便地創建常見收集器實例,具體方法與實例如下表:

在這裏插入圖片描述

在這裏插入圖片描述

5.Optional類

  • 到目前爲止,臭名昭著的空指針異常是導致Java應用程序失敗的最常見原因。以前,爲了解決空指針異常,Google公司著名的Guava項目引入了Optional類,Guava通過使用檢查空值的方式來防止代碼污染,它鼓勵程序員寫更乾淨的代碼。受到Google Guava的啓發,Optional類已經成爲Java 8類庫的一部分。
  • Optional 類(java.util.Optional) 是一個容器類,它可以保存類型T的值,代表這個值存在。或者僅僅保存null,表示這個值不存在。原來用 null 表示一個值不存在,現在 Optional 可以更好的表達這個概念。並且可以避免空指針異常。
  • Optional類的Javadoc描述如下:這是一個可以爲null的容器對象。如果值存在則isPresent()方法會返回true,調用get()方法會返回該對象。

  • Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測。
  • 創建Optional類對象的方法:
    Optional.of(T t) : 創建一個 Optional 實例,t必須非空
    Optional.empty() : 創建一個空的 Optional 實例
    Optional.ofNullable(T t)t可以爲null
  • 判斷Optional容器中是否包含對象:
    boolean isPresent() : 判斷是否包含對象
    void ifPresent(Consumer<? super T> consumer) :如果有值,就執行Consumer接口的實現代碼,並且該值會作爲參數傳給它。
  • 獲取Optional容器的對象:
    T get(): 如果調用對象包含值,返回該值,否則拋異常
    T orElse(T other) :如果有值則將其返回,否則返回指定的other對象。
    T orElseGet(Supplier<? extends T> other) :如果有值則將其返回,否則返回由Supplier接口實現提供的對象。
    T orElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值則將其返回,否則拋出由Supplier接口實現提供的異常。
@Test
public void test1() {
	Boy b = new Boy("張三");
	Optional<Girl> opt = Optional.ofNullable(b.getGrilFriend());
	// 如果女朋友存在就打印女朋友的信息
	opt.ifPresent(System.out::println);
}
@Test
public void test2() {
	Boy b = new Boy("張三");
	Optional<Girl> opt = Optional.ofNullable(b.getGrilFriend());
	// 如果有女朋友就返回他的女朋友,否則只能欣賞“嫦娥”了
	Girl girl = opt.orElse(new Girl("嫦娥"));
	System.out.println("他的女朋友是:" + girl.getName());
}
@Test
public void test3(){
	Optional<Employee> opt = Optional.of(new Employee("張三", 8888));
	//判斷opt中員工對象是否滿足條件,如果滿足就保留,否則返回空
	Optional<Employee> emp = opt.filter(e -> e.getSalary()>10000);
	System.out.println(emp);
}
@Test
public void test4(){
	Optional<Employee> opt = Optional.of(new Employee("張三", 8888));
	//如果opt中員工對象不爲空,就漲薪10%
	Optional<Employee> emp = opt.map(e -> 
{e.setSalary(e.getSalary()%1.1);return e;});
	System.out.println(emp);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章