使用了 23 年的 Java 不再免費(fèi)!

編程語(yǔ)言界的扛把子 Java,不僅搭載上了如火箭版的更新速度,現(xiàn)在還有意讓上車(chē)的用戶(hù)付費(fèi)買(mǎi)票了,那么身為 Java 開(kāi)發(fā)者,下一步究竟是該棄用 23 年的老牌工具 JDK 還是乖乖付費(fèi)繼續(xù)搭載這列車(chē)?



上個(gè)月,Java 開(kāi)發(fā)工具包(JDK)11剛剛到來(lái),JDK 12 就在緊密敲鑼籌備中,隨著消息接連不斷地爆出,不少使用 Java 的開(kāi)發(fā)者開(kāi)始有種一年一萬(wàn)個(gè)更新版本的錯(cuò)覺(jué),而當(dāng)面對(duì)厚厚的一堆堆 Java 8/9/10/11 的入門(mén)書(shū)籍和教程時(shí),就問(wèn)你怕不怕?不僅如此,就在 Java 早已在移動(dòng) App、服務(wù)器應(yīng)用、Web 開(kāi)發(fā)、J2EE 企業(yè)級(jí)應(yīng)用和嵌入式等領(lǐng)域根深蒂固時(shí),Oracle 于近日最新發(fā)布的一紙 Java 用戶(hù)使用協(xié)議轟動(dòng)了整個(gè)業(yè)界,因?yàn)?Java 將收費(fèi)了!

1.JDK 11 不容錯(cuò)過(guò)的那些新特性

JDK 11 作為 Oracle 以六個(gè)月為更新周期之后公開(kāi)發(fā)布的第一個(gè)長(zhǎng)期支持版本,其中還是有許多實(shí)用的功能特性。

局部變量推斷

Java 10 引入了新的關(guān)鍵字 var,可以在定義局部變量時(shí)代替類(lèi)型信息(局部指的是在方法體內(nèi)部的變量定義)。

在 Java 10 之前,局部變量必須這樣定義:

String text = "Hello Java 9";

而現(xiàn)在可以用 var 代替 String。編譯器會(huì)根據(jù)變量的賦值推斷出正確的類(lèi)型。因此,text 的類(lèi)型是 String:

var text = "Hello Java 10";

使用 var 定義的變量依然是靜態(tài)類(lèi)型。這種變量不能重新用不兼容的類(lèi)型賦值。比如,下面的代碼無(wú)法通過(guò)編譯:

var text = "Hello Java 11";

text = 23; // Incompatible types

還可以通過(guò)同時(shí)使用 var 和 final 來(lái)禁止變量的重新賦值:

final var text = "Banana";

text = "Joe"; // Cannot assign a value to final variable 'text'

而且,當(dāng)編譯器無(wú)法推斷出正確類(lèi)型時(shí)也不允許使用 var:

// Cannot infer type:

var a;

var nothing = null;

var lambda = () -> System.out.println("Pity!");

var method = this::someMethod;

局部變量類(lèi)型的推斷在泛型中非常有用。下面的例子中,current 有個(gè)非常復(fù)雜的類(lèi)型 Map<String, List<Integer>>,而這個(gè)類(lèi)型可以簡(jiǎn)化成一個(gè) var 關(guān)鍵字,節(jié)省了很多敲代碼的時(shí)間:

var myList = new ArrayList<Map<String, List<Integer>>>();

for (var current : myList) {

// current is infered to type: Map<String, List<Integer>>

System.out.println(current);

}

由于 Java 11 的 var 關(guān)鍵字也可以在 lambda 的參數(shù)上使用,因此可以給參數(shù)加注解:

Predicate<String> predicate = (@Nullable var a) -> true;

小提示:Intellij IDEA 中可以按住 CMD/CTRL 鍵并將鼠標(biāo)懸停在變量上來(lái)查看推斷出的類(lèi)型(鍵盤(pán)快捷鍵為Ctrl+J)。

HTTP 客戶(hù)端

Java 9 引入了新的 HttpClient API 來(lái)處理 HTTP 請(qǐng)求。在 Java 11 中,這個(gè) API 已穩(wěn)定,可以通過(guò) java.net 包使用。我們來(lái)看看這個(gè) API 能干什么。

新的 HttpClient 支持同步和異步方式。同步請(qǐng)求會(huì)阻塞當(dāng)前線(xiàn)程直到響應(yīng)返回。BodyHandlers 定義了期待的響應(yīng)體的類(lèi)型(如字符串、字節(jié)數(shù)組或文件):

var request = HttpRequest.newBuilder()

.uri(URI.create("https://winterbe.com"))

.GET()

.build();

var client = HttpClient.newHttpClient();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

