天天看點

Java17 vs Java8: 新舊對決,這些Java 17新特性你不容錯過

作者:Java架構日記

Java是一門非常流行的程式設計語言,由于其跨平台性、可移植性以及強大的面向對象特性而備受青睐。Java最初由Sun Microsystems公司于1995年推出,随着時間的推移,Java發展迅速,版本不斷更新。本篇部落格将重點介紹Java 17與Java 8的對比,以及Java 17的新特性。

特征 Java 17 Java 8
引入 2021年9月14日 2014年3月
垃圾收集器 ZGC(新型垃圾收集器) G1收集器
其他垃圾收集器 Shenandoah GC,G1 GC,Parallel GC,Serial GC Parallel GC,Serial GC
垃圾回收政策 全堆回收和增量模式 複制模式
應用程式類資料共享(AppCDS) 支援 不支援
JFR事件流 使用異步處理提高性能 未支援
條件性執行個體化卡片 支援 支援
嵌入式C / C ++庫 JDK不包括C / C ++編譯器 JDK不包括C / C ++編譯器
算法更新 SHA-3,SM3 / SM4,Ed448,RSASSA-PSS,X25519 / X448 SHA-1,RC4,DES,MD5,DSA,DH

一、Java 17與Java 8的對比

Java 17與Java 8是Java版本中的兩個重要裡程碑。Java 8是Java版本中的一次重大更新,于2014年釋出,引入了很多新的特性和功能,包括Lambda表達式、Stream API、函數式接口等。Java 17是Java SE 17版本,于2021年9月釋出,是Java SE 16的長期支援(LTS)版本。Java 17中也有一些新的特性和改進,我們将在後文中詳細讨論。

二、性能比較

Java 17與Java 8在性能方面的比較非常重要。Java 8引入了一些性能改進,例如優化了字元串連接配接和數組排序等操作。Java 17在性能方面也有一些新的改進,例如:

  • 改進了JIT編譯器,提高了應用程式的性能。
  • 改進了垃圾回收器,提高了垃圾回收的效率和吞吐量。
  • 引入了C++風格的記憶體管理,包括對堆記憶體配置設定的優化和對垃圾回收的改進。 這些改進都可以提高Java應用程式的性能和響應速度。

三、語言特性比較

Java 8引入了一些新的語言特性,例如Lambda表達式和函數式接口。這些特性讓Java程式員能夠使用函數式程式設計的方式編寫代碼,進而使得代碼更加簡潔、易讀、易維護。Java 17在語言特性方面也有一些新的改進,例如:

  • 引入了Sealed類,這是一種新的類修飾符,用于限制類的繼承。這樣可以使得代碼更加安全、可維護。
  • 引入了Pattern Matching for Switch文法,這是一種新的switch文法,可以用于模式比對。這樣可以使得代碼更加簡潔、易讀、易維護。
  • 引入了Record類,這是一種新的資料類,可以用于定義隻有屬性和通路器的簡單資料對象。這樣可以使得代碼更加簡潔、易讀、易維護。
  • 這些改進都可以使得Java程式員能夠使用更加先進、更加高效的語言特性編寫代碼。

四、應用場景比較

Java 8和Java 17都可以用于不同的應用場景,但是它們在一些方面有所不同。Java 8适用于開發中小型應用程式和Web應用程式,例如Web服務、企業級應用程式和桌面應用程式等。Java 8也可以用于開發大型應用程式,但是在大型應用程式中可能會出現一些性能問題。Java 17則更适合用于開發大型應用程式和高性能應用程式,例如高性能計算、雲計算、大資料處理等。

五、Java 17的新特性

Java 17是Java SE 17版本,于2021年9月釋出,是Java SE 16的長期支援(LTS)版本。Java 17中有許多新的特性和改進,以下是一些主要特性:

5.1 Sealed類

Sealed類是一種新的類修飾符,用于限制類的繼承。Sealed類可以控制哪些類可以繼承自它,這樣可以使得代碼更加安全、可維護。Sealed類的使用可以在編譯時強制執行一些規則,進而避免運作時錯誤。

5.1.1 代碼示例

java複制代碼public sealed abstract class Shape permits Circle, Rectangle {
    public abstract double calculateArea();
}

public final class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

public final class Rectangle extends Shape {
    private double length;
    private double width;

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    public double getLength() {
        return length;
    }

    public double getWidth() {
        return width;
    }

    public double calculateArea() {
        return length * width;
    }
}
           

代碼說明: 在這個示例中,Shape 是一個抽象類,并且使用 permits 關鍵字,明确允許哪些類繼承該類。Circle 和 Rectangle 是 Shape 的子類,并使用 final 關鍵字來表示它們是封閉類,不允許有其他子類繼承它們。這種方式可以在編譯時校驗代碼,并防止意外建立不受預期的子類。

5.2 Pattern Matching for Switch文法

Pattern Matching for Switch文法是一種新的switch文法,可以用于模式比對。Pattern Matching for Switch文法可以根據不同的模式執行不同的操作,進而使得代碼更加簡潔、易讀、易維護。Pattern Matching for Switch文法可以減少代碼量,避免出現大量的if-else語句。

5.2.1 代碼示例

java複制代碼public static void main(String[] args) {
    Object obj = "hello";

    switch (obj) {
        case String s && s.length() > 5 -> System.out.println("長字元串");
        case String s -> System.out.println("短字元串");
        case Integer i -> System.out.println("整型數");
        default -> System.out.println("不支援的類型");
    }
}

           

代碼說明: 在這個示例中,我們首先定義了一個 Object 類型的變量 obj,它可能是一個字元串、整型數或其他類型的對象。

接下來,我們使用了 switch 語句,并對 obj 進行了幾個模式比對:

  • 如果 obj 是一個長度大于 5 的字元串,表達式 case String s && s.length() > 5 就會被比對并執行相應的代碼塊。
  • 如果 obj 是一個短字元串,表達式 case String s 會比對并執行相應代碼塊。
  • 如果 obj 是一個整型數,表達式 case Integer i 就會執行相應代碼塊。
  • 如果 obj 不屬于以上任何一種類型,就會執行預設代碼塊。

5.3 Record類

Record類是一種新的資料類,可以用于定義隻有屬性和通路器的簡單資料對象。Record類可以簡化代碼,使得代碼更加易讀、易維護。Record類的使用可以減少代碼量,避免出現大量的getter和setter方法。

5.3.1 代碼示例

java複制代碼public record Person(String name, int age) {}

public class RecordExample {
    public static void main(String[] args) {
        Person person = new Person("John", 30);

        System.out.println("Name: " + person.name());
        System.out.println("Age: " + person.age());
    }
}

           

代碼說明: 在這個示例中,我們定義了一個名為 Person 的 Record 類,它有兩個字段:name 和 age。Record 類會自動生成一個帶有這些字段的構造函數、getter 方法和 equals、hashCode 和 toString 方法。

  • 我們在 main 方法中建立了一個 Person 對象,并使用 name() 和 age() 方法擷取其名稱和年齡資訊,然後将其列印出來。
  • 使用 Record 類,我們可以更輕松地定義簡單的資料類,而不需要手動編寫大量的構造函數和 getter 方法。這可以使我們的代碼更加簡潔、清晰,并且更易于閱讀和維護。

5.4 改進的垃圾回收器

Java 17中改進了垃圾回收器,提高了垃圾回收的效率和吞吐量。改進的垃圾回收器可以更加高效地回收記憶體,進而提高應用程式的性能和響應速度。

5.4.1 代碼示例

java複制代碼public class GarbageCollectorExample {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < 1000000; i++) {
            list.add(i);
        }

        System.out.println("List size: " + list.size());

        System.gc(); // 調用垃圾回收器

        System.out.println("List size after GC: " + list.size());
    }
}

           

代碼說明: 在這個示例中,我們使用了 ZGC 垃圾回收器來回收 list 對象占用的記憶體。我們在代碼中使用了 System.gc() 方法來手動觸發垃圾回收器。注意,在實際應用中,我們通常不需要手動觸發垃圾回收器,因為 JVM 會自動進行垃圾回收操作。

ZGC 垃圾回收器具有可伸縮性和低延遲的特點,可以在處理大型、高并發應用程式時提供更好的性能和吞吐量。除了 ZGC,Java 17 中還引入了 Shenandoah 垃圾回收器,它也具有類似的高性能和低延遲的特點。

5.5 改進的JIT編譯器

Java 17中改進了JIT編譯器,提高了應用程式的性能。改進的JIT編譯器可以更加高效地編譯代碼,進而提高應用程式的性能和響應速度。

5.5.1 代碼示例

java複制代碼public class JITCompilerExample {
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 0; i < 1000000; i++) {
            sum += i;
        }
        System.out.println("Sum is: " + sum);
    }
}
           

在Java 17中,可以通過添加以下指令行參數來啟用Graal編譯器:

ruby複制代碼-XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler
           

代碼說明: 當運作上述示例代碼時,Graal編譯器會自動将循環優化為一個簡單的算術公式,進而大大提高了性能。

5.6 C++風格的記憶體管理

Java 17中引入了C++風格的記憶體管理,包括對堆記憶體配置設定的優化和對垃圾回收的改進。C++風格的記憶體管理可以使得Java應用程式更加高效,進而提高應用程式的性能和響應速度。

5.6.1 代碼示例

java複制代碼import java.lang.management.MemoryPoolMXBean;
import java.lang.management.ManagementFactory;

public class MemoryManagementExample {

    public static void main(String[] args) throws InterruptedException {
        MemoryPoolMXBean heap = ManagementFactory.getMemoryPoolMXBeans().stream()
                .filter(p -> p.getName().equals("Java heap")).findFirst().orElseThrow();

            System.out.println("Heap memory utilization statistics:\n");

            try (var scope = heap.reserveMemory(1024 * 1024)) {
                long usedMemory = heap.getUsage().getUsed();
                long commitedMemory = heap.getUsage().getCommitted();

                System.out.printf("Before allocation: used=%d, committed=%d%n", usedMemory, commitedMemory);

                byte[] array = new byte[1024 * 1024];

                usedMemory = heap.getUsage().getUsed();
                int capacity = scope.getBytesReserved();

                System.out.printf("After allocation: used=%d, committed=%d, capacity=%d%n", usedMemory, commitedMemory,
                        capacity);
            }

            long usedMemory = heap.getUsage().getUsed();
            long commitedMemory = heap.getUsage().getCommitted();

            System.out.printf("After scope: used=%d, committed=%d%n", usedMemory, commitedMemory);
    }

}

           

代碼說明:

  • 定義了一個名為 MemoryManagementExample 的類,然後擷取 Java heap 記憶體池,并在 try-with-resources 語句中建立了一個名為 scope 的資源。
  • 然後,我們列印了記憶體使用率統計資訊,并在 scope 内部配置設定了一個 1MB 的位元組數組。我們使用 getBytesReserved() 方法擷取作用域中已保留的位元組數,并列印了記憶體使用情況和容量等資訊。
  • 最後,我們列印了作用域結束後記憶體的使用情況。

5.7 增強的Java集合庫

Java 17中增強了Java集合庫,包括新增了一些集合類型和對現有集合類型的改進。增強的Java集合庫可以提高開發人員的開發效率和代碼品質,進而減少出現錯誤的可能性。同時,增強的Java集合庫也可以提高應用程式的性能和響應速度,使得Java應用程式更加高效。

5.7.1 代碼示例

  1. of() 方法:建立一個不可變的集合
java複制代碼List<String> list = List.of("apple", "banana", "orange");
Set<Integer> set = Set.of(1, 2, 3, 4);
Map<String, Integer> map = Map.of("apple", 1, "banana", 2, "orange", 3);
           
  1. forEach() 方法:周遊集合
java複制代碼List<String> list = List.of("apple", "banana", "orange");
list.forEach(name -> System.out.println(name));
Set<Integer> set = Set.of(1, 2, 3, 4);
set.forEach(number -> System.out.println(number));

           
  1. Collectors類:提供了一系列的歸約操作
java複制代碼List<String> list = List.of("apple", "banana", "orange");
String joinedString = list.stream().collect(Collectors.joining("-", "[", "]"));
System.out.println(joinedString);

Map<String, Integer> map = Map.of("apple", 1, "banana", 2, "orange", 3);
Map<Integer, String> reversedMap = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
System.out.println(reversedMap);

           
  1. takeWhile() 方法和 dropWhile() 方法:根據條件截取集合
java複制代碼List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7);
List<Integer> takenList = list.stream().takeWhile(number -> number < 5).collect(Collectors.toList());
System.out.println(takenList);

List<Integer> dropedList = list.stream().dropWhile(number -> number < 5).collect(Collectors.toList());
System.out.println(dropedList);

           
  1. toArray(IntFunction<T[]>) 方法:傳回集合中的所有元素到一個新數組中
java複制代碼List<String> list = List.of("apple", "banana", "orange");
String[] array = list.toArray(String[]::new);
System.out.println(Arrays.toString(array));           

原文作者:陳書予

原文連結:https://juejin.cn/post/7239715560699052069

原文來源:稀土掘金