歡迎光臨
每天分享高質量文章

使用了 23 年的 Java 不再免費!

導讀:程式語言界的扛把子 Java,不僅搭載上瞭如火箭版的更新速度,現在還有意讓上車的使用者付費買票了,那麼身為 Java 開發者,下一步究竟是該棄用 23 年的老牌工具 JDK 還是乖乖付費繼續搭載這列車?

本文經授權轉自公眾號CSDN(ID:CSDNnews),譯者:彎月

上個月,Java 開發工具包(JDK)11 剛剛到來,JDK 12 就在緊密敲鑼籌備中,隨著訊息接連不斷地爆出,不少使用 Java 的開發者開始有種一年一萬個更新版本的錯覺,而當面對厚厚的一堆堆 Java 8/9/10/11 的入門書籍和教程時,就問你怕不怕?

不僅如此,就在 Java 早已在移動 App、伺服器應用、Web 開發、J2EE 企業級應用和嵌入式等領域根深蒂固時,Oracle 於近日最新釋出的一紙 Java 使用者使用協議轟動了整個業界,因為 Java 將收費了!

01 JDK 11 不容錯過的那些新特性

JDK 11 作為 Oracle 以六個月為更新週期之後公開釋出的第一個長期支援版本,其中還是有許多實用的功能特性。

1. 區域性變數推斷

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>,而這個型別可以簡化成一個 var 關鍵字,節省了很多敲程式碼的時間:

var myList = new ArrayList<Map<StringList>>();

for (var current : myList) {
    // current is infered to type: Map>

    System.out.println(current);
}

由於 Java 11 的 var 關鍵字也可以在 lambda 的引數上使用,因此可以給引數加註解:

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

小提示:Intellij IDEA 中可以按住 CMD/CTRL 鍵並將滑鼠懸停在變數上來檢視推斷出的型別(鍵盤快捷鍵為Ctrl+J)。

2. 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<Stringresponse = 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"))
    .essay-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() {
        @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

3. 集合

像 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();
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 會在你試圖改變不可變集合時發出警告。

4. 流

Java 8 引入了 流的概念,現在它有三個新的方法。Stream.ofNullable 能從單個元素構建一個流:

Stream.ofNullable(null)
    .count()   // 0

dropWhile 和 takeWhile 兩個方法都能接受 predicate 物件,從而可以拋棄流中的一些元素:

Stream.of(12321)
    .dropWhile(n -> n 3)
    .collect(Collectors.toList());  // [3, 2, 1]

Stream.of(12321)
    .takeWhile(n -> n 3)
    .collect(Collectors.toList());  // [1, 2]

5. 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

6. 字串

最基礎的類之一 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

7. 其他 JVM 特性

在我看來,上述這些是 Java 11 與 8 相比時最有意思的語言 API 特性,不過新的特性還有許多,比如下麵這些:

  • 用於響應式程式設計的流式 API

  • Java 模組系統

  • 應用程式類資料共享

  • 動態類-檔案常量

  • Java REPL(JShell)

  • 飛行記錄器

  • Unicode 10

  • G1:完全並行的垃圾回收器

  • ZGC:可擴充套件的低延遲垃圾回收器

  • Epsilon:No-Op垃圾回收器

  • 不推薦使用的Nashorn JavaScript引擎

  • ……

02 學不動的 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,並將其投入商業專案中使用,後續可能會帶來不小的商業糾紛。

03 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 的使用協議,你有著什麼樣的看法?歡迎下方留言,分享你的想法。

參考:

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

本文經授權轉自公眾號CSDN(ID:CSDNnews),譯者:彎月

據統計,99%的大咖都完成了這個神操作


猜你想看

Q: 付費版Java對你有影響嗎

歡迎留言與大家分享

覺得不錯,請把這篇文章分享給你的朋友

轉載 / 投稿請聯絡:baiyu@hzbook.com

更多精彩,請在後臺點選“歷史文章”檢視

贊(0)

分享創造快樂