1. 程式人生 > 程式設計 >Java中的操作符、表示式和語句

Java中的操作符、表示式和語句

Java中的操作符

Java是一門靜態強型別的語言,因此如果操作符接收的值的型別不符合操作符規定的型別,就會在編譯期產生編譯錯誤,通常IDE會對這種情況進行提示,所以Java的操作符不會跟JavaScript中的操作符那樣發生很多的型別轉換,其行為更具有確定性,所以只需要進行簡單的介紹。

運運算元說明 Java運運算元
分隔符 . [] (){},;
單目運運算元 ++ -- - !
強制型別轉換符 (type)
乘/除/求餘 * / %
加/減 + -
位移運運算元 << >> >>>
關係運算子 < <= >= > instanceof
等價運運算元 == !=
按位與 &
按位異或 ^
按位或 |
條件與 && (短路)
條件或 || (短路)
三目運運算元 ?:
賦值 = += -=*= /= &=|= ^= %=<<= >>= >>>=

特別注意==!=

==!=這兩個操作符用來進行相等性的判斷,但是需要注意的是,這類似於一個全等操作,對於基本型別的值來說沒有任何問題,但是對於引用型別的值來說,應該採用物件內部的equals()方法來進行比較。

Java中的表示式和合法的語句

在程式設計語言中,表示式和語句是完全不同的兩個概念,在我的理解中:

  • 表示式是有值的,任何一個表示式計算完成之後都會返回一個結果值
  • 語句是一段可執行的程式碼,它不一定有值。
  • 表示式本身是可以作為語句存在的,我們稱之為表示式語句

通過上面的介紹,我們可以認為表示式是語句的一種特殊情況,表示式是具有值的語句

同時,我們需要注意,在很多時候,我們提到語句的時候一般認為語句是沒有值的,這種情況下的語句被狹義化了。

使用表示式的目的和合法的語句

程式中的許多動作都是通過計算表示式而完成的,計算一個表示式要麼是為了他們的副作用,例如對變數賦值;要麼是為了得到他們的值,將其用做更大的表示式的引元或運算元,要麼用來改變語句的執行順序(如if中接收bool值進行流程控制),或者是同時為了這兩個目的

基於這個原因,很多的程式語言中如果一個表示式不滿足使用表示式的目的,那麼這個表示式就不能作為一條單獨的語句存在,也就是說不能執行這個表示式,因為這其實是沒有任何意義的,在Java中就是這樣的,如下:

int a = 0;
int b = 1;
a+b; //ERROR,是一個正確的表示式,但是由於不能滿足使用表示式的目的,把他作為一個語句執行沒有任何意義,所以Java認為這不是一個合法的語句。
複製程式碼

同時我們需要注意,賦值語句在Java中被認為是表示式,所以賦值語句是有值的,如下:

public static void main(String[] args) {
    int a = 0;
    System.out.println(a = 3+2); //5
    System.out.println(a); //5
}
複製程式碼

語句

我們接下來要討論的語句就是被狹義之後的語句,主要的功能是用於控制程式的執行流程。

程式的三種基本結構

無論我們使用哪種程式設計正規化(無論是結構化程式設計正規化,面向物件的程式設計正規化還是函式語言程式設計正規化),書寫演演算法都是必須的,而演演算法的實現過程是由一系列操作組成的,這些操作之間的執行次序就是程式的控制結構。

在以前,很多程式語言都提供了goto語句,goto語句非常靈活,可以讓程式的控制流程任意流轉。但是goto語句太隨意了,大量使用goto語句會使得程式難以理解並且容易出錯。

1996年,電腦科學家Bohm和Jacopini證明瞭這樣的事實:**任何簡單或者複雜的演演算法都可以有順序結構,分支(選擇)結構和迴圈結構這三種基本結構組合而成。**所以這三種結構就被稱為程式設計的三種基本結構。

不論哪一種程式語言,都會提供兩種基本的流程控制結構:分支結構和迴圈結構。

其中分支結構用於實現根據條件來選擇性地執行某段程式碼,迴圈結構則用於實現根據迴圈條件重複執行某段程式碼,通過這兩種控制結構,就可以改變程式原來順序執行的順序,實現流程的控制進而實現任意複雜的演演算法。

Java中也為我們提供了分支和迴圈語句,同時還提供了其他的一些語句用來更加靈活的控制程式流程。

順序結構

任何程式語言中最常見的程式結構就是順序結構。順序結構就是程式從上到下逐行地執行,中間沒有任何判斷和跳轉。

