Java14 都來了,你還不會用 Java8嗎?


Java14 都來了,你還不會用 Java8嗎?


Java 8 於2014年3月18日發佈,並且成為主流的 Java,如今,雖然 Java 14 都已經發布了,但是 開發者和公司選擇的版本依舊是經久不衰的 Java 8 版本,如果你還不瞭解這些新特性,是時候學習一下了。

Java 8 更新的一些重要功能一覽

  • Iterable 接口中的 forEach() 方法
  • 接口中的默認方法和靜態方法
  • 功能接口和 Lambda 表達式
  • 用於集合上批量數據操作的 Java Stream API
  • Java 時間 API
  • 集合 API 的改進
  • 併發 API 改進
  • Java IO改進
  • 其他核心 API 改進

下面來簡要了解一下這些Java 8功能。我將提供一些代碼片段以更好地理解,因此,如果要在Java 8中運行程序,則必須按照以下步驟設置Java 8環境。

下載並安裝JDK8。像其他Java版本一樣,安裝也很簡單,運行下面的示例,必須要安裝 JDK 環境。

下載最新的 IDEA 開發環境,這裡我不推薦使用 Eclipse ,但是如果你有使用 Eclipse 習慣,那我在這裡推薦你可以嘗試 IDEA,因為它真的太棒啦~

Iterable 接口中的 forEach()方法

在 Java 8 以前,每當需要遍歷 Collection 時,就需要創建一個 Iterator 來進行迭代 Collection 對象,然後針對 Collection 中 的每個元素將業務邏輯循環在一起。如果迭代器使用不正確,可能會拋出 ConcurrentModificationException。

Java 8 在接口中引入了forEach方法,java.lang.Iterable因此在編寫代碼時,我們僅關注業務邏輯。forEach方法將java.util.function.Consumer對象作為參數,因此有助於將我們的業務邏輯放在一個可以重用的單獨位置。讓我們通過簡單的示例查看forEach用法。

<code>package com.journaldev.java8.foreach;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.lang.Integer;

public class Java8ForEachExample {

    public static void main(String[] args) {

        //creating sample Collection
        List<integer> myList = new ArrayList<integer>();
        for(int i=0; i<10; i++) myList.add(i);


        //traversing using Iterator
        Iterator<integer> it = myList.iterator();
        while(it.hasNext()){
            Integer i = it.next();
            System.out.println("Iterator Value::"+i);
        }

        //traversing through forEach method of Iterable with anonymous class
        myList.forEach(new Consumer<integer>() {

            public void accept(Integer t) {
                System.out.println("forEach anonymous class Value::"+t);
            }

        });

        //traversing with Consumer interface implementation
        MyConsumer action = new MyConsumer();
        myList.forEach(action);

    }

}

//Consumer implementation that can be reused
class MyConsumer implements Consumer<integer>{

    public void accept(Integer t) {
        System.out.println("Consumer impl Value::"+t);
    }


}/<integer>/<integer>/<integer>/<integer>/<integer>/<code>

代碼的行數可能會增加,但是forEach方法有助於將迭代邏輯和業務邏輯放在不同的位置,從而使關注點和代碼更清晰地分離。

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

如果仔細閱讀forEach方法的詳細信息,會注意到它是在Iterable接口中定義的,但我們知道接口不能具有方法主體。從Java 8開始,接口已增強為具有實現的方法。我們可以使用default和static關鍵字來創建帶有方法實現的接口。Iterable接口中的forEach方法實現為:

<code>default void forEach(Consumer super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}/<code>

我們知道Java不會在Class中提供多重繼承,因為它會導致Diamond問題。由於接口現在類似於抽象類,因此現在如何使用接口處理它。解決方案是在這種情況下編譯器將引發異常,我們將不得不在實現接口的類中提供實現邏輯。

<code>package com.journaldev.java8.defaultmethod;

@FunctionalInterface
public interface Interface1 {

    void method1(String str);

    default void log(String str){
        System.out.println("I1 logging::"+str);
    }

    static void print(String str){
        System.out.println("Printing "+str);
    }

    //trying to override Object method gives compile-time error as
    //"A default method cannot override a method from java.lang.Object"

//    default String toString(){
//        return "i1";
//    }

}/<code>
<code>package com.journaldev.java8.defaultmethod;

