電腦程式的思維邏輯 (9) - 條件執行的本質

來源:互聯網
上載者:User

標籤:

條件執行

前面幾節我們介紹了如何定義資料和進行基本運算,為了對資料有透徹的理解,我們介紹了各種類型資料的二進位表示。

現在,讓我們回顧程式本身,只進行基本操作是不夠的,為了進行有現實意義的操作,我們需要對操作的過程進行流程式控制制。流程式控制制中最基本的就是條件執行,也就是說,某些操作只能在某些條件滿足的情況下才執行,在一些條件下執行某種操作,在另外一些條件下執行另外某種操作。這與交通控制中的紅燈停、綠燈行條件執行是類似的。

Java中表達這種流程式控制制的基本文法是If語句。

if

if的文法為:

if(條件陳述式){  代碼塊}


if(條件陳述式) 代碼; 

它表達的含義也非常簡單,只在條件陳述式為真的情況下,才執行後面的代碼,為假就不做了。具體來說,條件陳述式必須為布爾值,可以是一個直接的布爾變數,也可以 是變數運算後的結果,我們在第3節介紹過,比較運算和邏輯運算的結果都是布爾值,所以可作為條件陳述式。條件陳述式為true,則執行括弧{}中的代碼,如果後面沒有括弧,則執行後面第一個分號(;)前的代碼。

如,只在變數為偶數的情況下輸出:

int a=10;if(a%2==0){   System.out.println("偶數");}

int a=10;if(a%2==0) System.out.println("偶數");

if的陷阱

初學者有時會忘記在if後面的代碼塊中加括弧,有時希望執行多條語句而沒有加括弧,結果只會執行第一條語句。建議所有if後面都跟括弧。

if/else

if實現的是條件滿足的時候做什麼操作,如果需要根據條件做分支,即滿足的時候執行某種邏輯,而不滿足的時候執行另一種邏輯,則可以用if/else。

if/else的文法是:

if(判斷條件){   代碼塊1}else{   代碼塊2}

if/else也非常簡單,判斷條件是一個布爾值,為true的時候執行代碼塊1,為假的時候執行代碼塊2。

三元運算子

我們之前介紹了各種基本運算,這裡介紹一個條件運算,和if/else很像,叫三元運算子,文法為:

判斷條件 ? 運算式 1 : 運算式2

三元運算子會得到一個結果,判斷條件為真的時候就返回運算式1的值,否則就返回運算式2的值。三元運算子經常用於對某個變數賦值,例如求兩個數的最大值:

int max = x > y ? x : y;

三元運算子完全可以用if/else代替,但在某些情境下書寫更簡潔。

if/else if/else

如果有多個判斷條件,而且需要根據這些判斷條件的組合執行某些操作,則可以使用if/else if/else。

文法是

if(條件1){  代碼塊1}else if(條件2){  代碼塊2}...else if(條件n){   代碼塊n}else{   代碼塊n+1} 

if/else if/else也比較簡單,但可以表達複雜的條件執行邏輯,它逐個檢查條件,條件1滿足則執行代碼塊1,不滿足則檢查條件2,...,最後如果沒有條件滿 足,且有else語句,則執行else裡面的代碼。最後的else語句不是必須的,沒有就什麼都不執行。

if/else if/else陷阱

需要注意的是,在if/else if/else中,判斷的順序是很重要的,後面的判斷只有在前面的條件為false的時候才會執行。初學者有時會搞錯這個順序,如下面的代碼:

if(score>60){  return "及格";}else if(score>80){  return "良好";}else{  return "優秀"}

看出問題了吧?如果score是90,可能期望返回"優秀",但實際只會返回"及格".

switch

在if/else if/else中,如果判斷的條件基於的是同一個變數,只是根據變數值的不同而有不同的分支,如果值比較多,比如根據星期幾進行判斷,有7種可能性,或者根據英文字母進行判斷,有26種可能性,使用if/else if/else顯的比較囉嗦,這種情況可以使用switch,switch的文法是:

switch(運算式){   case 值1:           代碼1; break;   case 值2:           代碼2; break;           ...    case 值n:          代碼n; break;    default: 代碼n+1}

switch也比較簡單,根據運算式的值執行不同的分支,具體來說,根據運算式的值找匹配的case,找到後,執行後面的代碼,碰到break時結束,如果沒有找到匹配的值則執行default中的語句。

運算式值的資料類型只能是 byte, short, int, char, 枚舉, 和String (Java 1.7以後)。枚舉和String我們在後續文章介紹。

switch會簡化一些代碼的編寫,但break和case文法會對初學者造成一些困惑。

容易忽略的break

break是指跳出switch語句,執行switch後面的語句。每條case語句後面都應該跟break語句,否則的話它會繼續執行後面case中的代碼直到碰到break語句或switch結束,例如:下面的代碼會輸出所有數字而不只是1.

int a = 1;switch(a){case 1:    System.out.println("1");case 2:    System.out.println("2");default:    System.out.println("3");}

case堆疊

case語句後面可以沒有要執行的代碼,如下所示:

char c = ‘x‘;//某字元switch(c){   case ‘A‘:   case ‘B‘:   case ‘C‘:        System.out.println("A-Z");break;   case ‘D‘:       ....}

case ‘A‘/‘B‘後都沒有緊跟要執行的代碼,他們實際會執行第一塊碰到的代碼,即case ‘C‘匹配的代碼

條件小結

條件執行總體上是比較簡單的,單一條件滿足時執行某操作使用if,根據一個條件是否滿足執行不同分支使用if/else,表達複雜的條件使用if/else if/elese,條件賦值使用三元運算子,根據某一個運算式的值不同執行不同的分支使用switch。

從邏輯上講,if/else, if/else if/else,三元運算子,switch都可以只用if代替,但使用不同的文法表達更簡潔,在條件比較多的時候,switch從效能上也更高(馬上解釋為什麼)。

條件本質

正如我們探討資料類型的時候,研究資料的二進位表示一樣,我們也來看下這些條件執行具體是怎麼實現的。

程式最終都是一條條的指令,CPU有一個指令指標,指向下一條要執行的指令,CPU根據指標的指示載入指令並且執行。指令大部分是具體的操作和運算,在執行這些操作時,執行完一個操作後,指令指標會自動指向挨著的下一個指令。

但有一些特殊的指令,稱為跳轉指令,這些指令會修改指令指標的值,讓CPU跳到一個指定的地方執行。跳轉有兩種,一種是條件跳轉,另一種是無條件跳轉。條件跳轉檢查某個條件,滿足則進行跳轉,無條件跳轉則是直接進行跳轉。

if, else實際上會轉換為這些跳轉指令,比如說下面的代碼:

1 int a=10;2 if(a%2==0)3 {4    System.out.println("偶數");5 }6 //其他代碼

轉換到的轉移指令可能是:

1 int a=10;2 條件跳轉: 如果a%2==0,跳轉到第4行3 無條件跳轉:跳轉到第7行4 {5    System.out.println("偶數");6 }7 //其他代碼

你可能會奇怪其中的無條件跳轉指令,沒有它不行嗎?不行,沒有這條指令,不管什麼條件,括弧中的代碼都會執行。

不過,對應的跳轉指令也可能是:

1 int a=10;2 條件跳轉: 如果a%2!=0,跳轉到第6行3 {4    System.out.println("偶數");5 }6 //其他代碼

這個就沒有無條件跳轉指令,具體怎麼對應和編譯器實現有關。在單一if的情況下可能不用無條件跳轉指令,但稍微複雜一些的情況都需要。if, if/else, if/else if/else, 三元運算子都會轉換為條件跳轉和無條件跳轉。但switch不太一樣。

switch的轉換和具體系統實現有關,如果分支比較少,可能會轉換為跳轉指令。但如果分支比較多,使用條件跳轉會進行很多次的比較運算,效率比較低,可能會使用一種更為高效的方式,叫跳轉表。跳轉表是一個映射表,儲存了可能的值以及要跳轉到的地址,形如:

值1 代碼塊1的地址
值2 代碼塊2的地址
...  
值n 代碼塊n的地址

跳轉表為什麼會更為高效呢?因為,其中的值必須為整數,且按大小順序排序。按大小排序的整數可以使用高效的二分尋找,即先與中間的值比,如果小於中間的值則在開始和中間值之間找,否則在中間值和末尾值之間找,每找一次縮小一倍尋找範圍。如果值是連續的,則跳轉表還會進行特殊最佳化,最佳化為一個數組,連找都不用找了,值就是數組的下標索引,直接根據值就可以找到跳轉的地址。即使值不是連續的,但數字比較密集,差的不多,編譯器也可能會最佳化為一個數組型的跳轉表,沒有的值指向default分支。

程式原始碼中的case值排列不要求是排序的,編譯器會自動排序。之前說switch值的類型可以是byte, short, int, char, 枚舉和String。其中byte/short/int本來就是整數,在上節我們也說過,char本質上也是整數,而枚舉類型也有對應的整 數,String用於switch時也會轉換為整數(通過hashCode方法,後文介紹),為什麼不可以使用long呢?跳轉表值的儲存空間一般為32位,容納不下long。

總結

條件執行的文法是比較自然和容易理解的,需要注意的是其中的一些文法細節和陷阱。它執行的本質依賴於條件跳轉、無條件跳轉和跳轉表。

條件執行中的跳轉只會跳轉到跳躍陳述式以後的指令,能不能跳轉到之前的指令呢?

----------------

未完待續,查看最新文章,敬請關注公眾號“老馬說編程”(掃描下方二維碼),深入淺出,老馬和你一起探索Java編程及電腦技術的本質。原創文章,保留所有著作權。

電腦程式的思維邏輯 (9) - 條件執行的本質

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.