System.out.println(response.body());

同樣的請(qǐng)求也可以異步進(jìn)行。調(diào)用 sendAsync 不會(huì)阻塞當(dāng)前線(xiàn)程,它會(huì)返回一個(gè) CompletableFuture 用來(lái)構(gòu)建異步操作管線(xiàn)。

var request = HttpRequest.newBuilder()

.uri(URI.create("https://winterbe.com"))

.build();

var client = HttpClient.newHttpClient();

client.sendAsync(request, HttpResponse.BodyHandlers.ofString())

.thenApply(HttpResponse::body)

.thenAccept(System.out::println);

小提示:可以省略 .GET() 調(diào)用,因?yàn)樗悄J(rèn)的。

下面這個(gè)例子用 POST 方式將數(shù)據(jù)發(fā)送到給定的 URL。與 BodyHandlers 類(lèi)似,這里使用 BodyPublishers 來(lái)定義請(qǐng)求體中要發(fā)送的數(shù)據(jù)類(lèi)型,如字符串、字節(jié)數(shù)組、文件或輸入流:

var request = HttpRequest.newBuilder()

.uri(URI.create("https://postman-echo.com/post"))

.header("Content-Type", "text/plain")

.POST(HttpRequest.BodyPublishers.ofString("Hi there!"))

.build();

var client = HttpClient.newHttpClient();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());

System.out.println(response.statusCode()); // 200

下面的例子演示了通過(guò) BASIC-AUTH 方式進(jìn)行認(rèn)證的方法:

var request = HttpRequest.newBuilder()

.uri(URI.create("https://postman-echo.com/basic-auth"))

.build();

var client = HttpClient.newBuilder()

.authenticator(new Authenticator() {

@Override

protected PasswordAuthentication getPasswordAuthentication() {

return new PasswordAuthentication("postman", "password".toCharArray());

}

})

.build();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());

System.out.println(response.statusCode()); // 200

集合

像 List、Set 和 Map 等集合類(lèi)都加入了新的方法。List.of 會(huì)根據(jù)給定的參數(shù)創(chuàng)建一個(gè)新的不可變的 list。List.copyOf 能創(chuàng)建 list 的不可變副本。

var list = List.of("A", "B", "C");

var copy = List.copyOf(list);

System.out.println(list == copy); // true

因?yàn)?list 已經(jīng)是不可變的了,因此不需要實(shí)際創(chuàng)建 list 實(shí)例的副本,因此 list 和 copy 會(huì)指向同一個(gè)副本。但如果賦值一個(gè)可變的列表,copy 就會(huì)生成一個(gè)新的實(shí)例,以保證修改原始列表時(shí)不會(huì)產(chǎn)生副作用:

var list = new ArrayList<String>();

var copy = List.copyOf(list);

System.out.println(list == copy); // false

在創(chuàng)建不可變的 map 時(shí),不需要自行創(chuàng)建 map 的內(nèi)容,只需要傳遞鍵和值即可:

var map = Map.of("A", 1, "B", 2);

System.out.println(map); // {B=2, A=1}

Java 11 中的不可變集合依然使用與原來(lái)的集合 API 同樣的接口。但如果試圖通過(guò)添加或刪除元素的方式改變不可變集合,則會(huì)發(fā)生 java.lang.UnsupportedOperationException 異常。好在 Intellij IDEA 會(huì)在你試圖改變不可變集合時(shí)發(fā)出警告。

Java 8 引入了 流的概念,現(xiàn)在它有三個(gè)新的方法。Stream.ofNullable 能從單個(gè)元素構(gòu)建一個(gè)流:

Stream.ofNullable(null)

.count() // 0

dropWhile 和 takeWhile 兩個(gè)方法都能接受 predicate 對(duì)象,從而可以?huà)仐壛髦械囊恍┰兀?/p>

Stream.of(1, 2, 3, 2, 1)

.dropWhile(n -> n < 3)

.collect(Collectors.toList()); // [3, 2, 1]

Stream.of(1, 2, 3, 2, 1)

.takeWhile(n -> n < 3)

.collect(Collectors.toList()); // [1, 2]

Optional

Optional 也增加了幾個(gè)新方法,比如現(xiàn)在可以很容易將 optional 轉(zhuǎn)換成流,或者給空的 optional 提供另一個(gè) optional 作為出錯(cuò)時(shí)的備選方案:

Optional.of("foo").orElseThrow(); // foo

Optional.of("foo").stream().count(); // 1

Optional.ofNullable(null)

.or(() -> Optional.of("fallback"))

.get(); // fallback

字符串

