23 岁的 Java 成为编程界的扛把子,网友回复:这就是实力
編程語言界的扛把子 Java,不僅搭載上了如火箭版的更新速度,現在還有意讓上車的用戶付費買票了,那么身為 Java 開發者,下一步究竟是該棄用 23 年的老牌工具 JDK 還是乖乖付費繼續搭載這列車?
上個月,Java 開發工具包(JDK)11剛剛到來,JDK 12 就在緊密敲鑼籌備中,隨著消息接連不斷地爆出,不少使用 Java 的開發者開始有種一年一萬個更新版本的錯覺,而當面對厚厚的一堆堆 Java 8/9/10/11 的入門書籍和教程時,就問你怕不怕?不僅如此,就在 Java 早已在移動 App、服務器應用、Web 開發、J2EE 企業級應用和嵌入式等領域根深蒂固時,Oracle 于近日最新發布的一紙 Java 用戶使用協議轟動了整個業界,因為 Java 將收費了!
1.JDK 11 不容錯過的那些新特性
JDK 11 作為 Oracle 以六個月為更新周期之后公開發布的第一個長期支持版本,其中還是有許多實用的功能特性。
局部變量推斷
Java 10 引入了新的關鍵字 var,可以在定義局部變量時代替類型信息(局部指的是在方法體內部的變量定義)。
在 Java 10 之前,局部變量必須這樣定義:
String text = "Hello Java 9";
而現在可以用 var 代替 String。編譯器會根據變量的賦值推斷出正確的類型。因此,text 的類型是 String:
var text = "Hello Java 10";
使用 var 定義的變量依然是靜態類型。這種變量不能重新用不兼容的類型賦值。比如,下面的代碼無法通過編譯:
var text = "Hello Java 11";
text = 23; // Incompatible types
還可以通過同時使用 var 和 final 來禁止變量的重新賦值:
final var text = "Banana";
text = "Joe"; // Cannot assign a value to final variable 'text'
而且,當編譯器無法推斷出正確類型時也不允許使用 var:
// Cannot infer type:
var a;
var nothing = null;
var lambda = () -> System.out.println("Pity!");
var method = this::someMethod;
局部變量類型的推斷在泛型中非常有用。下面的例子中,current 有個非常復雜的類型 Map<String, List<Integer>>,而這個類型可以簡化成一個 var 關鍵字,節省了很多敲代碼的時間:
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 關鍵字也可以在 lambda 的參數上使用,因此可以給參數加注解:
Predicate<String> predicate = (@Nullable var a) -> true;
小提示:Intellij IDEA 中可以按住 CMD/CTRL 鍵并將鼠標懸停在變量上來查看推斷出的類型(鍵盤快捷鍵為Ctrl+J)。
HTTP 客戶端
Java 9 引入了新的 HttpClient API 來處理 HTTP 請求。在 Java 11 中,這個 API 已穩定,可以通過 java.net 包使用。我們來看看這個 API 能干什么。
新的 HttpClient 支持同步和異步方式。同步請求會阻塞當前線程直到響應返回。BodyHandlers 定義了期待的響應體的類型(如字符串、字節數組或文件):
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());
同樣的請求也可以異步進行。調用 sendAsync 不會阻塞當前線程,它會返回一個 CompletableFuture 用來構建異步操作管線。
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() 調用,因為它是默認的。
下面這個例子用 POST 方式將數據發送到給定的 URL。與 BodyHandlers 類似,這里使用 BodyPublishers 來定義請求體中要發送的數據類型,如字符串、字節數組、文件或輸入流:
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
下面的例子演示了通過 BASIC-AUTH 方式進行認證的方法:
var request = HttpRequest.newBuilder()
.uri(URI.create("https://postman-echo.com/basic-auth"))
.build();
var client = HttpClient.newBuilder()
.authenticator(new Authenticator() {br/>@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 等集合類都加入了新的方法。List.of 會根據給定的參數創建一個新的不可變的 list。List.copyOf 能創建 list 的不可變副本。
var list = List.of("A", "B", "C");
var copy = List.copyOf(list);
System.out.println(list == copy); // true
因為 list 已經是不可變的了,因此不需要實際創建 list 實例的副本,因此 list 和 copy 會指向同一個副本。但如果賦值一個可變的列表,copy 就會生成一個新的實例,以保證修改原始列表時不會產生副作用:
var list = new ArrayList<String>();
var copy = List.copyOf(list);
System.out.println(list == copy); // false
在創建不可變的 map 時,不需要自行創建 map 的內容,只需要傳遞鍵和值即可:
var map = Map.of("A", 1, "B", 2);
System.out.println(map); // {B=2, A=1}
Java 11 中的不可變集合依然使用與原來的集合 API 同樣的接口。但如果試圖通過添加或刪除元素的方式改變不可變集合,則會發生 java.lang.UnsupportedOperationException 異常。好在 Intellij IDEA 會在你試圖改變不可變集合時發出警告。
流
Java 8 引入了 流的概念,現在它有三個新的方法。Stream.ofNullable 能從單個元素構建一個流:
Stream.ofNullable(null)
.count() // 0
dropWhile 和 takeWhile 兩個方法都能接受 predicate 對象,從而可以拋棄流中的一些元素:
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 也增加了幾個新方法,比如現在可以很容易將 optional 轉換成流,或者給空的 optional 提供另一個 optional 作為出錯時的備選方案:
Optional.of("foo").orElseThrow(); // foo
Optional.of("foo").stream().count(); // 1
Optional.ofNullable(null)
.or(() -> Optional.of("fallback"))
.get(); // fallback
字符串
最基礎的類之一 String 也加了幾個輔助方法用來去除空白、檢查空白,以及以流的方式輸出字符串:
" ".isBlank(); // true
" Foo Bar ".strip(); // "Foo Bar"
" Foo Bar ".stripTrailing(); // " Foo Bar"
" Foo Bar ".stripLeading(); // "Foo Bar "
"Java".repeat(3); // "JavaJavaJava"
"A\nB\nC".lines().count(); // 3
其他 JVM 特性
在我看來,上述這些是 Java 11 與 8 相比時最有意思的語言 API 特性,不過新的特性還有許多,比如下面這些:
用于響應式編程的流式 API
Java 模塊系統
應用程序類數據共享
動態類-文件常量
Java REPL(JShell)
飛行記錄器
Unicode 10
G1:完全并行的垃圾回收器
ZGC:可擴展的低延遲垃圾回收器
Epsilon:No-Op垃圾回收器
不推薦使用的Nashorn JavaScript引擎
……
2.學不動的 Java,還要收費了?
對于 Java 新版本,不少開發者望塵莫及,紛紛表示不要再更新了,我的項目還停留在 Java 8 呢。話雖如此,事實上 Oracle 曾在今年四月就宣布,自 2019 年 1 月起,Java SE 8 公開更新將不向沒有商用許可證的業務、商用或生產用途提供。即未來開發者還想使用 JDK 8,Oracle 將不會提供免費的技術支持,需要另外收費。所以總體而言還是建議開發者應該轉換到最新版的 Java 11。
但就在這時,據國外網友@Stephen Colebourne 發布的一篇名為《Oracle's Java 11 trap - Use OpenJDK instead!》博文,我們才注意到,在 Java 11 中,Oracle 悄然更新了用戶使用協議(https://www.oracle.com/technetwork/java/javase/terms/license/javase-license.html):
簡而言之:
新版 Oracle JDK 不可以用在數據處理、商業、產品、或者內部商業用途(需要購買 License),僅可免費用于開發、測試、原型、演示。
正是這一修改意味著免費使用 23 年的 Java 即將走上收費的路子。倘若開發者還是如往常那般下載 Oracle JDK,并將其投入商業項目中使用,后續可能會帶來不小的商業糾紛。
3.Oracle JDK 收費了,企業和開發者怎么辦?
提及 Java 的商業糾紛,我們不禁想起此前轟動一時且長達八年的 Oracle 與 Google 關于 Java 的侵權案:
1995 年,Sun 公司發布了 Java;這其中需要介紹到 2006 年,Sun 公司開源了其 Java 項目——OpenJDK,但是并未開源此前的 Java 項目 SunJDK(現在是 OracleJDK);2009 年,Oracle 以 74 億美元收購 Sun 從而獲得了 Java 的版權,而其中也包含了 Sun 研發的 Java 商業項目版權;2010 年 8 月,Oracle 認為 Google Android 系統抄襲了 37 個 Java API 代碼段,而這些代碼屬于 Oracle 商業私有 JDK(OracleJDK)的一部分,于是將 Google 訴訟至法庭,要求賠償 26 億美元;經過 8 年的調解及上訴,這一案最終于今年 3 月,美國聯邦巡回法院判 Google 向 Oracle 賠償 88 億美元而告一段落。之所以沒說案件告終,是因為 Google 不服判決,還在向最高法院上訴中。
而就在 Google 邊應對訴訟時,或許就在周密思量針對自己的 Android 系統甚至是整個公司研發的下一步:如何擺脫 Oracle?
于是,在 2015 年年底,我們就親眼見證了 Google 宣布將 Java 應用程序接口(APIs)從 Oracle JavaAPI 替換成開源的 OpenJDK。甚至在兩年后的 Google I/O 大會上,震撼宣布 Kotlin 成為 Android 開發的一級編程語言,讓其與 Java 齊駕并驅,反觀,也是與之相抗衡。
再針對這一次的 Oracle 修改了 JDK 11 的用戶協議來看,從 Oracle 的角度其實不難理解他的這一行為,畢竟作為純軟件先驅的 Oracle 也是個商業公司,他需要提供一個軟件的商業版本來提高增值服務。但是對于使用 Oracle JDK 的開發者或企業而言,情況就變得有所不同了,為了避免上述 Google 的同等遭遇,我們除了付費是否還有其他選擇?
對此,不少開發者紛紛熱議:
Java 程序員是時候向 C# 轉移了;
換 .NET 也行;
這個僅針對 JDK 11,不升級不使用 Java 11 不就行了。
在訪問了幾位知名的 Java 開發者之后,他們給出的答案幾乎都是,「使用 Kotlin」。甚至就連微博研發副總經理@TimYang 也表示,這一行為直接導致 Kotlin 才是最大贏家,IDEA 環境將 Java 代碼粘貼到 Kotlin 文件,自動轉換。
不過相較于 23 歲的 Java,不少開發者還是對年僅 7 歲的 Kotlin 的未來感到迷茫,所以在面對 JDK 8 即將停止免費更新支持、JDK 11 無法商用的情況下,請記得 Oracle 還有一個名為 OpenJDK 的開源項目。
注:Java 9、10 并不是長期支持(LTS)版本,所以上述文章中并未提及。
要說 Oracle JDK 和 Open JDK 之前的差距很明顯,那么在 JDK 11 中,Oracle 很人性化地將兩者的不同盡可能地縮小了,甚至可以忽略微乎其微的差距。
或者除了 Open JDK 外,我們此前也發文為大家推薦了一些實用的
JDK
,譬如 AdoptOpenJDK builds、Red Hat OpenJDK builds、Azul Zulu 等等。最后,對于 Oracle 修改 JDK 11 的使用協議,你有著什么樣的看法?歡迎下方留言,分享你的想法。
最關鍵的一步
對Java技術,架構技術感興趣的同學, Java架構交流群 668041364, 一起學習,相互討論。
群內已經有小伙伴將知識體系整理好(源碼,筆記,PPT,學習視頻),歡迎加群領取。
轉載于:https://blog.51cto.com/14233733/2404937
總結
以上是生活随笔為你收集整理的23 岁的 Java 成为编程界的扛把子,网友回复:这就是实力的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 安全考虑
- 下一篇: Echarts的提示(Tooltip)显