Java14新特性
1. instanceof 模式匹配
舊:類型匹配後的使用,需要強制轉換
private static void old(Object obj) {
if(obj instanceof String){
String str = (String)obj;
System.out.println(str.contain("java"));
}else{
System.out.println("error");
}
}
新:類型匹配過程中就可以完成轉換,並且定義變量str
private static void New(Object obj) {
if(obj instanceof String str){
System.out.println("無需強制轉換");
}else{
System.out.println("error");
}
}
注意:str變量只能在它所在if
中使用
private static void New(Object obj) {
if(obj instanceof String str){
System.out.println("無需強制轉換");
}else{
System.out.println(str.contains("java"));
System.out.println("error");
}
}
這樣寫是錯誤的。
2. 實用的NullPointerException
該特性改進了空指針異常的可讀性,能更準確地給出null變量的信息。
回顧Java8,提供了Optional來處理空指針異常。
舉例:
class Bank{
private Customer customer;
public Bank(Customer customer) {
this.customer = customer;
}
public Bank(){}
public Customer getCustomer(){
return customer;
}
public void setCustomer(Customer customer){
this.customer = customer;
}
}
class Customer{
private Account account;
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public Customer(Account account) {
this.account = account;
}
public Customer() {
}
}
class Account{
private int balance;
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
public Account(int balance) {
this.balance = balance;
}
}
main方法:
public class Test02 {
public static void main(String[] args) {
Bank bank = new Bank(new Customer());
bank.getCustomer().getAccount().getBalance();
}
}
很明顯我們沒有實例化 Account對象,這個代碼bank.getCustomer().getAccount()
運行會報空指針
運行看看結果:
Exception in thread "main" java.lang.NullPointerException
at NullPointerException.Test02.main(Test02.java:6)
的確有報錯,但是沒有給出具體哪兒報錯了。
接下來我們在VM options加個指令:-XX:+ShowCodeDetailsInExceptionMessages
再運行看看結果:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "NullPointerException.Account.getBalance()" because the return value of "NullPointerException.Customer.getAccount()" is null
at NullPointerException.Test02.main(Test02.java:6)
這樣就顯示出詳細報錯信息了。
3. Record(預覽特性)
Java語言架構師Brian Goetz吐槽Java語言太囉嗦,有太多重複的,容易出錯的代碼,如構造函數、getter/setter等。
Java14 也許最令人興奮的,同時也是最令人驚訝的創新就是:Record類型的引入!
使用record來減少類聲明語法,效果類似lombok的的@Data註解。
它們的個共同的是類的部分或全部狀態可以直接在類頭中描述。
和枚舉類一樣,記錄也是類的一種受限形式。
舉例
原來定義一個實體類
public final class User {
private final String name;
private final User partner;
public User(String name, User partner) {
this.name = name;
this.partner = partner;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return name.equals(user.name) &&
partner.equals(user.partner);
}
@Override
public int hashCode() {
return Objects.hash(name, partner);
}
public String getName() {
return name;
}
public User getPartner() {
return partner;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", partner=" + partner +
'}';
}
}
需要這麼多代碼,雖然IDE可以自動生成,但是我們瀏覽的時候就顯得很複雜。
Java14提供了一種Record創建類的方式
現在只需要這樣創建:
public record Person(String name,Person partner) {
//成員變量定義在頭部
}
測試
public static void main(String[] args) {
//測試構造器
Person p1 = new Person("Romio",new Person("Julia",null));
System.out.println(p1);
}
##輸出
Person[name=Romio, partner=Person[name=Julia, partner=null]]
相當於record提供了toString方法
我們查看一下Person的字節碼文件:
public final class Person extends java.lang.Record {
private final java.lang.String name;
private final Record.Person partner;
public Person(java.lang.String name, Record.Person partner) { /* 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 java.lang.String name() { /* compiled code */ } //相當於getName()方法
public Record.Person partner() { /* compiled code */ } //相當於getPartner()方法
}
注意事項:
-
我們還可以在Record聲明的類中定義靜態字段、靜態方法、構造器或實例方法
-
不能在Record聲明的類中定義非靜態實例字段;類不能聲明爲abstract;不能聲明顯式的父類等。
public record Person(String name,Person partner) {
//靜態屬性
public static String nation;
//靜態方法
public static String showNation(){
return nation;
}
//構造器
public Person(String name){
this(name,null);
}
//實例方法
public String getNameToUpperCase(){
return name.toUpperCase();
}
}
- 因爲record聲明默認爲final類,類不能被繼承,類成員變量不可被更改(即set方法)
不合法:
abstract record A{ //不可用abstract修飾
}
record B() extends Thread{ //不可繼承其他類,因爲record隱式繼承了Record父類,由於Java的單繼承,不可再顯示繼承其他父類。
}
值得一提:Java.lang.Class對象中添加了兩個新方法:
RecordComponent[] getRecordComponents()
boolean isRecord()
用於反射的使用
4. switch表達式的使用
jdk12、13的預覽版如今已成爲正式版
switch可以當做語句使用,也可以當作表達式使用。
具體情況:使用->來替代以前的: break;另外還提供了yield來在block中返回值。
最早的switch
public class Test04 {
public static void main(String[] args) {
Week day = Week.FRIDAY;
switch (day){
case MONDAY:
case TUESDAY:
case WEDNESDAY:
System.out.println(3);
break;
case THUSDAY:
System.out.println(4);
break;
case FRIDAY:
System.out.println(5);
break;
case SATURDAY:
System.out.println(6);
break;
case SUNDAY:
System.out.println(7);
break;
default:
break;
}
}
}
enum Week{
SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THUSDAY,FRIDAY,SATURDAY;
}
JDK12的新特性:引用switch表達式
public static void siwtch12(){
switch (day) {
case MONDAY, TUESDAY, WEDNESDAY -> System.out.println(3);
case THUSDAY -> System.out.println(4);
case FRIDAY -> System.out.println(5);
case SATURDAY -> System.out.println(6);
case SUNDAY -> System.out.println(7);
default -> System.out.println(1);
}
}
//還可以用一個參數接收
public static void siwtch12(){
Week day = Week.FRIDAY;
int num = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY -> 3;
case THUSDAY -> 4;
case FRIDAY -> 5;
case SATURDAY -> 6;
case SUNDAY -> 7;
default -> 1;
};
System.out.println(num);
}
JDK13新特性:引入yield關鍵字,用於返回指定的數據,結束switch結構,可以理解爲return
public void siwtch13(){
String x = "5";
int num = switch (x){
case "1"->1;
case "2"->2;
case "3":yield 3;
default -> {
System.out.println("default..");
yield 4;
}
};
System.out.println(num);
}
5. 文本塊(預覽版)
jdk14增加了兩個轉義字符:\
禁止換行 \s
空格
目標:
- 簡化跨越多行的字符串,避免對換行等特殊子進行轉義,簡化編寫Java程序
- 增強Java程序中用字符串表示的其他語言的代碼的可讀性
- 解析新的轉義序列
//jdk13以前
String text1 = "China's football club Guangzhou Evergrande " +
"and Taobao reported a loss of 1.9 billion yuan (274 million US dollars)" +
" in 2019, according to the National Equities Exchange " +
"and Quotations (NEEQ) listed company's annual report " +
"released on Wednesday.";
System.out.println(text1);
//jdk13新特性(前後3個雙引號包裹文本)
String text2 = """
China's football club Guangzhou Evergrande
and Taobao reported a loss of 1.9 billion yuan (274 million US dollars)
in 2019, according to the National Equities Exchange
and Quotations (NEEQ) listed company's annual report
released on Wednesday.
""";
//html
@Test
public void HtmlTest(){
String html1 = "<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>Title</title>\n" +
"</head>\n" +
"<body>\n" +
"hello \n" +
"</body>\n" +
"</html>";
String html2 = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hello\s
</body>
</html>
""";
}
還有一些如JSON、SQL語句都可以這樣使用。
JDK14的新特性,\
和\s
@Test
public void TestJDK14(){
String html2 = """
<!DOCTYPE html>\
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hello\sworld
</body>
</html>
""";
System.out.println(html2);
}
//結果
<!DOCTYPE html><html lang="en"> //沒有換行
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hello world //中間多了空格
</body>
</html>
Process finished with exit code 0
6. 棄用一個垃圾收集器組合
jdk9廢除(Serial+CMS) (ParNew+Serial Old)
jdk14棄用(ParallelScavenge +SerialOld GC)
棄用-XX:+UseParallelGC-XX :- UseParallelOldGC組合,-XX:UseParallelOld GC選項也被棄用,因爲它的作用是取消老年代並行GC,支持老年代串行GC。因此,任何與UseUseParallelOldGC選項有關的用法都會輸出警告。
7. 刪除CMS垃圾回收器
自從G1(基於Region分代)橫空出世後,CMS在JDK9就被標記爲棄用。
原因:
- 會產生內存碎片,導致併發清除後,用戶線程可用的空間不足
- 既然強調了併發,CMS收集器對CPU資源非常敏感
- CMS收集器無法處理浮動垃圾(一個線程清理垃圾,另一個線程產生垃圾)。
兩個新的收集器
ZGC(JDK11出現) 和 Shenandoah (open jdk12)
特點:低停頓時間/低延遲
8. ZGC (試驗狀態)
在儘可能對系統吞吐量影響不大的前提下,實現在任意堆內存大小下都可以把垃圾收集的停頓時間限制在10ms以內的低延遲。
它是一款以低延遲爲首要目標的一款垃圾收集器
ps: 還有一些不太常用的新特性,這邊就不指出了!