@FunctionalInterface
public interface Interface2 {

    void method2();

    default void log(String str){
        System.out.println("I2 logging::"+str);
    }

}/<code>

注意,兩個接口都有一個帶有實現邏輯的通用方法log()。

<code>package com.journaldev.java8.defaultmethod;

public class MyClass implements Interface1, Interface2 {

    @Override
    public void method2() {
    }

    @Override
    public void method1(String str) {
    }

    //MyClass won't compile without having it's own log() implementation
    @Override
    public void log(String str){
        System.out.println("MyClass logging::"+str);
        Interface1.print("abc");
    }

}/<code>

如你所見, Interface1 具有在 MyClass.log() 方法實現中使用的靜態方法實現。Java 8 在 Collection API 中大量使用默認和靜態方法,並且添加了默認方法,以便使 JDK 8 之前的代碼保持向後兼容。

如果層次結構中的任何類都具有具有相同的方法,則默認方法將變得無關緊要。由於任何實現接口的類都已經具有 Object 作為超類,因此如果接口中具有 equals(),hashCode() 默認方法,它將變得無關緊要。這就是為什麼為了更清楚起見,不允許接口具有Object默認方法。

有關Java 8接口特性的完整詳細信息,請閱讀 Java 8接口更改。

功能接口和Lambda表達式

如果你注意到上述接口代碼,則會注意到 @FunctionalInterface 註解。該功能接口是Java 8 中引入的新概念。具有一種抽象方法的接口就變成了功能接口。我們不需要使用 @FunctionalInterface 註解將接口標記為Functional Interface。@FunctionalInterface 註解是一種避免在功能接口中意外添加抽象方法的工具。您可以將其視為 @Override 批註,並且是使用它的最佳實踐。java.lang.Runnable 使用單個抽象方法 run() 是功能接口的一個很好的例子。

功能接口的主要優點之一是可以使用 lambda 表達式實例化它們。在 Java 8 之前,可以用匿名類實例化一個接口,但是代碼看起來很龐大。

<code>Runnable r = new Runnable(){
    @Override
    public void run() {
            System.out.println("My Runnable");
    }};/<code>

由於功能接口只有一種方法,因此 lambda 表達式可以輕鬆提供該方法的實現。只需要提供方法參數和業務邏輯。例如,可以使用 lambda 表達式編寫上面的實現:

<code>Runnable r1 = () -> {
            System.out.println("My Runnable");
        };/<code>

如果方法實現中只有一條語句,那麼我們也不需要花括號。例如,上面的Interface1匿名類可以使用lambda實例化,如下所示:

<code>Interface1 i1 = (s) -> System.out.println(s);

i1.method1("abc");/<code>

因此,lambda 表達式是輕鬆創建功能接口的匿名類的一種方法。使用 lambda 表達式對於代碼運行沒有任何的影響,因此要謹慎謹慎使用它,因為我們並不介意編寫一些額外的代碼行。

新包裝 java.util.function 添加了帶有功能接口束的,以提供 lambda 表達式和方法引用的目標類型。Lambda 表達式是一個非常複雜的話題,後續我會編寫一篇文章專門針對 lambda 表達式。

您可以在 Java 8 Lambda Expressions Tutorial 中閱讀完整的教程。

用於集合上批量數據操作的Java Stream API

java.util.stream 是Java 8中添加的一個新內容,以對該集合執行類似過濾/映射/遍歷的操作。Stream API 將允許順序執行和並行執行。這對我來說是非常好用的一個功能,因為我經常處理 Collections,而且通常使用很多的數據進行過濾數據,遍歷數據,stream 就完美的解決了這個問題。

Collection 接口已使用 stream() 和 parallelStream() 默認方法進行了擴展,以獲取用於順序執行和並行執行的 Stream ,用一個簡單的例子看看它們的用法。

<code>package com.journaldev.java8.stream;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamExample {

    public static void main(String[] args) {

        List<integer> myList = new ArrayList<>();
        for(int i=0; i<100; i++) myList.add(i);

        //sequential stream
        Stream<integer> sequentialStream = myList.stream();

        //parallel stream
        Stream<integer> parallelStream = myList.parallelStream();

        //using lambda with Stream API, filter example
        Stream<integer> highNums = parallelStream.filter(p -> p > 90);
        //using lambda in forEach
        highNums.forEach(p -> System.out.println("High Nums parallel="+p));

        Stream<integer> highNumsSeq = sequentialStream.filter(p -> p > 90);
        highNumsSeq.forEach(p -> System.out.println("High Nums sequential="+p));

    }

}/<integer>/<integer>/<integer>/<integer>/<integer>/<code>

如果你運行示例代碼,將獲得如下輸出:

<code>High Nums parallel=91
High Nums parallel=96
High Nums parallel=93
High Nums parallel=98
High Nums parallel=94
High Nums parallel=95
High Nums parallel=97
High Nums parallel=92
High Nums parallel=99
High Nums sequential=91
High Nums sequential=92
High Nums sequential=93
High Nums sequential=94
High Nums sequential=95
High Nums sequential=96
High Nums sequential=97
High Nums sequential=98
High Nums sequential=99/<code>

請注意,並行處理的時候。值不按順序排列,因此在處理龐大的集合時,並行處理將非常有幫助。

這篇文章無法涵蓋有關Stream API的所有內容,您可以在 Java 8 Stream API Example Tutorial 中閱讀有關Stream API的所有內容。

Java 8 時間API

在之前的 Java 中使用日期,時間和時區一直很困難。Java 中沒有用於日期和時間的標準方法或 API。Java 8 有一個不錯的附加功能是 java.time 軟件包,它簡化了 Java 中使用時間的過程。

僅查看 Java Time API 軟件包,就可以感覺到它非常易於使用。它具有一些子包 java.time.format,這些子包提供用於打印和解析日期和時間的類,還有java.time.zone 提供對時區及其規則的支持。

新的 Time API 在整月的幾個月和一週中的幾天中都使用了枚舉而不是整數常量。常用的類之一是 DateTimeFormatter 將 DateTime 對象轉換為字符串。

有關完整的教程,請轉到 Java日期時間API示例教程。

集合API的改進

在上面的介紹已經看到了 forEach() 方法和用於集合的 Stream API。

Collection API 中添加的一些新方法是:

IteratorforEachRemaining(Consumer action) 在所有元素都已處理完畢或該動作引發異常之前,對其餘每個元素執行給定操作的默認方法。

CollectionremoveIf(Predicate filter) 刪除此集合中所有滿足給定謂詞的元素的默認方法。

Collection spliterator() 該方法返回Spliterator實例,該實例可用於順序或並行遍歷元素。地圖replaceAll(),compute(),merge()方法。

另外還有一些具有 hash 衝突 的HashMap 類的性能改進

併發API改進

一些重要的併發API增強功能包括:

ConcurrentHashMap compute(),forEach(),forEachEntry(),forEachKey(),forEachValue(),merge(),reduce()和search()方法。CompletableFuture 可以明確完成(設置其值和狀態)。Executors newWorkStealingPool() 使用所有可用處理器作為目標並行度級別創建竊取線程池的方法。

Java IO改進

我知道的一些IO改進:

Files.list(Path dir) 返回一個延遲加載的 Stream,其元素是目錄中的文件夾和文件列表。

Files.lines(Path path) 返回一個讀取指定文件所有行的文件流。

Files.find() 返回一個根據指定目錄搜索指定文件的文件列表流。

BufferedReader.lines() 返回一個Stream,其元素是從此 BufferedReader 中讀取的行。

其他核心API改進

一些雜項API改進在某些特殊情況可能會派上用場:

  • ThreadLocal 靜態方法可以使用 withInitial 方法創建實例。
  • 比較器接口已擴展了許多默認和靜態方法,用於自然排序,反向排序等。
  • Integer,Long 和 Double 包裝器類中增加了 min(),max() 和 sum() 方法。
  • Boolean 類中的 logicalAnd() ,logicalOr() 和 logicalXor() 方法。
  • ZipFile.stream() 方法獲取 ZIP 文件條目上的有序Stream,並以壓縮時的順序出現在 Stream 中。
  • Math 類中增加了幾種實用方法。
  • jjs 添加命令以調用 Nashorn Engine。
  • jdeps 添加命令以分析類文件
  • JDBC-ODBC 橋已被刪除。
  • PermGen 內存空間已被刪除


碼字不易,如果能幫到你,請點點關注~


分享到:


相關文章: