Java8新特性


 

Java 8新特性簡介

  • 速度更快

  • 代碼更少(增加了新的語法 Lambda 表達式)

  • 強大的 Stream API

  • 便於並行

  • 最大化減少空指針異常 Optional

其中最為核心的為 Lambda 表達式與Stream API

1 Lambda表達式

為什么使用  Lambda  表達式

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

從匿名類到 Lambda 的轉換

 

Lambda 表達式語法

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

左側:指定了 Lambda 表達式需要的所有參數

右側:指定了 Lambda 體,即 Lambda 表達式要執行的功能。

語法格式一:無參,無返回值,Lambda 體只需一條語句

 

語法格式二:Lambda 需要一個參數

 

語法格式三:Lambda 只需要一個參數時,參數的小括號可以省略

語法格式四:Lambda 需要兩個參數,並且有返回值

 

語法格式五:當 Lambda 體只有一條語句時,return 與大括號可以省略

 

 

類型推斷

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

函數式接口

什么是函數式接口

只包含一個抽象方法的接口,稱為函數式接口。

你可以通過 Lambda 表達式來創建該接口的對象。(若 Lambda 表達式拋出一個受檢異常,那么該異常需要在目標接口的抽象方 法上進行聲明)。

我們可以在任意函數式接口上使用 @FunctionalInterface 注解, 這樣做可以檢查它是否是一個函數式接口,同時 javadoc 也會包含一條聲明,說明這個接口是一個函數式接口。

自定義函數式接口

函數式接口中使用泛型:

 

作為參數傳遞 Lambda 表達式

 

作為參數傳遞 Lambda 表達式:為了將 Lambda 表達式作為參數傳遞,接收Lambda 表達式的參數類型必須是與該 Lambda 表達式兼容的函數式接口的類型。

Java 內置四大核心函數式接口

函數式接口

參數類型

返回類型

用途

Consumer<T> 消費型接口

T

void

對類型為T的對象應用操作,包含方法: void accept(T t)

Supplier<T> 供給型接口

T

返回類型為T的對象,包含方法:T get();

Function<T, R> 函數型接口

T

R

對類型為T的對象應用操作,並返回結果。結果是R類型的對象。包含方法:R apply(T t);

Predicate<T> 斷定型接口

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<T>

(Function子接口)

T

T

對類型為T 的對象進行一元運算, 並返回T 類型的結果。包含方法為

T apply(T t);

BinaryOperator<T>

(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)

ToIntFunction<T>

ToLongFunction<T>

ToDoubleFunction<T>

T

int

long

double

分 別 計 算 int 、 long 、

double、值的函數

IntFunction<R>

LongFunction<R>

DoubleFunction<R>

int

long

double

R

參數分別為int 、long 、

double 類型的函數

方法引用與構造器引用

方法引用

當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!(實現抽象方法的參數列表,必須與方法引用方法的參數列表保持一致!)

方法引用:使用操作符 “::” 將方法名和對象或類的名字分隔開來。

如下三種主要使用情況:

對象::實例方法

類::靜態方法

類::實例方法

方法引用

例如:

等同於:

 

例如:

 

等同於:

 

例如:

 

等同於:

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

構造器引用

格式: ClassName::new 

與函數式接口相結合,自動與函數式接口中方法兼容。 可以把構造器引用賦值給定義的方法,與構造器參數列表要與接口中抽象方法的參數列表一致!

例如:

等同於:

數組引用

格式: type[] :: new

例 如 :
等同於:

強大的 Stream API

了解 Stream

Java8中有兩大最為重要的改變。第一個是 Lambda 表達式;另外一個則是 Stream API(java.util.stream.*)。

Stream 是 Java8 中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常復雜的查找、過濾和映射數據等操作。  使用Stream API 對集合數據進行操作,就類似於使用 SQL 執行的數據庫查詢。也可以使用 Stream API 來並行執行操作。簡而言之, Stream API 提供了一種高效且易於使用的處理數據的方式。

什么是 Stream

流(Stream) 到底是什么呢?

是數據渠道,用於操作數據源(集合、數組等)所生成的元素序列。

“集合講的是數據,流講的是計算!”

注意:

①Stream 自己不會存儲元素。

②Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream。

③Stream 操作是延遲執行的。這意味着他們會等到需要結果的時候才執行。

Stream 的操作三個步驟

創建 Stream

一個數據源(如:集合、數組),獲取一個流

中間操作

一個中間操作鏈,對數據源的數據進行處理

終止操作(終端操作)

一個終止操作,執行中間操作鏈,並產生結果

創建 Stream

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

default Stream<E> stream() : 返回一個順序流

default Stream<E> parallelStream() : 返回一個並行流

由數組創建流

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

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

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

public static IntStream stream(int[] array)

public static LongStream stream(long[] array)

public static DoubleStream stream(double[] array)

由值創建流

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

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

由函數創建流:

創建無限流

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

迭代

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

生成

public static<T> Stream<T> generate(Supplier<T> s) :

Stream 的中間操作

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

篩選與切片

方 法

描 述

filter(Predicate p)

接收 Lambda , 從流中排除某些元素。

distinct()

篩選,通過流所生成元素的 hashCode() 和 equals() 去除重復元素

limit(long maxSize)

截斷流,使其元素不超過給定數量。

skip(long n)

跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補

Stream 的中間操作

映射

方 法

描 述

map(Function f)

接收一個函數作為參數,該函數會被應用到每個元素上,並將其映射成一個新的元素。

mapToDouble(ToDoubleFunction f)

接收一個函數作為參數,該函數會被應用到每個元素上,產生一個新DoubleStream。

mapToInt(ToIntFunction f)

接收一個函數作為參數,該函數會被應用到每個元素上,產生一個新的 IntStream。

mapToLong(ToLongFunction f)

接收一個函數作為參數,該函數會被應用到每個元素上,產生一個新的LongStream。

flatMap(Function f)

接收一個函數作為參數,將流中的每個值都換成另一個流,然后把所有流連接成一個流

Stream 的中間操作

排序

方 法

描 述

sorted()

產生一個新流,其中按自然順序排序

sorted(Comparator comp)  產生一個新流,其中按比較器順序排序

 

Stream 的終止操作

終端操作會從流的流水線生成結果。其結果可以是任何不是流的值,例如:List、Integer,甚至是 void 。

查找與匹配

方 法

描 述

allMatch(Predicate p)

檢查是否匹配所有元素

anyMatch(Predicate p)

檢查是否至少匹配一個元素

noneMatch(Predicate p)

檢查是否沒有匹配所有元素

findFirst()

返回第一個元素

findAny()

返回當前流中的任意元素

Stream 的終止操作

count()

返回流中元素總數

 max(Comparator c)

返回流中最大值

min(Comparator c)

返回流中最小值

forEach(Consumer c)

內部迭代(使用 Collection 接口需要用戶去做迭代,稱為外部迭代。相反,Stream API 使用內部迭代——它幫你把迭代做了)

歸約

reduce(T iden, BinaryOperator b)

可以將流中元素反復結合起來,得到一個值。返回 T

reduce(BinaryOperator b)

可以將流中元素反復結合起來,得到一個值。返回 Optional<T>

 

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

Stream 的終止操作

收集 

方 法

描 述

collect(Collector c)

將流轉換為其他形式。接收一個 Collector接口的實現,用於給Stream中元素做匯總的方法


Collector 接口中方法的實現決定了如何對流執行收集操作(如收集到 List、Set、Map)。但是 Collectors 實用類提供了很多靜態方法,可以方便地創建常見收集器實例,具體方法與實例如下表:

方法

返回類型

作用

toList

List<T>

把流中元素收集到List

List<Employee> emps= list.stream().collect(Collectors.toList());

toSet

Set<T>

把流中元素收集到Set

Set<Employee> emps= list.stream().collect(Collectors.toSet());

toCollection

Collection<T>

把流中元素收集到創建的集合

Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new));

counting

Long

計算流中元素的個數

long count = list.stream().collect(Collectors.counting());

summingInt

Integer

對流中元素的整數屬性求和

inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary));

averagingInt

Double