最基礎(chǔ)的類(lèi)之一 String 也加了幾個(gè)輔助方法用來(lái)去除空白、檢查空白,以及以流的方式輸出字符串:

" ".isBlank(); // true

" Foo Bar ".strip(); // "Foo Bar"

" Foo Bar ".stripTrailing(); // " Foo Bar"

" Foo Bar ".stripLeading(); // "Foo Bar "

"Java".repeat(3); // "JavaJavaJava"

"A

B

C".lines().count(); // 3

其他 JVM 特性

在我看來(lái),上述這些是 Java 11 與 8 相比時(shí)最有意思的語(yǔ)言 API 特性,不過(guò)新的特性還有許多,比如下面這些:

用于響應(yīng)式編程的流式 API

Java 模塊系統(tǒng)

應(yīng)用程序類(lèi)數(shù)據(jù)共享

動(dòng)態(tài)類(lèi)-文件常量

Java REPL(JShell)

飛行記錄器

Unicode 10

G1:完全并行的垃圾回收器

ZGC:可擴(kuò)展的低延遲垃圾回收器

Epsilon:No-Op垃圾回收器

不推薦使用的Nashorn JavaScript引擎

……

2.學(xué)不動(dòng)的 Java,還要收費(fèi)了?

對(duì)于 Java 新版本,不少開(kāi)發(fā)者望塵莫及,紛紛表示不要再更新了,我的項(xiàng)目還停留在 Java 8 呢。話(huà)雖如此,事實(shí)上 Oracle 曾在今年四月就宣布,自 2019 年 1 月起,Java SE 8 公開(kāi)更新將不向沒(méi)有商用許可證的業(yè)務(wù)、商用或生產(chǎn)用途提供。即未來(lái)開(kāi)發(fā)者還想使用 JDK 8,Oracle 將不會(huì)提供免費(fèi)的技術(shù)支持,需要另外收費(fèi)。所以總體而言還是建議開(kāi)發(fā)者應(yīng)該轉(zhuǎn)換到最新版的 Java 11。


但就在這時(shí),據(jù)國(guó)外網(wǎng)友@Stephen Colebourne 發(fā)布的一篇名為《Oracle's Java 11 trap - Use OpenJDK instead!》博文,我們才注意到,在 Java 11 中,Oracle 悄然更新了用戶(hù)使用協(xié)議(https://www.oracle.com/technetwork/java/javase/terms/license/javase-license.html):


簡(jiǎn)而言之:

新版 Oracle JDK 不可以用在數(shù)據(jù)處理、商業(yè)、產(chǎn)品、或者內(nèi)部商業(yè)用途(需要購(gòu)買(mǎi) License),僅可免費(fèi)用于開(kāi)發(fā)、測(cè)試、原型、演示。


正是這一修改意味著免費(fèi)使用 23 年的 Java 即將走上收費(fèi)的路子。倘若開(kāi)發(fā)者還是如往常那般下載 Oracle JDK,并將其投入商業(yè)項(xiàng)目中使用,后續(xù)可能會(huì)帶來(lái)不小的商業(yè)糾紛。

3.Oracle JDK 收費(fèi)了,企業(yè)和開(kāi)發(fā)者怎么辦?

提及 Java 的商業(yè)糾紛,我們不禁想起此前轟動(dòng)一時(shí)且長(zhǎng)達(dá)八年的 Oracle 與 Google 關(guān)于 Java 的侵權(quán)案:

1995 年,Sun 公司發(fā)布了 Java;

這其中需要介紹到 2006 年,Sun 公司開(kāi)源了其 Java 項(xiàng)目——OpenJDK,但是并未開(kāi)源此前的 Java 項(xiàng)目 SunJDK(現(xiàn)在是 OracleJDK);

2009 年,Oracle 以 74 億美元收購(gòu) Sun 從而獲得了 Java 的版權(quán),而其中也包含了 Sun 研發(fā)的 Java 商業(yè)項(xiàng)目版權(quán);

2010 年 8 月,Oracle 認(rèn)為 Google Android 系統(tǒng)抄襲了 37 個(gè) Java API 代碼段,而這些代碼屬于 Oracle 商業(yè)私有 JDK(OracleJDK)的一部分,于是將 Google 訴訟至法庭,要求賠償 26 億美元;

經(jīng)過(guò) 8 年的調(diào)解及上訴,這一案最終于今年 3 月,美國(guó)聯(lián)邦巡回法院判 Google 向 Oracle 賠償 88 億美元而告一段落。之所以沒(méi)說(shuō)案件告終,是因?yàn)?Google 不服判決,還在向最高法院上訴中。

而就在 Google 邊應(yīng)對(duì)訴訟時(shí),或許就在周密思量針對(duì)自己的 Android 系統(tǒng)甚至是整個(gè)公司研發(fā)的下一步:如何擺脫 Oracle?

于是,在 2015 年年底,我們就親眼見(jiàn)證了 Google 宣布將 Java 應(yīng)用程序接口(APIs)從 Oracle JavaAPI 替換成開(kāi)源的 OpenJDK。甚至在兩年后的 Google I/O 大會(huì)上,震撼宣布 Kotlin 成為 Android 開(kāi)發(fā)的一級(jí)編程語(yǔ)言,讓其與 Java 齊駕并驅(qū),反觀,也是與之相抗衡。

再針對(duì)這一次的 Oracle 修改了 JDK 11 的用戶(hù)協(xié)議來(lái)看,從 Oracle 的角度其實(shí)不難理解他的這一行為,畢竟作為純軟件先驅(qū)的 Oracle 也是個(gè)商業(yè)公司,他需要提供一個(gè)軟件的商業(yè)版本來(lái)提高增值服務(wù)。但是對(duì)于使用 Oracle JDK 的開(kāi)發(fā)者或企業(yè)而言,情況就變得有所不同了,為了避免上述 Google 的同等遭遇,我們除了付費(fèi)是否還有其他選擇?

對(duì)此,不少開(kāi)發(fā)者紛紛熱議:

Java 程序員是時(shí)候向 C# 轉(zhuǎn)移了;

換 .NET 也行;

這個(gè)僅針對(duì) JDK 11,不升級(jí)不使用 Java 11 不就行了。

在訪(fǎng)問(wèn)了幾位知名的 Java 開(kāi)發(fā)者之后,他們給出的答案幾乎都是,「使用 Kotlin」。甚至就連微博研發(fā)副總經(jīng)理@TimYang 也表示,這一行為直接導(dǎo)致 Kotlin 才是最大贏家,IDEA 環(huán)境將 Java 代碼粘貼到 Kotlin 文件,自動(dòng)轉(zhuǎn)換。

小編也是一名從事了6年java開(kāi)發(fā)的全棧工程師,花了近一個(gè)月整理了一份較適合18年學(xué)習(xí)的java干貨,送給每一位學(xué)習(xí)java小伙伴,加java學(xué)習(xí)qq群領(lǐng)?。?18464711


不過(guò)相較于 23 歲的 Java,不少開(kāi)發(fā)者還是對(duì)年僅 7 歲的 Kotlin 的未來(lái)感到迷茫,所以在面對(duì) JDK 8 即將停止免費(fèi)更新支持、JDK 11 無(wú)法商用的情況下,請(qǐng)記得 Oracle 還有一個(gè)名為 OpenJDK 的開(kāi)源項(xiàng)目。

注:Java 9、10 并不是長(zhǎng)期支持(LTS)版本,所以上述文章中并未提及。

要說(shuō) Oracle JDK 和 Open JDK 之前的差距很明顯,那么在 JDK 11 中,Oracle 很人性化地將兩者的不同盡可能地縮小了,甚至可以忽略微乎其微的差距。

或者除了 Open JDK 外,我們此前也發(fā)文為大家推薦了一些實(shí)用的

JDK

,譬如 AdoptOpenJDK builds、Red Hat OpenJDK builds、Azul Zulu 等等。最后,對(duì)于 Oracle 修改 JDK 11 的使用協(xié)議,你有著什么樣的看法?歡迎下方留言,分享你的想法。

參考:

https://winterbe.com/posts/2018/09/24/java-11-tutorial/,譯者:彎月

https://blog.joda.org/2018/09/do-not-fall-into-oracles-java-11-trap.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,697評(píng)論 19 139
  • /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home...
    光劍書(shū)架上的書(shū)閱讀 4,197評(píng)論 2 8
  • 本文參加#未完待續(xù),就要表白#活動(dòng),本人承諾,文章內(nèi)容為原創(chuàng),且未在其他平臺(tái)發(fā)表過(guò)。 ...
    永清隨筆閱讀 1,013評(píng)論 16 28
  • liunx下的文件都是有權(quán)限的,對(duì)于不同的用戶(hù),不同的組權(quán)限都是不一樣的。超級(jí)用戶(hù)的權(quán)限是最大的.文件的基本權(quán)限:...
    丨Coats閱讀 348評(píng)論 0 1
  • 日薄西山了無(wú)痕, 經(jīng)年漂泊未立根。 幾分饑寒襲倦體, 一碗羊湯暖孤心。 縱酒放歌少知音, 躍馬揚(yáng)鞭滿(mǎn)征塵。 獨(dú)悲阮...
    豫西笑笑生閱讀 294評(píng)論 0 1

友情鏈接更多精彩內(nèi)容