如果一個方法的多行程式碼之間沒有任何流程控制,則程式總是從上向下依次執行,排在前面的程式碼先執行,排在後面的程式碼後執行。這意味著,沒有流程控制,Java中方法的語句是一個順序執行流,從上向下依次執行每一條語句

分支(選擇)結構

Java中提供了兩種常見的分支控制結構:if語句switch語句。其中if語句使用布林表示式或者布林值作為分支條件來進行分支控制;而switch則使用者對多個值進行匹配,從而實現分支控制。

if語句

if語句使用布林表示式或者布林值作為分支條件來進行分支控制。if語句有如下三種形式:

if(logic expression) {
	statement...
}
複製程式碼
if (logic expression) {
    statement...
} else {
    statement...
}
複製程式碼
if (logic expression) {
    statement...
} else if(logic expression) {
    statement...
} ...
複製程式碼

使用if語句的時候需要注意下面幾點:

  • 當if和else後面之後一條語句的時候可以省略{},但是通常最好不要省略{}

  • 對於if語句,還有一個很容易出現的邏輯錯誤。看如下的程式:

    int age = 45;
    if (age > 20) {
        System.out.println("青年人");
    } else if (age > 40) {
        System.out.println("中年人");
    } else if (age > 60) {
        System.out.println("老年人");
    }
    複製程式碼

    表面上看來,上面的程式沒有任何問題,但是age=45程式的執行結果卻是“青年人”,這顯然是有問題的!

    **對於任何if else語句,表面上看起來else後沒有任何條件,或者esle if後面只有一個條件——但是這只是表面現象,因為else的含義是"否則"——else本身就是一個條件!else的隱含條件是對前面的條件取反。**因此,上面的程式碼可以修改為:

    int age = 45;
    if (age > 60) {
        System.out.println("老年人");
    } else if (age > 40) {
        System.out.println("中年人");
    } else if (age > 20) {
        System.out.println("青年人");
    }
    複製程式碼

    上面的程式執行之後就能得到正確的結果,其實上面的程式就等同於下面的這段程式碼:

    int age = 45;
    if (age > 60) {
        System.out.println("老年人");
    }
    //在原本的if條件中增加了else的隱含條件
    if (age > 40 && !(age > 60)) {
        System.out.println("中年人");
    }
    //在原本的if條件中增加了else的隱含條件
    if (age > 20 && !(age > 40 && !(age > 60)) && !(age > 60) {
        System.out.println("青年人");
    }
    複製程式碼

    也就是說上面的判斷邏輯轉為如下三種情況:

    • age大於60歲,判斷為“老年人”
    • age大於40歲,並且age小於等於60歲,判斷為“中年人”
    • age大於20歲,並且age小於等於40歲,判斷為“青年人”。

    上面的邏輯才是實際希望的判斷邏輯。因此,使用if...else語句進行流程控制的時候,一定不要忽略了else所帶的隱含條件。

    如果每次都去計算if條件和else條件的交集也是一件麻煩的事情,為了避免出現上述的錯誤,在使用if...else語句的時候有一條基本規則: 總是優先把包含範圍小(子集)的條件放在前面處理。

    如 age>60是age>40的子集,而age>40是age>20的子集,把自己放在前面進行處理就可以避免忽略else的隱含條件而造成程式錯誤。

switch語句

switch語句由一個控制表示式和多個case標籤組成,switch語句根據表示式的值將控制流程轉移到了多個case標籤中的某一個上。其形式如下:

switch (expression) {
    case value1: {
        statement...;
        break;
    }
    case value2: {
        statement...;
        break;
    }
    ...
    case valuen: {
       statement...;
       break;
    }
    default: {
        statement...;
    }
}
複製程式碼

switch語句先執行對expression的求職,然後依次匹配value1、value2...valuen,遇到匹配的值則從對應的case塊開始向下執行程式碼,如果沒有任何一個case塊能夠匹配,則會執行default塊中的程式碼。

switch支援的資料型別

Java7之後,對switch語句進行了一定的增強,switch語句後的控制表示式的資料型別只能是byteshortcharint四種整型型別,還有列舉型別String型別。

  • switch中不支援浮點數,因為二進位制儲存浮點數字不是完全精確的,所以對浮點數進行相等性判斷並不可靠
  • switch中不支援boolean型別的資料,因為對於boolean型別,使用if語句是更好的選擇
  • switch中不支援long整型,我認為一個原因是根本使用不了這麼多的分支

同時,我們需要注意的是,case關鍵字後面只能跟一個和switch中表達式的資料型別相同的常量表達式

switch的貫穿

需要注意的是,switch語句並不是一個多選一的分支控制語句,考慮到物理上的多種情況可能是邏輯上的一種情況switch塊中語句的執行會貫穿標號”,除非遇到break語句。如下程式碼:

public void switchTest(int a) {
    switch (a) {
        case 1: System.out.print("1");
        case 2: System.out.print("2");
        case 3: System.out.print("3");
        default: System.out.print("default");
    }
}

switchTest(1);
複製程式碼

上面的程式碼中執行switchTest(1)方法輸出的結果如下:

123default
複製程式碼

但是如果在某個case塊中加入break語句,那麼這個case塊就不會被貫穿,如下:

public void switchTest(int a) {
    switch (a) {
        case 1: {
            System.out.print("1");
            break;
        }
        case 2: System.out.print("2");
        case 3: System.out.print("3");
        default: System.out.print("default");
    }
}

switchTest(1);
switchTest(2);
複製程式碼

上面的程式碼中執行switchTest(1)輸出結果為:

1
複製程式碼

可見加入break之後沒有被貫穿,

而執行switchTest(2)輸出結果為:

23default
複製程式碼

關於break語句後面會有詳細的介紹

我對case程式碼塊的理解。

其實case程式碼塊和其他的程式碼塊在本質上是一樣的,它有自己的作用域,如下:

public static void main(String[] args) {
       int a = 1;
       switch(a) {
           case 1: {
               int b = 1;
               System.out.println(b);
           }
           case 2: {
               int b = 2;
               System.out.println(b);
           }
       }
    }
複製程式碼

上述main方法執行的結果是

1
2
複製程式碼

case程式碼塊和如下的程式碼塊本質上是一樣的:

//普通的程式碼塊,使用的主要目的就是建立一個單獨的詞法作用域
{
	int a = 3;
    System.out.println(a);
}

//帶標籤的程式碼塊,除了能夠建立一個單獨的詞法作用域,還能夠使用break進行一定的流程控制
labelName: {
    int a = 3;
    System.out.println(a);
}
複製程式碼

case程式碼塊不同的地方在於,case程式碼塊只能出現在switch語句中,並且其後面跟著一個跟switch中的表示式的值的型別相同的常量表達式,其實case的作用就是宣告瞭一個個的錨點,能夠跟switch後的表示式的值匹配的哪個case錨點就是switch程式開始執行的地方,這也就解釋了switch的貫穿

基本上所有擁有switch語句的語言中,case的作用大都是相同的,同時switch也都具有貫穿的特性。

迴圈結構

迴圈語句可以在滿足迴圈條件的情況下,反覆執行某一段程式碼,這段被重複執行的程式碼被稱為迴圈體。迴圈語句可能包含如下四個部分

  • 初始化語句(init_statement):一條或者多條語句,這些語句用於完成一些初始化工作,初始化語句在迴圈體開始執行之前執行。
  • 迴圈條件(test_expression):這個測試表示式的值必須是boolean型別的,這個表示式決定是否繼續執行迴圈體
  • 迴圈體(loop_statement):這個部分是迴圈的主體,如果迴圈條件成立,這個程式碼塊將被一直執行。
  • 迭代語句(iteration_statement):這個部分在每一次迴圈體結束之後,在對測試表示式求值之前執行,通常用於改變迴圈條件相關的變數,使得迴圈在合適的時候結束。

上面的四個部分只是一般性的分類,並不是每個迴圈中都非常明確的分出了這4個部分。

直到型迴圈結構

直到型迴圈結構的特點就是迴圈體會無條件的執行一次,在Java中提供了do while語句對直到型迴圈提供了支援。

do while語句

do while迴圈的結構如下:

init_statement
do {
	loop_statement
	iteration_statement
	[break/continue] //[]代表可選的
} while(test_expression); //不要忘記末尾的分號
複製程式碼

需要注意的是,iteration_statementloop_statement之間並沒有明確的順序關係,但是我們需要注意的是,iteration_statement應該位於continue語句之前,否則會容易造成死迴圈,如下:

public static void main(String[] args) {
    int i = 0;
    do {
        System.out.println(i);
        if (i%3 == 1)
            continue;
        i++;
    } while (i < 9);
}
複製程式碼

執行上面的main方法將會產生死迴圈。所以迭代語句應該放在continue語句之前

當型迴圈結構

當型迴圈結構的特點就是,在執行迴圈體之前就進行迴圈條件的檢測,如果迴圈條件在剛開始就不滿足,那麼迴圈體一次都不會執行。

Java提供了while語句和for語句對當型迴圈結構提供支援

while語句

while迴圈的語法格式如下:

[init statement]
while(test_expression) {
	loop_statement
	[iteration_statement]
	[break/continue] //跟do類似的是,如果存在迭代語句,要放在continue語句之前,防止造成死迴圈
}
複製程式碼
for語句

for迴圈是最簡潔的一種迴圈語句,同時其寫法也是最為靈活的,它把迴圈體獨立出來,同時固定了迭代語句和測試表示式的執行順序,for語句的基本格式如下:

for ([init_statement]; [test_statement],[iteration_statement]) {
    loop_statement
}
複製程式碼

可以看到,for語句彙總的init_statement、test_statement和iteration_statement都是可選的,這就意味著for迴圈可以完全取代while迴圈,for迴圈是使用頻率最高的迴圈語句,原因如下:

  • 在for迴圈的初始化語句中宣告的變數其作用於可以限制在for迴圈的語句體之內。
  • for迴圈的迭代語句沒有與迴圈體放在一起,因此即使是在執行迴圈體的時候遇到了continue語句提前結束本次迴圈,迭代語句也會得到執行。

雖然for迴圈的寫法比較自由,但是我們在使用for迴圈的時候儘量採用標準的寫法,這樣可以充分利用for迴圈的特性,同時提高程式碼的可讀性,所以這裡就不介紹關於for迴圈的騷操作了。

增強的for語句

Java5中,為Iterable陣列型別新增了增強的for語句,其格式如下:

for ([variableModifier] Type variableDeclaratorId: Expression) {
    loop_statement
}
複製程式碼

需要注意的是,上述Express的型別必須是Iterable或者是陣列型別,否則會產生編譯錯誤

增強的for迴圈只是一種語法糖,它在編譯器的解糖階段就會轉換成普通的for迴圈,增強的for迴圈會按照如下規則轉譯為基本的for語句:

  • 如果Expression 的型別是 Iterable 的子型別,那麼就按照如下方式進行轉譯: 以如下程式碼為例
List<String> nameList = Arrays.asList("zhangsan","lisi","wangwu");

//增強for迴圈
for(String name: nameList) {
   System.out.println(name);
}

//上述的增強的for迴圈就等價於如下的普通迴圈
for (Iterator<String> i = Expression.iterator(); i.hasNext(); ) {
   String name = i.next();
   System.out.println(name);
}
複製程式碼
  • 否則, Expression必須具有陣列型別,這時候其轉換方式如下: 以如下程式碼為例:
String[] nameArr = new String[]{"zhangsan","wangwu"};

//增強的for迴圈
for(String name: nameArr) {
   System.out,println(name);
}

//上述的增強的for迴圈等價於如下的普通for迴圈
for (int i = 0; i< nameArr.length; i++) {
   String name = nameArr[i];
   System.out.println(name);
}
複製程式碼

更精細的流程控制

分支和迴圈結構只是為我們提供了基本的流程控制功能,但是在迴圈中有時候我們需要提前結束某次或者是整個迴圈,又或者某段程式碼在執行到一定的流程的時候需要提前結束,這樣基本的流程控制是滿足不了的。

雖然Java中放棄了飽受詬病的goto語句,但是也為我們提供了一些對程式流程進行更加精細控制的方式。

前面也對break語句和continue語句做過一些介紹了,下面進行一下詳細的說明。

break語句

break語句會將控制流程轉移到包含它的語句或者塊之外。它分為帶標號和不帶標號兩種情況。不帶標號的 break語句只能用在迴圈結構或者是switch語句之中,而帶標號的 break可以使用在任何語句塊中

不帶標籤的break語句

不帶標號的break語句檢視將控制流轉移到包圍它的最內層的switchdowhile或者for語句中(一定要注意這一點,因為這四種語句之間可以相互巢狀或者是自巢狀)。這條語句被稱為break目標,然後立即正常結束。

如果該不帶標號的break語句不被包含在任何switchdowhile或者for語句中,那麼就會編譯失敗。

帶標籤的break語句

在瞭解帶標籤的break語句之前,我們先來瞭解一下什麼是標號語句

語句前可以有標號字首,如下:

label: {
    System.out.println("a1");
}
複製程式碼

java程式語言沒有任何goto語句,識別符號語句標號會用於出現在標號語句內的任何地方的break或者continue語句之上。

標號本質上其實也是一個識別符號,而這個識別符號的作用域就是其直接包含的語句的內部,因為Java中規定,只能在該標號所包含的語句之中才能引用這個標號。,如下程式碼說明瞭標號語句的標號識別符號的作用域:

public class a {
    public static void main(String[] args) {

        a: {
            int a = 3;
            System.out.println("a1");
        }
		
        //這個地方編譯通過, 說明a的作用域並不是在main方法中,而是在它包含的語句之中
        a: {
            System.out.println("a2");
        }
    }
}
複製程式碼

同時,通過上面的程式碼我們也可以看出,不同型別的識別符號之間是互不影響的(Java中的識別符號有型別名、變數名、標號等等),即對於將相同的識別符號同時用做標號和包名、類名、介面名、方法名、域名城、方法引數名、或者區域性變數名這種做法,Java沒有做出任何限制

下面正式進入帶標號的break語句的講解

帶有標號的break語句檢視將控制流轉移到將相同標號作為其標號的標號語句,該語句稱為break目標語句,然後立即結束。

在帶標號的break語句中break目標不必是switchdowhile或者for語句

需要注意的是,前面講到過標號語句表標號的作用範圍是在標號語句的語句體內部,所以被break的標號語句必須包含break語句

前面的描述中稱“檢視轉移控制流程”而不是"直接轉移控制流程",是因為如果在 break目標 內有任何try語句,其try子句或者catch子句包含break語句,那麼在控制流程轉移到 break 目標之前,這些 try語句的所有的 finally 子句會按照從最內部到最外部的數學被執行,而 finally 子句的猝然結束都會打斷break語句觸發的控制流轉移

continue語句

continue語句只能出現在while、do或者for語句彙總,這三種語句被稱為迭代語句。控制流會傳遞到迭代語句的迴圈持續點上。

continue語句也有代標號和不帶標號兩種使用方式,和break語句不同的是,continue語句的這兩種使用方式都只能出現在迭代語句中,下文進行詳細的介紹

不帶標籤的continue語句

不帶標籤的continue試圖將控制流轉移到包圍它的最內層的switchdowhile或者for語句中(一定要注意這一點,因為這四種語句之間可以相互巢狀或者是自巢狀)。這條語句被稱為continue目標,然後立即結束當前的迭代並進行下一輪的迭代。

如果該不帶標號的continue語句不被包含在任何switchdowhile或者for語句中,那麼就會編譯失敗。

帶標籤的continue語句

前面已經對標號進行了介紹,下面我們直接進入帶標籤的continue語句的講解

帶標號的continue語句檢視將控制流轉移到將相同標號作為標號的標號語句,但是同時,這個標號語句必須是一個迭代語句,這條語句被稱為continue目標然後理解結束當前的迭代並進行新一輪的迭代。

如果continue目標不是一個迭代語句,那麼就會產生一個編譯時錯誤。

前面的描述中稱“檢視轉移控制流程”而不是"直接轉移控制流程",是因為如果在 continue目標 內有任何try語句,其try子句或者catch子句包含continue語句,那麼在控制流程轉移到 continue目標之前,這些 try語句的所有的 finally 子句會按照從最內部到最外部的數學被執行,而 finally 子句的猝然結束都會打斷 continue 語句觸發的控制流轉移

其他語句

return 語句

return 語句會將控制流返回給方法或構造器的呼叫者。return語句必須被包含在可執行成員中(方法構造器lambda表示式,return語句也分為兩種,一種是帶有返回值的,一種是不帶有返回值的。

需要注意的是,return語句觸發的流程轉移也是檢視轉移控制流,而不是直接轉移控制流,原因也是try的finally子句

對於return語句,我們不需要說明太多。

throw 語句

throw語句會導致異常物件被丟擲,這將會檢視將控制流程進行轉移,有可能會退出多個語句和多個構造器、例項初始化器、靜態初始化器和域初始化器的計算以及方法呼叫,知道找到可以捕獲丟擲值的try語句。如果沒有這種try語句,那麼執行throw的執行緒的執行會在該執行緒所屬的執行緒組上呼叫uncaughtException 方法後被終止。

上述也提到了檢視轉移控制流程,也是因為try的finally子句,如下:

 try {
     throw new RuntimeException("故意丟擲");
} finally {
     System.out.println("還是會執行");
     throw new RuntimeException("finally中丟擲");
}
複製程式碼

但是上述的用法是基本不會出現的,因為在一個執行體中丟擲異常就是為了給呼叫這個執行體的上層執行體提供一種提示,並期望呼叫者能夠處理這種異常。

關於異常會在後面進行更加詳細的說明。

synchronized 語句

這涉及到了多執行緒程式設計,以後會進行專門的介紹。

try 語句

try語句會執行一個語句塊。**如果丟擲了值並且try語句有一個或者多個可以捕獲它的catch子句,那麼控制流就會轉移到第一個這種catch子句上。**這一點在後面講異常處理的時候也會講到。

如果try語句有finally子句,那麼將會執行另一個程式碼塊,無論try塊是正常結束還是猝然結束,並且無論控制流之前是否轉移到某個catch塊上。

try語句的格式如下所示,有三種形式:

// try-catch
try {
    statement
} catch(Type1 | Type2... e) {
    statement
}

//try-finally
try {
    statement
} finally {
    statement
}

//try-catch-finally
try {
    statement
} catch (Type1 | Type2... e) {
    statement
} finally {
    statement
}
複製程式碼

在try語句後緊跟的語句塊我們稱之為try語句的try塊

在finally關鍵字之後緊跟的語句塊我們稱之為finally塊

try語句的catch子句通常被稱為異常處理器catch子句有且僅有一個引數,被稱為異常引數,異常引數可以將它的型別表示成單一的型別別,或者表示成兩個或者更多型別的聯合體(這些型別稱為可選項),聯合體中的可選項在語法上使用|進行分割

需要注意的是,當catch子句的異常引數的型別是一個型別聯合體的時候,這個異常引數就一定是final型別的,要麼是隱式的,當然也可以顯示宣告為final的,但是這時候應該講型別聯合體看做是一個型別,final修飾符只需要書寫一次就可以了。如下:

try {
    throw new RuntimeException();
} catch (final UnsupportedOperationException | NullPointerException e) { //final修飾符只需在型別聯合體前面書寫一次就可以了,這時候要把型別聯合體看做一個型別
    System.out.println("是可以改變的!");
}
複製程式碼

這是因為catch型別處理器完全依賴於try塊中丟擲的異常的型別。而使用了型別聯合體之後,異常引數的型別就只能在執行時才能確定,所以必須是final的,在被捕獲的時候初始化一次。

同時我們上面也提到過一句話,**如果丟擲了值並且try語句有一個或者多個可以捕獲它的catch子句,那麼控制流就會轉移到第一個這種catch子句上。**這就提醒我們在使用catch子句的時候需要注意,要把子型別別放在前面進行捕獲,否則就會被父型別別的異常處理器攔截。同時,在catch子句中使用型別聯合體的時候,型別聯合體中的各個可選項之間不能具有任何的父子關係。

try語句的執行流程沒有什麼好說的,我們只需要記住一點,finally子句肯定會被執行

帶資源的 try 語句

帶資源的try語句是用變數(被稱為資源)來引數化的,這些資源在try塊執行之前被初始化,並且在try塊執行之後,自動地以與初始化相反的順序被關閉。當資源會被自動關閉的時候,catch子句和finally子句經常不是必須的。

關於資源,我們需要注意以下幾點:

  • try中的資源變數肯定是final的,如果不顯式宣告為final,則會被隱式宣告為final,這是因為需要對宣告的資源進行自動關閉,所以不允許修改資源變數的指向。
  • 在帶資源的try語句中宣告的資源必須是AutoCloseable的子型別,否則會產生編譯錯誤。
  • 資源是按照從左到右的順序被初始化的。如果某個資源初始化失敗了(即初始化表示式丟擲了異常),那麼所有已經完成了初始化的資源都將被關閉。如果所有的資源都初始化成功了,那麼try塊就會正常執行,然後帶資源的try語句的所有非null資源都將被關閉。
  • 資源將以它們被初始化的順序相反的順序被關閉。資源只有在它們被初始化為非null值的時候才會被關閉。在關閉資源時丟擲的異常不會阻止其他資源的關閉,其實最終帶資源的try語句都會被轉化為try-finally語句

不可達語句

如果某條語句因為它是不可達的而不能被執行,那麼就是一個編譯時錯誤。我們只需知道這種語句的存在,在不可達表現的非常明顯的時候IDE就會給出提示,但是有時候會有錯誤的邏輯造成的不可達語句,就需要我們自己對程式碼的結構進行分析了。

總結

以上就是我對Java中的表示式和操作符的理解和介紹,如有不當之處,歡迎進行指正。