還看不懂同事程式碼?快來補一波 Java 7 語法特性
前言
Java 平臺自出現到目前為止,已經 20 多個年頭了,這 20 多年間 Java 也一直作為最流行的程式設計語言之一,不斷面臨著其他新興程式語言的挑戰與衝擊。Java 語言是一種靜態強型別語言,這樣的語言特性可以讓 Java 編譯器在編譯階段發現錯誤,這對於構建出一個穩定安全且健壯的應用來說,尤為重要。但是也因為這種特性,讓 Java 開發似乎變得缺少靈活性,開發某些功能的應用時,程式碼量可能是其他語言的幾倍。Java 開發的不足之處也體現越來越複雜的 JDK 上,越來越複雜的 JDK 讓開發者完全理解的難度變的非常大。以至於開發者有時會重複實現一個 JDK 中已經提供了的功能。
為了跟上網際網路應用程式設計發展的腳步, Java 從 9 版本開始調整了 JDK 釋出的節奏,JDK 的每次更新都注重提高生產效率,提高 JVM 效能,推行模組化等,讓開發者可以更多的專注於業務本身,而不是浪費過多的時間在語言特性上。 Java 語言的更新要在語言的嚴謹性和靈活性上找到一個平衡點,畢竟靈活性可以減少編碼的複雜度,而嚴謹性是構建複雜且健壯應用的基石。
Java 7 語言特性
Java 重要的更新版本是在 Java 5 版本,這個版本中增加了如泛型、增強 for、自動裝箱拆箱、列舉型別,可變引數、註解等一系列重要功能,但是隨後的 Java 6 中並沒有增加新的重要的語言特性。Java 5 的釋出是在 2004 年,已經很久遠了,網上關於 Java 的教程也大多是基於 Java 6 的,也因此我準備從 Java 7 開始介紹每個 Java 版本的新特性。
下面所有程式碼的執行演示都是基於 Java 7 ,所以你如果嘗試下面的程式碼,需要安裝並配置 Jdk 1.7 或者已上版本。
1. switch String
在 Java 7 之前,switch 語法中只支援整數型別以及這些整數型別的封裝類進行判斷,在 Java 7 中,支援了 string 字串型別的判斷,使用起來非常的簡單,但是實用性是很高的。
1.1. switch String 基本用法
編寫一個簡單的 switch 判斷字串的測試類。
public class SwitchWithString { public static void main(String[] args) { String gender = "男"; System.out.println(gender.hashCode()); switch (gender) { case "男": System.out.println("先生你好"); break; case "女": System.out.println("女士你好"); break; default: System.out.println("你好"); } } }
switch 判斷字串使用起來很簡單,結果也顯而易見會先輸出 gender 變數的 hashCode,然後輸出匹配結果“先生你好”。
30007
先生你好
在使用 switch string 時候,如果結合 Java 5 的列舉類,那麼效果會更好,Java 7 之前使用列舉類要為每個值編數字代號,Java 7 之後可以直接定義字串名稱。
1.2. switch String 實現原理
但是這個支援只是編譯器層面的支援, JVM 依舊是不支援的。在對字串進行 switch 時,編譯器會把字串轉換成整數型別再進行判斷。為了驗證上面說的只是編譯器層面的支援,我們反編譯(可以使用 Jad 反編譯工具,也可以在 Idea 中雙擊編譯生成的 class )生成的 class 檔案,看到編譯器把 switch string 轉換成了字串 hashCode 判斷,為了防止 hashCode 衝突,又使用了 equals 再次判斷。
public class SwitchWithString {
public SwitchWithString() {
}
public static void main(String[] args) {
String gender = "男";
System.out.println(gender.hashCode());
byte var3 = -1;
switch(gender.hashCode()) {
case 22899:
if (gender.equals("女")) {
var3 = 1;
}
break;
case 30007:
if (gender.equals("男")) {
var3 = 0;
}
}
switch(var3) {
case 0:
System.out.println("先生你好");
break;
case 1:
System.out.println("女士你好");
break;
default:
System.out.println("你好");
}
}
}
2. try-with-resource
Java 不同於 C++,需要開發者自己管理每一塊記憶體,大多時候 Java 虛擬機器都可以很好的幫我們進行資源管理,但是也有時候需要手動釋放一些資源,比如資料庫連線、磁碟檔案連線、網路連線等。換句話說,只要是資源數量有限的,都需要我們手動的進行釋放。
2.1. try-catch-finally
在操作有限資源的時候,可能會出現各種異常,不管是讀取階段還是在最後關閉資源的過程中,都有可能出現問題,我們通常會使用下面的方式 try-catch-finally
保證資源的釋放。
像下面這樣。
/**
* 釋放資源
*
* @author www.codingme.net
*/
public class TryCatachFinally {
/**
* 異常處理
*
* @param args
*/
public static void main(String[] args) throws Exception {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("jdk-feature-7.iml");
} catch (FileNotFoundException e) {
throw e;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
throw e;
}
}
}
}
}
看看這噁心的程式碼結構,為了捕獲異常,我們寫了一個 catch
,為了能保證釋放資源,我們又寫了 finally
進行資源釋放,在資源釋放時為了捕捉 close
時丟擲的異常,我們又寫了一個 try-catch
。最後看著這複雜的程式碼,如果有人告訴你這段程式碼有 bug
,那你一定不會相信。但是確實是這樣,看起來嚴密的程式碼邏輯,當 try
中的程式碼邏輯和 close
方法同時產生異常的時候,try
中的異常資訊會丟失。
可以看這裡例子。
package net.codingme.feature.jdk7;
import java.io.IOException;
/**
* 釋放資源
*
* @author www.codingme.net
*/
public class TryCatachFinallyThrow {
/**
* 異常處理
*
* @param args
*/
public static void main(String[] args) throws Exception {
read();
}
public static void read() throws Exception {
FileRead fileRead = null;
try {
fileRead = new FileRead();
fileRead.read();
} catch (Exception e) {
throw e;
} finally {
if (fileRead != null) {
try {
fileRead.close();
} catch (Exception e) {
throw e;
}
}
}
}
}
class FileRead {
public void read() throws Exception {
throw new IOException("讀取異常");
}
public void close() throws Exception {
System.out.println("資源關閉");
throw new IOException("關閉異常");
}
}
很明顯程式碼裡 read
和 close
方法都會產生異常,但是執行程式發現只能收到 close
的異常資訊。
資源關閉
Exception in thread "main" java.io.IOException: 關閉異常
at net.codingme.feature.jdk7.FileRead.close(TryCatachFinallyThrow.java:51)
at net.codingme.feature.jdk7.TryCatachFinallyThrow.read(TryCatachFinallyThrow.java:33)
at net.codingme.feature.jdk7.TryCatachFinallyThrow.main(TryCatachFinallyThrow.java:20)
異常資訊丟失了,可怕的是你以為只是 close
時發生了異常而已。
2.2. try-autocloseable
上面的問題在 Java 7 中其實已經提供了新的解決方式,Java 7 中對 try
進行了增強,可以保證資源總能被正確釋放 。使用增強 try
的前提是 try
中的類實現了 AutoCloseable
介面,在 Java 7 中大量的需要釋放資源的操作其實都已經實現了此介面了。
實現了 AutoCloseable
的類,在增強 try
中使用時,不用擔心資源的關閉,在使用完畢會自動的呼叫 close
方法,並且異常不會丟失。
讓我們編寫的模擬資源操作的類實現 AutoCloseable
介面,然後時候增強 try
看看效果。
package net.codingme.feature.jdk7;
/**
* 自動關閉
*
* @author www.codingme.net
*/
public class AutoCloseResource {
public static void main(String[] args) throws Exception {
try (Mysql mysql = new Mysql();
OracleDatabase oracleDatabase = new OracleDatabase()) {
mysql.conn();
oracleDatabase.conn();
}
}
}
class Mysql implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("mysql 已關閉");
}
public void conn() {
System.out.println("mysql 已連線");
}
}
class OracleDatabase implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("OracleDatabase 已關閉");
}
public void conn() {
System.out.println("OracleDatabase 已連線");
}
}
測試類 Mysql 和 OracleDatabase 都是實現了 AutoCloseable,執行檢視結果。
mysql 已連線
OracleDatabase 已連線
OracleDatabase 已關閉
mysql 已關閉
確認在發生異常時候異常資訊不會丟失,寫一個有異常的模擬測試類進行測試。
package net.codingme.feature.jdk7;
import java.io.IOException;
/**
* 釋放資源
*
* @author www.codingme.net
*/
public class AutoCloseThrow {
public static void main(String[] args) throws Exception {
try (FileReadAutoClose fileRead = new FileReadAutoClose()) {
fileRead.read();
}
}
}
class FileReadAutoClose implements AutoCloseable {
public void read() throws Exception {
System.out.println("資源讀取");
throw new IOException("讀取異常");
}
@Override
public void close() throws Exception {
System.out.println("資源關閉");
throw new IOException("關閉異常");
}
}
執行檢視異常資訊。
資源讀取
資源關閉
Exception in thread "main" java.io.IOException: 讀取異常
at net.codingme.feature.jdk7.FileReadAutoClose.read(AutoCloseThrow.java:23)
at net.codingme.feature.jdk7.AutoCloseThrow.main(AutoCloseThrow.java:14)
Suppressed: java.io.IOException: 關閉異常
at net.codingme.feature.jdk7.FileReadAutoClose.close(AutoCloseThrow.java:29)
at net.codingme.feature.jdk7.AutoCloseThrow.main(AutoCloseThrow.java:15)
自動關閉,異常清晰,關閉異常存在於 Suppressed
,稱為抑制異常,後續文章會詳細介紹。
3. try-catch
在 Java 7 之前,一個 catch 只能捕獲一個異常資訊,當異常種類非常多的時候就很麻煩,但是在 Java 7 中,一個 catch 可以捕獲多個異常資訊,每個異常捕獲之間使用 |
分割,
package net.codingme.feature.jdk7;
import java.io.IOException;
/**
* 多異常捕獲
*/
public class TryCatchMany {
public static void main(String[] args) {
try (TxtRead txtRead = new TxtRead()) {
txtRead.reader();
} catch (IOException | NoSuchFieldException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class TxtRead implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("資源釋放");
}
public void reader() throws IOException, NoSuchFieldException {
System.out.println("資料讀取");
}
}
需要注意的是,一個 catch 捕獲多個異常時,不能出現重複的異常型別,也不能出現一個異常型別是另一個類的子類的情況。
4. 二進位制
Java 7 開始,可以直接指定不同的進位制數字。
- 二進位制指定數字值,只需要使用
0b
或者OB
開頭。 - 八進位制指定數字值,使用
0
開頭。 - 十六進位制指定數字值,使用
0x
開頭。
/**
* 二進位制
*
* @author www.codingme.net
*/
public class Binary {
public static void main(String[] args) {
// 二進位制
System.out.println("------2進位制-----");
int a = 0b001;
int b = 0b010;
System.out.println(a);
System.out.println(b);
// 八進位制
System.out.println("------8進位制-----");
int a1 = 010;
int b1 = 020;
System.out.println(a1);
System.out.println(b1);
// 十六進位制
System.out.println("------16進位制-----");
int a2 = 0x10;
int b2 = 0x20;
System.out.println(a2);
System.out.println(b2);
}
}
輸出結果。
------2進位制-----
1
2
------8進位制-----
8
16
------16進位制-----
16
32
5. 數字下劃線
Java 7 開始支援在數字定義時候使用下劃線分割,增加了數字的可讀性。
/**
* 數字下環線
*
* @author www.codingme.net
*/
public class NumberLine {
public static void main(String[] args) {
int a = 1_000;
int b = 1_0__0_0_0_____00;
System.out.println(a);
System.out.println(b);
}
}
得到結果。
1000
1000000
6. 結束語
雖然 Java 7 早在 2011 年就已經發布了,但是據我發現,使用到 Java 7 開始的新特性新語法的並不多,所以我的 JDK 新特性系列文章計劃從 Java 7 開始,一直介紹到目前已經發布的 Java 13,以後 Java 新版本更新的同時,這個新特性系列文章也會持續更新。
此去山高水遠,願能一路堅持,願你我一路同行。
<完>
個人網站:https://www.codingme.net
如果你喜歡這篇文章,可以關注公眾號,一起成長。
關注公眾號回覆資源可以沒有套路的獲取全網最火的的 Java 核心知識整理&面試核心資料。