JDK1.7與JDK1.8的區別,你知道了嗎


Java 8於2014年3月18日發布,在閱讀相關文章后,在本教程中,我們將通過示例研究 Java 8功能。

一、Java 8 的新特性

  1. Iterable 接口中的 forEach()方法
  2. 接口中允許有默認方法和靜態方法
  3. 函數式接口與 Lambda 表達式
  4. 用於集合上批量數據操作的 Java Stream API
  5. 新的時間 API
  6. 集合 API 的改進
  7. 並發 API 的改進
  8. Java IO 的改進
  9. 其他核心 API 的改進

以上是 Java 8 的新特性的列舉,接下來展示一些代碼片段,來更好理解這些新特性

二、新特性應用

2.1、Iterable 接口中的 forEach()方法

package com.taotao.springboot;

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

public class IterableForEachExample {
    public static void main(String[] args) {
        List<Integer> ints = new ArrayList<>();
        for (int i = 0; i < 10; i++) ints.add(i);
        //迭代器遍歷
        Iterator<Integer> iterator = ints.iterator();
        while (iterator.hasNext()) {
            Integer i = iterator.next();
            System.out.println("Iterator Value::" + i);
        }
        //使用forEach方法遍歷,這里使用了匿名內部類
        ints.forEach(new Consumer<Integer>() {
            public void accept(Integer t) {
                System.out.println("forEach anonymous class Value::" + t);
            }
        });
        //通過實現Consumer接口的子類遍歷,業務處理從遍歷邏輯中抽取出來
        MyConsumer action = new MyConsumer();
        ints.forEach(action);
    }
}

//實現Conumer接口
class MyConsumer implements Consumer<Integer> {

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

通過上面這個例子,看出使用 forEach()方法代碼行數會增加,但是它將遍歷邏輯與業務處理邏輯分離,有助於我們將關注點放在業務邏輯的處理上。

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

在上面的例子中,如果有點進去看 forEach()方法,你會發現它是在接口 Iterable 中定義的。但是我們知道接口不能有方法體。從 Java 8開始,接口已改進,允許有默認的實現方法。我們可以在接口中使用關 defaultstatic 關鍵字來創建方法實現。以下是 forEach()的定義。

 Iterable 接口中 forEach方法的定義

我們知道在 Java 中不能同時繼承多個類,因為這樣做會引發 Diamond 問題 ,接口允許有默認方法實現后,接口變得與抽象類非常類似,現在可以利用接口解決這個問題。我們定義兩個接口 FunctionInterface1 和FunctionInterface2,以及一個它兩的實現類 FunctionInterfaceImpl

package com.taotao.springboot;

@FunctionalInterface
public interface FunctionInterface1 {

    void hello1(String str);

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

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

    //如果默認方法重寫了 Object 類中方法,編譯不能通過
//    default String toString(){
//        return "";
//    }

}

在上面代碼中,我們知道接口中的默認實現方法不能與Object類中的方法簽名相同

package com.taotao.springboot;

@FunctionalInterface
public interface FunctionInterface2 {

    void hello2(String str);

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

}

注意,FunctionInterface1 與 FunctionInterface2 都有一個默認的實現方法 eat()。在這種情況下,我們看看它兩的實現類 FunctionInterfaceImpl 會不會有要注意的點。

package com.taotao.springboot;

public class FunctionInterfaceImpl implements FunctionInterface1, FunctionInterface2 {
    @Override
    public void hello1(String str) {
        System.out.println("hello1 " + str);
    }

    //如果不實現 eat() 方法,編譯不通過
    @Override
    public void eat(String str) {
        System.out.println("functionInterfaceImpl eatting " + str);
    }

    @Override
    public void hello2(String str) {
        System.out.println("hello2 " + str);
    }

    public static void main(String[] args) {
        FunctionInterface1 interface1 = new FunctionInterfaceImpl();
        interface1.hello1("world");
        interface1.eat("apple");
        FunctionInterface2 interface2 = new FunctionInterfaceImpl();
        interface2.eat("banana");
        interface2.hello2("world");
    }
}

注意,FunctionInterfaceImpl 必須實現 eat()方法。運行 main 方法,打印輸出如下:

hello1 world
functionInterfaceImpl eatting apple
functionInterfaceImpl eatting banana
hello2 world

可見,Java 8 在 Collection API 中大量使用默認靜態方法,以便我們的代碼保持向后兼容。

同時從 functionInterfaceImpl 的例子可以看出,如果一個類同時實現了兩個及以上接口,而這些接口中都有一個簽名相同的方法,那么,這個類必須實現這個方法,否則將不能通過編譯。此時,接口中的默認實現方法已經不起作用了。如果類只是實現一個接口,則沒有這個問題

這也就解釋了之前提到的 接口中的默認實現方法不能與Object類中的方法簽名相同 。因為Object是所有類的超類(父類),因此如果接口中具有equals(),hashCode()默認方法,那這些默認方法也將不起作用。

2.3、函數式接口與 Lambda 表達式

如果您仔細觀察上述接口代碼,則會注意到 @FunctionalInterface 注解。函數式接口是 Java 8中引入的新概念。有且只有一個抽象方法的接口就是函數式接口。@FunctionalInterface 注解不是函數式接口定義的必要條件,它是一個避免在函數式接口中意外添加其他抽象方法的注解。java.lang.Runnable具有單個抽象方法run(),是函數式接口一個很好的例子。

函數式接口的主要優點之一是可以使用 lambda 表達式實例化它們。我們可以用匿名內部類實例化一個接口,但是代碼看起來很龐大。

//匿名內部類實現Runnable
Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("My Runnable");
            }
        };
//lambda 表達式實現Runnable
Runnable runnable = () -> {
            System.out.println("My Runnable");
        };

如果方法實現中只有一條語句,我們也不需要花括號。如下所示:

Runnable runnable = () -> System.out.println("My Runnable");

因此,lambda表達式是輕松創建函數式接口的匿名類的一種方法。使用lambda表達式代碼可讀性感覺更差,因此要謹慎使用它,不介意編寫一些額外的代碼行。

java.util.function是添加了帶有函數式接口的新程序包,以提供lambda表達式和方法引用的目標類型。

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

java.util.stream是 Java 8中添加的新程序包,以便對集合執行類似過濾/映射/歸約的操作。Stream API 將允許順序執行和並行執,是最好的功能之一,如果經常處理Collections,而且集合元素很多,我們可以根據某些條件過濾掉它們。

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

package com.taotao.springboot;

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

public class StreamList {
    public static void main(String[] args) {
        List<Integer> myList = new ArrayList<>();
        for (int i = 0; i < 100; i++) myList.add(i);

        //串行流
        Stream<Integer> sequentialStream = myList.stream();

        //並行流
        Stream<Integer> parallelStream = myList.parallelStream();

        //過濾
        Stream<Integer> highNums = parallelStream.filter(p -> p > 90);
        //使用 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));
    }
}

運行上述代碼的 main 方法,你將看到如下輸出:

High Nums parallel=91
High Nums parallel=93
High Nums parallel=96
High Nums parallel=94
High Nums parallel=95
High Nums parallel=92
High Nums parallel=97
High Nums parallel=98
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

請注意,並行流不按集合元素排列順序處理,但在處理大量元素的集合時將非常有用。

2.5、新的時間 API

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

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

新的Time API在整月的幾個月和一周中的幾天中都更喜歡枚舉而不是整數常量。將DateTime對象轉換為字符串的類是DateTimeFormatter

2.6、集合API的改進

我們已經看到了 forEach()方法和用於集合的 Stream API。Collection API還有一些新方法是:

  • Iterator forEachRemaining(Consumer action)在所有元素都已處理完畢或該動作引發異常之前,對每個剩余元素執行給定操作的默認方法。
  • Collection removeIf(Predicate filter)刪除此集合中所有滿足特定條件的元素的默認方法。
  • Collection spliterator() 該方法返回Spliterator實例,該實例可用於順序或並行遍歷元素。
  • map replaceAll()compute()merge()方法。
  • 具有鍵沖突的HashMap類的性能改進

2.7、並發 API 的改進

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

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

2.8、Java IO改進

一些IO改進包括:

  • Files.list(Path dir) 返回延遲填充的Stream,其元素是目錄中的條目。
  • Files.lines(Path path) 從文件中讀取所有行作為流。
  • Files.find() 通過在以給定起始文件為根的文件樹中搜索文件,返回通過路徑延遲填充的Stream。
  • BufferedReader.lines() 返回一個Stream,其元素是從此BufferedReader中讀取的行。

2.9、其他核心API改進

一些其他API改進:

  1. ThreadLocal 靜態方法可以使用 withInitial(Supplier supplier) 輕松創建實例。
  2. Comparator 接口已擴展了許多默認和靜態方法,用於自然排序,反向排序等。
  3. Integer,Long 和 Double 包裝器類中的 min(),max()和sum()方法。
  4. 布爾類中的 logicalAnd(),logicalOr()和 logicalXor()方法。
  5. ZipFile.stream()方法獲取ZIP文件條目上的有序 Stream。條目以在 ZIP 文件的中央目錄中出現的順序出現在 Stream 中。
  6. Math類中的幾種實用方法。
  7. jjs 添加命令以調用 Nashorn Engine。
  8. jdeps 添加命令以分析類文件
  9. JDBC-ODBC 橋已被刪除。
  10. PermGen 內存空間已被刪除

總結

這就是帶有代碼片段的 Java 8 全部新特性。


免責聲明!

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



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