計算流中元素Integer屬性的平均

doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary));

summarizingInt

IntSummaryStatistics

收集流中Integer屬性的統計值。

如:平均值

IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));

joining

String

連接流中每個字符串

String str= list.stream().map(Employee::getName).collect(Collectors.joining());

maxBy

Optional<T>

根據比較器選擇最大值

Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));

minBy

Optional<T>

根據比較器選擇最小值

Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));

reducing

歸約產生的類型

從一個作為累加器的初始值開始,利用BinaryOperator與流中元素逐個結合,從而歸

約成單個值

inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));

collectingAndThen

轉換函數返回的類型

包裹另一個收集器,對其結

果轉換函數

inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));

groupingBy

Map<K, List<T>>

根據某屬性值對流分組,屬

性為K,結果為V

Map<Emp.Status, List<Emp>> map= list.stream()

.collect(Collectors.groupingBy(Employee::getStatus));

partitioningBy

Map<Boolean, List<T>>

根據true或false進行分區

Map<Boolean,List<Emp>>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));

並行流與串行流

並行流就是把一個內容分成多個數據塊,並用不同的線程分

別處理每個數據塊的流。

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

了解 Fork/Join 框架

Fork/Join 框架:就是在必要的情況下,將一個大任務,進行拆分(fork)成若干個小任務(拆到不可再拆時),再將一個個的小任務運算的結果進行 join 匯總.

Fork/Join 框架與傳統線程池的區別

采用 “工作竊取”模式(work-stealing):

當執行新的任務時它可以將其拆分分成更小的任務執行,並將小任務加到線 程隊列中,然后再從一個隨機線程的隊列中偷一個並把它放在自己的隊列中。

相對於一般的線程池實現,fork/join框架的優勢體現在對其中包含的任務的處理方式上.在一般的線程池中,如果一個線程正在執行的任務由於某些原因無法繼續運行,那么該線程會處於等待狀態.而在fork/join框架實現中,如果某個子問題由於等待另外一個子問題的完成而無法繼續運行.那么處理該子問題的線程會主動尋找其他尚未運行的子問題來執行.這種方式減少了線程的等待時間,提高了性能.

新時間日期 API

使用 LocalDateLocalTimeLocalDateTime

LocalDate、LocalTime、LocalDateTime 類的實例是不可變的對象,分別表示使用 ISO-8601日歷系統的日期、時間、日期和時間。它們提供 了簡單的日期或時間,並不包含當前的時間信息。也不包含與時區相關的信息。

注:ISO-8601日歷系統是國際標准化組織制定的現代公民的日期和時間的表示法

方法

描述

示例

now()

靜態方法,根據當前時間創建對象

LocalDate localDate = LocalDate.now();

LocalTime localTime = LocalTime.now(); LocalDateTime localDateTime = LocalDateTime.now();

of()

靜態方法,根據指定日期/時間創建

對象

LocalDate localDate = LocalDate.of(2016, 10, 26);

LocalTime localTime = LocalTime.of(02, 22, 56); LocalDateTime localDateTime = LocalDateTime.of(2016, 10, 26, 12, 10, 55);

plusDays, plusWeeks, plusMonths, plusYears

向當前 LocalDate 對象添加幾天、幾周、幾個月、幾年

 

minusDays, minusWeeks, minusMonths, minusYears

從當前 LocalDate 對象減去幾天、幾周、幾個月、幾年

 

plus, minus

添加或減少一個 Duration 或 Period

 

withDayOfMonth, withDayOfYear, withMonth, withYear

將月份天數、年份天數、月份、年

份 修 改 為 指 定 的 值 並 返 回 新 的

LocalDate 對象

 

getDayOfMonth

獲得月份天數(1-31)

 

getDayOfYear

獲得年份天數(1-366)

 

getDayOfWeek

獲得星期幾(返回一個 DayOfWeek

枚舉值)

 

getMonth

獲得月份, 返回一個 Month 枚舉值

 

getMonthValue

獲得月份(1-12)

 

getYear

獲得年份

 

until

獲得兩個日期之間的 Period 對象, 或者指定 ChronoUnits 的數字

 

isBefore, isAfter

比較兩個 LocalDate

 

isLeapYear

判斷是否是閏年

 

Instant 時間戳

用於“時間戳”的運算。它是以Unix元年(傳統的設定為UTC時區1970年1月1日午夜時分)開始所經歷的描述進行運算

Duration 和 Period

Duration:用於計算兩個“時間”間隔

Period:用於計算兩個“日期”間隔

日期的操縱

TemporalAdjuster : 時間校正器。有時我們可能需要獲取例如:將日期調整到“下個周日”等操作。

TemporalAdjusters : 該類通過靜態方法提供了大量的常用 TemporalAdjuster 的實現。

例如獲取下個周日:

 

解析與格式化

java.time.format.DateTimeFormatter 類:該類提供了三種格式化方法:

預定義的標准格式

語言環境相關的格式

自定義的格式

時區的處理

Java8 中加入了對時區的支持,帶時區的時間為分別為:

ZonedDate、ZonedTime、ZonedDateTime

其中每個時區都對應着 ID,地區ID都為 “{區域}/{城市}”的格式

例如 :Asia/Shanghai 等

ZoneId:該類中包含了所有的時區信息getAvailableZoneIds() : 可以獲取所有時區時區信息of(id) : 用指定的時區信息獲取 ZoneId 對象

與傳統日期處理的轉換

To 遺留類

From 遺留類

java.time.Instant

java.util.Date

 

Date.from(instant)

 

date.toInstant()

java.time.Instant java.sql.Timestamp

 

Timestamp.from(instant)

 

timestamp.toInstant()

java.time.ZonedDateTime java.util.GregorianCalendar

GregorianCalendar.from(zonedDateTim e)

 

cal.toZonedDateTime()

java.time.LocalDate java.sql.Time

 

Date.valueOf(localDate)

 

date.toLocalDate()

java.time.LocalTime java.sql.Time

 

Date.valueOf(localDate)

 

date.toLocalTime()

java.time.LocalDateTime java.sql.Timestamp

 

Timestamp.valueOf(localDateTime)

 

timestamp.toLocalDateTime()

java.time.ZoneId java.util.TimeZone

 

Timezone.getTimeZone(id)

 

timeZone.toZoneId()

java.time.format.DateTimeFormatter java.text.DateFormat

 

formatter.toFormat()

接口中的默認方法與靜態方法

接口中的默認方法

Java 8中允許接口中包含具有具體實現的方法,該方法稱為“默認方法”,默認方法使用 default 關鍵字修飾。

例如:

接口中的默認方法

接口默認方法的”類優先”原則

若一個接口中定義了一個默認方法,而另外一個父類或接口中又定義了一個同名的方法時

選擇父類中的方法。如果一個父類提供了具體的實現,那么接口中具有相同名稱和參數的默認方法會被忽略。

接口沖突。如果一個父接口提供一個默認方法,而另一個接口也提供了一個具有相同名稱和參數列表的方法(不管方法是否是默認方法),那么必須覆蓋該方法來解決沖突

接口中的靜態方法

Java8 中,接口中允許添加靜態方法。

例如:

其他新特性

Optional 

Optional<T> 類(java.util.Optional) 是一個容器類,代表一個值存在或不存在, 原來用 null 表示一個值不存在,現在 Optional 可以更好的表達這個概念。並且可以避免空指針異常。

常用方法:

Optional.of(T t) : 創建一個 Optional 實例Optional.empty() : 創建一個空的 Optional 實例

Optional.ofNullable(T t):若 t 不為 null,創建 Optional 實例,否則創建空實例isPresent() : 判斷是否包含值

orElse(T t) :  如果調用對象包含值,返回該值,否則返回t orElseGet(Supplier s) :如果調用對象包含值,返回該值,否則返回 s 獲取的值

map(Function f): 如果有值對其處理,並返回處理后的Optional,否則返回 Optional.empty()

flatMap(Function mapper):與 map 類似,要求返回值必須是Optional

重復注解與類型注解

Java 8對注解處理提供了兩點改進:可重復的注解及可用於類型的注解。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM