首頁>技術>

1. JDK

JDK 全稱 Java Development Kit,是 Java 開發環境。我們通常所說的 JDK 指的是 Java SE (Standard Edition) Development Kit。除此之外還有 Java EE(Enterprise Edition)和 Java ME(Micro Edition platforms)。

1.1 Java 的釋出週期

版本

釋出時間

名稱

JDK Beta

1995

WebRunner

JDK 1.0

1996.1

Oak

JDK 1.1

1997.2

J2SE 1.2

1998.12

playground

J2SE 1.3

2000.5

Kestrel

J2SE 1.4

2002.2

Merlin

J2SE 5.0

2004.9

Tiger

Java SE 6

2006.12

Mustang

Java SE 7

2011.7

Dolphin

Java SE 8(Lts)

2014.3

Java SE 9

2017.9

Java SE 10

2018.3

Java SE 11(Lts)

2018.9

Java SE 12

2019.3

Java SE 13

2019.9

Java SE 14

2020.3

Java SE 15

2020.9

1.2 Java發展過程中的重要節點

1995年: alpha 和 beta Java 公開版本釋出,取名為 WebRunner。

1996年: Java 第一個版本釋出,取名叫 Oak。但是第一個穩定版本是 JDK 1.0.2,被稱做 Java 1。

1998.12.8: 釋出了 J2SE 1.2。這個版本到 J2SE 5.0 更名為 Java 2。其中的 SE 指的是 Standard Edition,為了區別於 J2EE(Enterprise Edition)和 J2ME(Micro Edition)。

2000.5: 釋出了 J2SE 1.3,其中包含了 HotSpot JVM。而 HotSpot JVM 首次釋出是在 1999.4,名為 J2SE 1.2 JVM。

2004.9.30: 釋出了 J2SE 5.0。為什麼這個版本命名和前面幾個版本不一樣呢?這個版本原本計劃以 1.5 命名的,沿用以前的命名方式。但是為了更好的反映這個版本的成熟度,所以改名為 5.0。

這個版本以後,有了一個新的版本控制系統,5.0 用來表示產品版本,用來表示穩定的 J2SE 版本,而 1.5.0 用來表示開發者版本,也就是 Java 5.0 = JDK 1.5.0。

2006.12.11: J2SE 改名為 Java SE,版本號去掉了 .0。此後對應版本就是 Java 6 = JDK 1.6,Java 7 = JDK 1.7。

2011.7.7: 釋出 Java SE 7,是一個重大版本更新。更新了眾多特性。

2018.3: 釋出 Java SE 10。在此之前,Java 基本上是兩年一個版本,除了 Java SE 7 經過了五年,Java SE 8 經過了三年。在此之後,就是每六個月釋出一次新版本。但是不是每個版本都是 LTS(Long-Term-Support)。按照 Oracle 的計劃,每三年會有一個 LTS 版本。最近的 LTS 版本就是 Java SE 11 了。

2. Java SE 8後各個版本介紹2.1 Java SE 8

2.1.1. Lambda 和 函式式介面 Lambda 表示式相信不用再過多的介紹,終於在 Java 8 引入了,可以極大的減少程式碼量,程式碼看起來更清爽。函式式介面就是有且僅有一個抽象方法,但是可以有多個非抽象方法的介面。可以隱式轉化為 Lambda 表示式。我們定義一個函式式介面如下:

@FunctionalInterfaceinterface Operation {    int operation(int a, int b);}// 再定義一個 Class 用來操作 Operation 介面。class Test {    private int operate(int a, int b, Operation operation) {        return operation.operation(a, b);    }}Test test = new Test();// 在 Java 8 之前,我們想要實現 Operation 介面並傳給 Test.operate() 方法使用,我們要定義一個匿名類,實現 Operation 方法。test.operate(1, 2, new Operation() {    @Override    public int operation(int a, int b) {        return a + b;    }});// 而使用 Lambda 表示式,我們就可以這樣寫了:test.operate(1, 2, (a, b) -> a + b);複製程式碼
2.1.2 方法推導

透過方法引用,可以使用方法的名字來指向一個方法。使用一對冒號來引 "::" 用方法。還是以上面的例子來看,我們再新增幾個方法:

@FunctionalInterfaceinterface Operation {    int operation(int a, int b);}interface Creater<T> {    T get();}interface TestInt {    int cp(Test test1, Test test2);}class Test {    public static Test create(Creater<Test> creater) {        return creater.get();    }    private int operate(int a, int b, Operation operation) {        return operation.operation(a, b);    }    private static int add(int a, int b) {        return a + b;    }    private int sub(int a, int b) {        return a - b;    }    public int testM(Test test) {        return 0;    }    public void test(TestInt testInt) {        Test t1 = Test.create(Test::new);         Test t2 = Test.create(Test::new);        testInt.cp(t1, t2);    }}複製程式碼

那麼對應的方法引用有四種:

構造方法引用

使用方式:Class::new

Test test = Test.create(Test::new);複製程式碼
靜態方法引用

使用方式:Class::staticMethod

test.operate(1, 2, Test::add);複製程式碼
物件的例項方法引用

使用方式:instance::method

test.operate(1, 2, test::sub);複製程式碼
類的例項方法引用

使用方式:Class::method

test.test(Test::testM);複製程式碼

其實上面三種方法引用都好理解,最後類的例項方法引用,有兩個條件:

首先要滿足例項方法,而不是靜態方法Lambda 表示式的第一個引數會成為呼叫例項方法的物件

根據這兩點我們看上面的例子,test 方法接受一個 TestInt 例項,用 Lambda 表示式表示就是 (Test t1, Test t2) -> res,而我們呼叫 test 方法時傳入的方法引用是 Test::testM,其引數也是一個 Test 例項,最終 test.test(Test::testM) 的呼叫效果就是 t1.testM(t2)

2.1.3 介面預設方法和靜態方法

Java 8 新增了介面的預設實現,透過 default 關鍵字表示。同時也可以提供靜態預設方法。

public interface TestInterface {    String test();    // 介面預設方法    default String defaultTest() {        return "default";    }    static String staticTest() {        return "static";    }}複製程式碼
2.1.4 重複註解

Java 8 支援了重複註解。在 Java 8 之前想實現重複註解,需要用一些方法來繞過限制。比如下面的程式碼。

@interface Author {    String name();}@interface Authors {    Author[] value();}@Authors({@Author(name="a"), @Author(name = "b")})class Article {}複製程式碼

而在 Java 8 中,可以直接用下面的方式。

@Repeatable(Authors.class)@interface Author {    String name();}@interface Authors {    Author[] value();}@Author(name = "a")@Author(name = "b")class Article {}複製程式碼

在解析註解的時候,Java 8 也提供了新的 API。

AnnotatedElement.getAnnotationsByType(Class<T>)複製程式碼
2.1.5 型別註解

Java 8 之前註解只能用在宣告中,在 Java 8 中,註解可以使用在 任何地方。

@Author(name="a")private Object name = "";private String author = (@Author(name="a")String) name;複製程式碼
2.1.6 更好的型別推斷

Java 8 對於型別推斷做了改進。比如在 Java 7 中下面的寫法:

List<String> stringList = new ArrayList<>();stringList.add("A");stringList.addAll(Arrays.<String>asList());複製程式碼

在 Java 8 中改進後的寫法,可以自動做型別推斷。

List<String> stringList = new ArrayList<>();stringList.add("A");stringList.addAll(Arrays.asList());複製程式碼
2.1.7 Optional

Java 8 中新增了 Optional 類用來解決空指標異常。Optional 是一個可以儲存 null 的容器物件。透過 isPresent() 方法檢測值是否存在,透過 get() 方法返回物件。除此之外,Optional 還提供了很多其他有用的方法,具體可以檢視文件。下面是一些示例程式碼。

// 建立一個 String 型別的容器Optional<String> str = Optional.of("str");// 值是否存在boolean pre = str.isPresent();// 值如果存在就呼叫 println 方法,這裡傳入的是 println 的方法引用str.ifPresent(System.out::println);// 獲取值String res = str.get();// 傳入空值str = Optional.ofNullable(null);// 如果值存在,返回值,否則返回傳入的引數res = str.orElse("aa");str = Optional.of("str");// 如果有值,對其呼叫對映函式得到返回值,對返回值進行 Optional 包裝並返回res = str.map(s -> "aa" + s).get();// 返回一個帶有對映函式的 Optional 物件res = str.flatMap(s -> Optional.of(s + "bb")).flatMap(s -> Optional.of(s + "cc")).get();複製程式碼
2.1.8 Stream

Java 8 中新增的 Stream 類提供了一種新的資料處理方式。這種方式將元素集合看做一種流,在管道中傳輸,經過一系列處理節點,最終輸出結果。

關於 Stream 提供的具體方法,可以參照 API。下面是一些示例程式碼。

List<String> list = Arrays.asList("maa", "a", "ab", "c");list.stream()        .filter(s -> s.contains("a"))        .map(s -> s + "aa")        .sorted()        .forEach(System.out::println);System.out.println("####");list.parallelStream().forEach(System.out::println);List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);int res = numbers.stream().map(i -> i + 1).mapToInt(i -> i).summaryStatistics().getMax();System.out.println(res);複製程式碼
2.1.9 日期時間 API

Java 8 中新增了日期時間 API 用來加強對日期時間的處理,其中包括了 LocalDate,LocalTime,LocalDateTime,ZonedDateTime 等等,關於 API 可以參照官方文件以及這篇部落格,寫的很詳細。

www.cnblogs.com/muscleape/p…

下面是示例程式碼。

LocalDate now = LocalDate.now();System.out.println(now);System.out.println(now.getYear());System.out.println(now.getMonth());System.out.println(now.getDayOfMonth());LocalTime localTime = LocalTime.now();System.out.println(localTime);LocalDateTime localDateTime = now.atTime(localTime);System.out.println(localDateTime);複製程式碼
2.1.10 Base64 支援

Java 8 標準庫中提供了對 Base 64 編碼的支援。具體 API 見可參照文件。下面是示例程式碼。

String base64 = Base64.getEncoder().encodeToString("aaa".getBytes());System.out.println(base64);byte[] bytes = Base64.getDecoder().decode(base64);System.out.println(new String(bytes));複製程式碼
2.1.11 並行陣列 ParallelSort

Java 8 中提供了對陣列的並行操作,包括 parallelSort 等等,具體可參照 API。 Arrays.parallelSort(new int[] {1, 2, 3, 4, 5});

2.1.12 其他特性對併發的增強在java.util.concurrent.atomic包中還增加了下面這些類:

DoubleAccumulator DoubleAdder LongAccumulator LongAdder

提供了新的 Nashorn javascript 引擎提供了 jjs,是一個給予 Nashorn 的命令列工具,可以用來執行 JavaScript 原始碼提供了新的類依賴分析工具 jdepsJVM 的新特性

JVM記憶體永久區已經被metaspace替換(JEP 122)。JVM引數 -XX:PermSize 和 –XX:MaxPermSize被XX:MetaSpaceSize 和 -XX:MaxMetaspaceSize代替。 可以看到,Java 8 整體上的改進是很大的,最重要的是引入 Lambda 表示式,簡化程式碼。

其他一些改進可參照:

www.oracle.com/technetwork…

2.2 Java SE 92.2.1 Jigsaw 模組系統

在 Java 9 以前,打包和依賴都是基於 JAR 包進行的。JRE 中包含了 rt.jar,將近 63M,也就是說要執行一個簡單的 Hello World,也需要依賴這麼大的 jar 包。在 Java 9 中提出的模組化系統,對這點進行了改善。

關於模組化系統具體可以看看這篇文章。

zhuanlan.zhihu.com/p/24800180

2.2.2 JShell REPL

Java 9 提供了互動式直譯器。有了 JShell 以後,Java 終於可以像 Python,Node.js 一樣在 Shell 中執行一些程式碼並直接得出結果了。

2.2.3 私有介面方法,介面中使用私有方法

Java 9 中可以在介面中定義私有方法。示例程式碼如下:

public interface TestInterface {    String test();    // 介面預設方法    default String defaultTest() {        pmethod();        return "default";    }    private String pmethod() {        System.out.println("private method in interface");        return "private";    }}複製程式碼
2.2.4 集合不可變例項工廠方法

在以前,我們想要建立一個不可變的集合,需要先建立一個可變集合,然後使用 unmodifiableSet 建立不可變集合。程式碼如下:

Set<String> set = new HashSet<>();set.add("A");set.add("B");set.add("C");set = Collections.unmodifiableSet(set);System.out.println(set);複製程式碼

Java 9 中提供了新的 API 用來建立不可變集合。

List<String> list = List.of("A", "B", "C");Set<String> set = Set.of("A", "B", "C");Map<String, String> map = Map.of("KA", "VA", "KB", "VB");複製程式碼
2.2.5 改進 try-with-resources

Java 9 中不需要在 try 中額外定義一個變數。Java 9 之前需要這樣使用 try-with-resources:

InputStream inputStream = new StringBufferInputStream("a");try (InputStream in = inputStream) {    in.read();} catch (IOException e) {    e.printStackTrace();}複製程式碼

在 Java 9 中可以直接使用 inputStream 變數,不需要再額外定義新的變量了。

InputStream inputStream = new StringBufferInputStream("a");try (inputStream) {    inputStream.read();} catch (IOException e) {    e.printStackTrace();}複製程式碼
2.2.6 多版本相容 jar 包

Java 9 中支援在同一個 JAR 中維護不同版本的 Java 類和資源。

2.2.7 增強了 Stream,Optional,Process API2.2.8 新增 HTTP2 Client2.2.9 增強 Javadoc,增加了 HTML 5 文件的輸出,並且增加了搜尋功能2.2.10 增強 @Deprecated

對 Deprecated 新增了 since 和 forRemoval 屬性

2.2.11 增強了鑽石運算子 "<>",可以在 匿名內部類中使用了。

在 Java 9 之前,內部匿名類需要指定泛型型別,如下:

Handler<? extends Number> intHandler1 = new Handler<Number>(2) {}複製程式碼

而在 Java 9 中,可以自動做型別推導,如下:

Handler<? extends Number> intHandler1 = new Handler<>(2) {}複製程式碼
2.2.12 多解析度影象 API:定義多解析度影象API,開發者可以很容易的操作和展示不同解析度的影象了。2.2.13 改進的 CompletableFuture API

CompletableFuture 類的非同步機制可以在 ProcessHandle.onExit 方法退出時執行操作。 其他一些改進可參照:

docs.oracle.com/javase/9/wh…

2.3 Java SE 102.3.1 新增區域性型別推斷 var
var a = "aa";System.out.println(a);複製程式碼

var 關鍵字目前只能用於區域性變數以及 for 迴圈變數宣告中。

2.3.3 統一的垃圾回收介面,改進了 GC 和其他內務管理

其他特性

ThreadLocal 握手互動 JDK 10 引入一種線上程上執行回撥的新方法,很方便的停止單個執行緒而不是停止全部執行緒或者一個都不停。

基於Java的實驗性JIT編譯器 Java 10 開啟了 Java JIT編譯器 Graal,用作Linux / x64平臺上的實驗性JIT編譯器。

提供預設的 CA 根證書

將 JDK 生態整合到單個倉庫 此JEP的主要目標是執行一些記憶體管理,並將JDK生態的眾多儲存庫組合到一個儲存庫中。

其他一些改進可以參照:

www.oracle.com/technetwork…

2.4 Java SE 112.4.1 Lambda 中使用 var
(var x, var y) -> x.process(y)複製程式碼
2.4.2 字串 API 增強

Java 11 新增了 一系列字串處理方法,例如:

// 判斷字串是否為空白" ".isBlank(); " Javastack ".stripTrailing();  // " Javastack"" Javastack ".stripLeading();   // "Javastack "複製程式碼
2.4.3 標準化 HttpClient API2.4.4 java 直接編譯並執行,省去先 javac 編譯生成 class 再執行的步驟2.4.5 增加對 TLS 1.3 的支援2.4.6 添加了新的垃圾收集器ZGC,不過只是實驗性引入

Z垃圾收集器(也稱為ZGC)是可伸縮的低延遲垃圾收集器(JEP 333)。它旨在滿足以下目標:

暫停時間不超過10毫秒暫停時間不會隨著堆或活動集的大小而增加處理大小從幾百兆位元組到幾兆兆位元組的堆

ZGC的核心是併發的垃圾收集器,這意味著在Java執行緒繼續執行的同時,所有繁重的工作(標記,壓縮,參考處理,字串表清理等)都已完成。這極大地限制了垃圾回收對應用程式響應時間的負面影響。 ZGC的實驗版本具有以下限制:

僅在Linux / x64上可用。不支援使用壓縮的oop和/或壓縮的類點。在-XX:+UseCompressedOops和-XX:+UseCompressedClassPointers選項預設為禁用。啟用它們將無效。不支援類解除安裝。在-XX:+ClassUnloading和-XX:+ClassUnloadingWithConcurrentMark選項預設為禁用。啟用它們將無效。不支援將ZGC與Graal結合使用。

其他一些改進可以參照:

www.oracle.com/technetwork…

2.5 Java SE 122.5.1 switch 表示式

Java 12 以後,switch 不僅可以作為語句,也可以作為表示式。

private String switchTest(int i) {    return switch (i) {        case 1 -> "1";        default -> "0";    };}複製程式碼

其他一些改進可以參照:

www.oracle.com/technetwork…

2.6 Java SE 132.6.1 使用歸檔方式啟動

在使用歸檔檔案啟動時,JVM 將歸檔檔案對映到其對應的記憶體中,其中包含所需的大多數類,而需要使用多麼複雜的類載入機制。甚至可以在併發執行的 JVM 例項之間共享記憶體區域,透過這種方式可以釋放需要在每個 JVM 例項中建立相同資訊時浪費的記憶體,從而節省了記憶體空間。 在 Java 12 中,預設開啟了對 JDK 自帶 JAR 包類的存檔,如果想關閉對自帶類庫的存檔,可以在啟動引數中加上:

-Xshare:off複製程式碼

而在 Java 13 中,可以不用提供歸檔類列表,而是透過更簡潔的方式來建立包含應用程式類的歸檔。具體可以使用引數 -XX:ArchiveClassesAtExit 來控制應用程式在退出時生成存檔,也可以使用 -XX:SharedArchiveFile 來使用動態存檔功能,詳細使用見如下示例。 建立存檔檔案示例

$ java -XX:ArchiveClassesAtExit=helloworld.jsa -cp helloworld.jar Hello複製程式碼

使用存檔檔案示例

$ java -XX:SharedArchiveFile=hello.jsa -cp helloworld.jar Hello複製程式碼

上述就是在 Java 應用程式執行結束時動態進行類歸檔,並且在 Java 10 的基礎上,將多條命令進行了簡化,可以更加方便地使用類歸檔功能。

2.6.2 Switch 表示式擴充套件(預覽功能)

在 Java 12 中引入了 Switch 表示式作為預覽特性,而在 Java 13 中對 Switch 表示式做了增強改進,在塊中引入了 yield 語句來返回值,而不是使用 break。這意味著,Switch 表示式(返回值)應該使用 yield,而 Switch 語句(不返回值)應該使用 break,而在此之前,想要在 Switch 中返回內容,還是比較麻煩的,只不過目前還處於預覽狀態。 在 Java 13 之後,Switch 表示式中就多了一個關鍵字用於跳出 Switch 塊的關鍵字 yield,主要用於返回一個值,它和 return 的區別在於:return 會直接跳出當前迴圈或者方法,而 yield 只會跳出當前 Switch 塊,同時在使用 yield 時,需要有 default 條件。 在 Java 12 之前,傳統 Switch 語句寫法為:傳統形式

private static String getText(int number) {    String result = "";    switch (number) {        case 1, 2:        result = "one or two";        break;        case 3:        result = "three";        break;        case 4, 5, 6:        result = "four or five or six";        break;        default:        result = "unknown";        break;    };    return result;}複製程式碼

在 Java 12 之後,關於 Switch 表示式的寫法改進為如下:標籤簡化形式

private static String getText(int number) {    String result = switch (number) {        case 1, 2 -> "one or two";        case 3 -> "three";        case 4, 5, 6 -> "four or five or six";        default -> "unknown";    };    return result;}複製程式碼

而在 Java 13 中,,value break 語句不再被編譯,而是用 yield 來進行值返回,上述寫法被改為如下寫法: yield 返回值形式

private static String getText(int number) {    return switch (number) {        case 1, 2:            yield "one or two";        case 3:            yield "three";        case 4, 5, 6:            yield "four or five or six";        default:            yield "unknown";    };}複製程式碼
2.6.3 文字塊(預覽功能)

一直以來,Java 語言在定義字串的方式是有限的,字串需要以雙引號開頭,以雙引號結尾,這導致字串不能夠多行使用,而是需要透過換行轉義或者換行連線符等方式來變通支援多行,但這樣會增加編輯工作量,同時也會導致所在程式碼段難以閱讀、難以維護。

Java 13 引入了文字塊來解決多行文字的問題,文字塊以三重雙引號開頭,並以同樣的以三重雙引號結尾終止,它們之間的任何內容都被解釋為字串的一部分,包括換行符,避免了對大多數轉義序列的需要,並且它仍然是普通的 java.lang.String 物件,文字塊可以在 Java 中可以使用字串文字的任何地方使用,而與編譯後的程式碼沒有區別,還增強了 Java 程式中的字串可讀性。並且透過這種方式,可以更直觀地表示字串,可以支援跨越多行,而且不會出現轉義的視覺混亂,將可以廣泛提高 Java 類程式的可讀性和可寫性。

在 Java 13 之前,多行字串寫法為:多行字串寫法

String html ="<html>\n" +              "   <body>\n" +              "      <p>Hello, World</p>\n" +              "   </body>\n" +              "</html>\n";                 String json ="{\n" +              "   \"name\":\"mkyong\",\n" +              "   \"age\":38\n" +              "}\n";複製程式碼

在 Java 13 引入文字塊之後,寫法為:多行文字塊寫法

String html = """                <html>                    <body>                        <p>Hello, World</p>                    </body>                </html>                """;  String json = """                {                    "name":"mkyong",                    "age":38                }                """;複製程式碼

文字塊是作為預覽功能引入到 Java 13 中的,這意味著它們不包含在相關的 Java 語言規範中,這樣做的好處是方便使用者測試功能並提供反饋,後續更新可以根據反饋來改進功能,或者必要時甚至刪除該功能,如果該功能立即成為 Java SE

準的一部分,則進行更改將變得更加困難。重要的是要意識到預覽功能不是 beta 形式。

由於預覽功能不是規範的一部分,因此有必要為編譯和執行時明確啟用它們。需要使用下面兩個命令列引數來啟用預覽功能:清單 8. 啟用預覽功能

$ javac --enable-preview --release 13 Example.java$ java --enable-preview Example複製程式碼
2.7 Java SE 142.7.1 instanceof模式匹配

通常情況下我們使用instanceof來探測類的真實型別,如果符合該型別,則可進行強制轉換。在Java14之前,我們通常的寫法如下:

2.7.2 記錄型別(Record Type)的引入

Java 14中記錄型別(Record Type)作為預覽特性被引入。記錄物件允許使用緊湊的語法來宣告類,和列舉型別一樣,記錄也是類的一種受限形式。在idea 2020.1中,建立Record與建立類和列舉一樣,可以在建立時直接選擇對應的型別。定義一個Record型別如下:

public record Point(int x, int y) {}複製程式碼

使用Record操作如下:

Point point = new Point(1,3);System.out.println(point.x());System.out.println(point.y());複製程式碼

對Record類進行反編譯我們會看到如下內容:

public final class Point extends java.lang.Record {    private final int x;    private final int y;    public Point(int x, int y) { /* compiled code */ }    public java.lang.String toString() { /* compiled code */ }    public final int hashCode() { /* compiled code */ }    public final boolean equals(java.lang.Object o) { /* compiled code */ }    public int x() { /* compiled code */ }    public int y() { /* compiled code */ }}複製程式碼

文章來源:https://juejin.cn/post/6930771509900214286

16
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 翻譯:《實用的Python程式設計》01_06_Files