【這是 ZY 第 11 篇原創技術文章】javascript
某天在網上閒逛,忽然看到有篇介紹 Java 11 新特性的文章,頓時內心一驚,畢竟我對於 Java 的版本認識還停留在 Java 8 上,而平常使用的語法和 API 還停留在 Java 7 上。因而抽時間看了看 Java 8 之後各個版本的特性,作了一個總結。html
JDK 全稱 Java Development Kit,是 Java 開發環境。咱們一般所說的 JDK 指的是 Java SE (Standard Edition) Development Kit。除此以外還有 Java EE(Enterprise Edition)和 Java ME(Micro Edition platforms)。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 (18.3) | 2018.3 | |
Java SE 11 (18.9 LTS) | 2018.9 | |
Java SE 12 (19.3) | 2019.3 | |
Java SE 13 (19.9) | 2019.9 |
下面咱們看一些 Java 發展過程當中重要的節點。
1995 年 alpha 和 beta Java 公開版本發佈,取名爲 WebRunner。android
1996.1.23 Java 第一個版本發佈,取名叫 Oak。可是第一個穩定版本是 JDK 1.0.2,被稱作 Java 1。git
1998.12.8 發佈了 J2SE 1.2。這個版本到 J2SE 5.0 改名爲 Java 2。其中的 SE 指的是 Standard Edition,爲了區別於 J2EE(Enterprise Edition)和 J2ME(Micro Edition)。api
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。bash
2006.12.11,J2SE 更名爲 Java SE,版本號去掉了 .0。此後對應版本就是 Java 6 = JDK 1.6,Java 7 = JDK 1.7。併發
2011.7.7. 發佈 Java SE 7,是一個重大版本更新。更新了衆多特性。oracle
2018.3 發佈 Java SE 10。在此以前,Java 基本上是兩年一個版本,除了 Java SE 7 通過了五年,Java SE 8 通過了三年。在此以後,就是每六個月發佈一次新版本。可是不是每一個版本都是 LTS(Long-Term-Support)。按照 Oracle 的計劃,每三年會有一個 LTS 版本。最近的 LTS 版本就是 Java SE 11 了。
OpenJDK 是 在 2007 年由 Sun Corporation(如今的Oracle Corporation) 發佈的。是 Oracle JDK 的開源實現版本,以 GPL 協議發佈。在 JDK 7 的時候,Sub JDK 就是在 Open JDK 7 的基礎上發佈的,只替換了少許的源碼。在 Sun 公司被 Oracle 收購之後,Sun SDK 就被稱爲 Oracle JDK。Oracle JDK 是基於 Oracle Binary COde License Agreement 協議。 二者的區別以下:
提及 Android 和 OpenJDK 的歷史淵源,仍是略微複雜。 簡單來講,Android 最開始使用的 Java 是基於 Apache 協議發佈的 Harmony,後來因爲 Harmony 自己的限制和 Oracle 公司的起訴,從 Android N 之後, Google 開始使用 OpenJDK。 而後咱們再稍微展開聊聊。
Sun 公司最初開發了 Java 語言,同時也開發了 JVM,而且定義了 JVM 規範。這個咱們比較清楚,只要基於 JVM 規範開發本身的語言,就能夠運行在 JVM 上。可是依照規範開發了語言以後,須要經過 Sun 的 TCK(Technology Compatibility Kit)測試,以後才能成爲官方承認的 JVM 語言。
基於 JVM 規範,Apache 開發了一個開源免費的 Java 實現 Harmony,而且根據 Apache License v2 發佈。可是 Sun 公司並無給 Harmony TCK 許可。
在 2009.4.15 Sun 公司發佈了 OpenJDK,基於 GNU GPL 發佈。同時 Sun 公司規定只有衍生自 OpenJDK 採用的 GPL 協議的開源實現才能運行 OpenJDK 的 TCK。以後 Oracle 收購 Sun 公司之後接管了 OpenJDK。 因爲 Apache 的 Harmony 是 Apache 協議,與 OpenJDK 的 GPL 協議不兼容,因此 Harmony 一直沒有獲得 TCK 受權。
Android 最開始是採用了 Harmony 做爲本身的 Java 類庫,由於 Harmony 使用的 Apache 協議更自由。而因爲 Harmony 沒有經過 TCK 認證,也爲後來 Oracle 起訴 Google 埋下伏筆。
後來 Oracle 起訴 Google 主要集中在兩點,一是 Oracle 認爲 Google 代碼中使用了 Java 的 37 個 API,二是 Sun 公司前員工在跳槽後爲 Android 項目開發時,直接複製了 OpenJDK 中的九行代碼,而 Android 項目並無按照 GPL 協議受權,因此複製 OpenJDK 代碼是沒有經過 GPL 受權的。
因此到後來爲了解決專利的問題,Android N 之後,Android 開始使用 OpenJDK 替換 Harmony。
以上 Android 和 JDK 參考資料:
juejin.im/entry/5abc5…
zh.wikipedia.org/zh/Android#…
gudong.name/2019/04/05/…
聊了一些關於 Java 的歷史,下面咱們看看各個 Java 版本有那些新特性。這裏只列出了對開發者影響比較大的一些特性~
Lambda 表達式相信不用再過多的介紹,終於在 Java 8 引入了,能夠極大的減小代碼量,代碼看起來更清爽。
函數式接口就是有且僅有一個抽象方法,可是能夠有多個非抽象方法的接口。能夠隱式轉化爲 Lambda 表達式。 咱們定義一個函數式接口以下:
@FunctionalInterface
interface 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);
複製代碼
經過方法引用,可使用方法的名字來指向一個方法。使用一對冒號來引 "::" 用方法。 仍是以上面的例子來看,咱們再添加幾個方法:
@FunctionalInterface
interface 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);
複製代碼
其實上面三種方法引用都好理解,最後類的實例方法引用,有兩個條件:
Java 8 新增了接口的默認實現,經過 default 關鍵字表示。同時也能夠提供靜態默認方法。
public interface TestInterface {
String test();
// 接口默認方法
default String defaultTest() {
return "default";
}
static String staticTest() {
return "static";
}
}
複製代碼
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>)
複製代碼
Java 8 以前註解只能用在聲明中,在 Java 8 中,註解可使用在 任何地方。
@Author(name="a")
private Object name = "";
private String author = (@Author(name="a")String) name;
複製代碼
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());
複製代碼
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();
複製代碼
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);
複製代碼
Java 8 中新增了日期時間 API 用來增強對日期時間的處理,其中包括了 LocalDate,LocalTime,LocalDateTime,ZonedDateTime 等等,關於 API 能夠參照官方文檔以及這篇博客,寫的很詳細。下面是示例代碼。
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);
複製代碼
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));
複製代碼
Java 8 中提供了對數組的並行操做,包括 parallelSort 等等,具體可參照 API。
Arrays.parallelSort(new int[] {1, 2, 3, 4, 5});
複製代碼
能夠看到,Java 8 總體上的改進是很大的,最重要的是引入 Lambda 表達式,簡化代碼。
其餘一些改進可參照 www.oracle.com/technetwork…
在 Java 9 之前,打包和依賴都是基於 JAR 包進行的。JRE 中包含了 rt.jar,將近 63M,也就是說要運行一個簡單的 Hello World,也須要依賴這麼大的 jar 包。在 Java 9 中提出的模塊化系統,對這點進行了改善。 關於模塊化系統具體能夠看看這篇文章。
Java 9 提供了交互式解釋器。有了 JShell 之後,Java 終於能夠像 Python,Node.js 同樣在 Shell 中運行一些代碼並直接得出結果了。
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";
}
}
複製代碼
在之前,咱們想要建立一個不可變的集合,須要先建立一個可變集合,而後使用 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");
複製代碼
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();
}
複製代碼
Java 9 中支持在同一個 JAR 中維護不一樣版本的 Java 類和資源。
對 Deprecated 新增了 since 和 forRemoval 屬性
在 Java 9 以前,內部匿名類須要指定泛型類型,以下:
Handler<? extends Number> intHandler1 = new Handler<Number>(2) {
}
複製代碼
而在 Java 9 中,能夠自動作類型推導,以下:
Handler<? extends Number> intHandler1 = new Handler<>(2) {
}
複製代碼
CompletableFuture 類的異步機制能夠在 ProcessHandle.onExit 方法退出時執行操做。
其餘一些改進可參照 docs.oracle.com/javase/9/wh…
var a = "aa";
System.out.println(a);
複製代碼
var 關鍵字目前只能用於局部變量以及 for 循環變量聲明中。
從JDK中移除了 javah 工具,使用 javac -h 代替。
ThreadLocal 握手交互
JDK 10 引入一種在線程上執行回調的新方法,很方便的中止單個線程而不是中止所有線程或者一個都不停。
基於Java的實驗性JIT編譯器
Java 10 開啓了 Java JIT編譯器 Graal,用做Linux / x64平臺上的實驗性JIT編譯器。
提供默認的 CA 根證書
將 JDK 生態整合到單個倉庫
此JEP的主要目標是執行一些內存管理,並將JDK生態的衆多存儲庫組合到一個存儲庫中。
其餘一些改進能夠參照 www.oracle.com/technetwork…
(var x, var y) -> x.process(y)
複製代碼
Java 11 新增了 一系列字符串處理方法,例如:
// 判斷字符串是否爲空白
" ".isBlank();
" Javastack ".stripTrailing(); // " Javastack"
" Javastack ".stripLeading(); // "Javastack "
複製代碼
其餘一些改進能夠參照 www.oracle.com/technetwork…
Java 12 之後,switch 不只能夠做爲語句,也能夠做爲表達式。
private String switchTest(int i) {
return switch (i) {
case 1 -> "1";
default -> "0";
};
}
複製代碼
其餘一些改進能夠參照 www.oracle.com/technetwork…
Java8
ifeve.com/java-8-feat…
juejin.im/post/5ae6bf…
wizardforcel.gitbooks.io/java8-tutor…
www.oracle.com/technetwork…
www.oracle.com/technetwork…
Java9
www.runoob.com/java/java9-…
docs.oracle.com/javase/9/wh…
www.twle.cn/c/yufei/jav…
Java10
baijiahao.baidu.com/s?id=159443…
blog.csdn.net/visant/arti…
www.oracle.com/technetwork…
Java 11
openjdk.java.net/projects/jd…
www.ctolib.com/topics-1351…
Java 12
zhuanlan.zhihu.com/p/59798800
其餘
www.baeldung.com/oracle-jdk-…
zh.wikipedia.org/wiki/Java版本…
www.zhihu.com/question/19…
juejin.im/post/5ca1c7…
gudong.name/2019/04/05/…