本文中使用到的工具是Intellij IDEA和JDK 8,需要安裝兩款工具的請檢視這兩篇教學:點我檢視安裝JDK8/11/17教學、點我檢視安裝Intellij IDEA教學。
前面我們寫過的程式碼,都是在main
方法中自上到下按順序執行的,舉一個程式碼栗子:
/**
* 計算西瓜的價格
*
* @author iCode504
* @date 2023-10-31
*/
public class MyWatermelonDemo1 {
public static void main(String[] args) {
int price = 2; // 西瓜的單價
int weight = 10; // 西瓜的重量(公斤)
int totalPrice = price * weight; // 購買價格
System.out.println("西瓜的價格是: " + totalPrice + "元");
}
}
這段程式碼就是先定義西瓜的單價、再定義西瓜的重量,然後計算西瓜的價格,最後對價格進行輸出。像這樣程式碼從上到下執行的結構就是順序結構。
程式一共有三種控制結構:順序結構、選擇結構和迴圈結構。其中選擇結構是根據條件判定的結果,選擇執行不同的程式碼,例如:紅燈停,綠燈行。判斷條件就是交通訊號燈的狀態。
Java也有選擇結構,並且有多種型別的條件判斷語句:單分支的if
語句、雙分支的if-else
語句、多分支的if-else if-else
語句、if
巢狀語句和switch
語句。
單分支if
語句的語法如下:
if (條件表示式) {
執行程式碼...
}
其中條件表示式的計算結果必須是boolean
型別。如果條件表示式的計算結果是true
,那麼就會執行if
內部的程式碼;如果表示式為false
,此時就會跳過if
程式碼塊(也就是if
內部程式碼不執行),概念圖如下:
我們可以在if
程式碼塊中可以編寫多個執行語句。
以下是if
的使用案例:
案例:之前長春下了大暴雪,氣溫驟降,我想在某寶上買幾雙棉襪子,假設每雙襪子4元,請確保輸入的數位大於0再計算購買價格。
案例:輸入一個數位,如果能被10整除,則輸出內容為:xx能被10整除。如果能被15整除,則輸出內容為:xx能被15整除。
解決本題的關鍵點在於被10整除和被15整除的條件怎麼計算。其實前面我們學過取餘運運算元,如果數值number
能被10整除,那麼可以寫成number % 10 == 0
;如果數值number
能被15整除,那麼可以寫成number % 15 == 0
。這兩個布林表示式可以寫入到兩個if
語句中:
import java.util.Scanner;
/**
* 使用if語句判斷數位能否被10和15整除
*
* @author iCode504
* @date 2023-11-07
*/
public class IfDemo2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入數位: ");
int number = scanner.nextInt();
// 整除10的條件:數位對10取餘等於0
if (number % 10 == 0) {
System.out.println(number + "能被10整除");
}
// 整除15的條件:數位對15取餘等於0
if (number % 15 == 0) {
System.out.println(number + "能被15整除");
}
}
}
輸入不同的數位以後,會得到如下的執行結果:
案例:輸入兩個整數,如果輸入的第一個數位比第二個數位大,那麼執行兩數交換,並將交換結果輸出。否則不交換,正常輸出兩個數
本題的條件表示式是輸入的兩個數位的比較,無論數位大小比較結果如何,都需要將結果進行輸出,我們可以將輸出語句放到if
語句後面執行。
兩數交換有多種方式,較為穩妥的方式是再定義一個臨時變數,用這個臨時變數來接收第一個變數值,然後第二個變數值賦值給第一個變數,最後將臨時變數的值賦值給第二個變數。
以下是範例程式碼:
import java.util.Scanner;
/**
* 單分支if語句實現兩數交換
*
* @author iCode504
* @date 2023-11-11
*/
public class IfDemo3 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入兩個整數");
int number1 = scanner.nextInt();
int number2 = scanner.nextInt();
int temp = 0; // 定義臨時變數
if (number1 > number2) { // 實現兩數交換功能
temp = number1;
number1 = number2;
number2 = temp;
}
System.out.println("第一個數是: " + number1 + ", 第二個數是: " + number2);
}
}
執行結果:
當然,除了上述方式能實現兩數交換,還有其他的方式。
方式一:使用加減法進行交換(推薦使用整數,浮點數不推薦,因為浮點數計算時會出現誤差)
int number1 = 3;
int number2 = 2;
number1 = number1 + number2; // number1 = 3 + 2 = 5
number2 = number1 - number2; // number2 = 5 - 2 = 3
number1 = number1 - number2; // number1 = 5 - 3 = 2
方式二:使用位運運算元進行交換(推薦使用整數,此處涉及到二進位制互斥或運算,互斥或運算可以檢視這篇文章:入門篇-其之六-Java運運算元(中)第四部分-位運運算元)
int number1 = 3;
int number2 = 2;
number1 = number1 ^ number2; // 3 ^ 2 = 1
number2 = number1 ^ number2; // 3 ^ 1 = 2
number1 = number1 ^ number2; // 1 ^ 2 = 3
當然,上述三種方式我個人最推薦的還是第一種定義臨時變數的方式,這種方式對處理浮點型別的數進行交換很友好,如果使用了下面兩種方式的話,可能會在計算過程中出現精度損失的問題。後兩種方式的好處是不需要定義第三個變數,只需要進行一系列運算即可完成整數值的交換。
前面講過的單分支if
語句只有在布林表示式為true
的時候執行其內部的內容,但是如果在布林表示式為false
的時候不會做任何事情。為了解決上述問題,Java為我們提供了if-else
雙分支語句。以下是雙分支if-else
語句程式碼結構:
if (條件表示式) {
執行程式碼1...
} else {
執行程式碼2...
}
如果條件表示式的值是true
,那麼就執行if
內部的語句,如果條件表示式為false
,此時就進入else
程式碼塊。執行流程圖如下:
案例:我們還是以上述買襪子為例,最近雙十一打折,如果買了10雙及以上襪子,此時每雙襪子打八折優惠,否則打九折優惠(襪子的價格假設是4元/雙)。
題目中的條件表示式在於要買的襪子數量是否大於等於10,如果是,價格打8折,否則打9折,使用剛剛講到的if-else
語句即可搞定。
當然,這道題中還有一個隱藏的細節需要我們處理:輸入襪子的數量需要大於0,否則判定為無效,這個可以使用單分支if
語句就可以搞定。
以下是範例程式碼:
import java.util.Scanner;
/**
* if-else雙分支語句
*
* @author iCode504
* @date 2023-11-11
*/
public class IfDemo4 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入要購買的襪子數量");
int count = scanner.nextInt();
double price = 4; // 每雙襪子的價格
double totalPrice = 0.0; // 預設總價格為0.0
// 需要保證輸入的襪子數量要大於0
if (count > 0) {
// 如果襪子的數量大於10,此時每雙襪子的價格為8折,即每雙襪子的價格乘以0.8,九折的計算方式和上述內容同理
if (count >= 10) {
totalPrice = price * 0.8 * count;
} else {
totalPrice = price * 0.9 * count;
}
}
System.out.println("購買" + count + "雙襪子,雙十一期間購買價格是" + totalPrice + "元");
}
}
執行結果(可能會出現浮點數計算不準確的情況,屬於正常現象):
雙分支的if-else
語句對於條件表示式為true
和false
的時候比較適用,但是如果對於一個問題而言,此時經過分析可能存在多個條件表示式時,if-else
語句並不能很好地完成任務,此時Java為我們提供了另一種分支語句:if-else if-else
語句,其語法格式如下:
if (條件表示式1) {
執行程式碼1...
} else if (條件表示式2) {
執行程式碼2...
} else if (條件表示式3) {
執行程式碼3...
} ...
else if (條件表示式n) {
執行程式碼n...
} else {
不符合上述所有條件表示式時執行else程式碼...
}
以上述語法格式為例,其執行順序為:
true
,那麼執行程式碼1,如果結果是false
,此時就會跳轉到第一個else if
。true
,那麼執行程式碼2,如果結果是false
,那麼就會跳轉到第二個else if
。true
,那麼執行程式碼3,如果結果是false
,那麼就會跳轉到下一個else if
,依次類推。false
)時,就會執行else
中的語句。多分支的if-else if-else
語句中,你可以寫任意個else if
,每個else if
需要寫上條件表示式。
當然,最後的else
也是可選的,if
和else-if
搭配使用也是可以的。以下是執行流程圖:
案例:已知長春的地鐵/輕軌票價標準如下
- 0-7公里(含7公里),票價2元;
- 7-13公里(含13公里),票價3元;
- 13-19公里(含19公里),票價4元;
- 19-27公里(含27公里),票價5元;
- 27-35公里(含35公里),票價6元;
- 35公里以上每增加10公里,增加1元
假設從1號線紅嘴子地鐵站到8號線廣通路輕軌站的距離是31.4公里,從2號線汽車公園地鐵站到2號線東方廣場地鐵站的距離是20.5公里,從4號線長春站北輕軌站到4號線天工路輕軌站的距離是16.3公里。
輸入上述里程,利用程式計算出乘坐軌道交通所需要的票價。
四、if的巢狀使用
正如標題所講,
if
語句可以巢狀使用。舉個栗子:在main
方法中,假設已經存在了一個if-else
語句,那麼在這個if
程式碼塊或者else
程式碼塊還可以存在條件判斷語句,下面就是其中一種if
的巢狀使用方式(事實上它可以if
程式碼塊可以進行多種組合巢狀使用):if (條件表示式1) { if (條件表示式2) { 執行程式碼1... } else { 執行程式碼2... } } else { if (條件表示式3) { 執行程式碼3... } else { 執行程式碼4... } }
它的執行流程如下:
- 如果條件表示式1的執行結果是
true
,進入條件表示式2,如果條件表示式2執行結果是true
,此時執行程式碼1。- 如果條件表示式1的執行結果是
true
,進入條件表示式2,如果條件表示式2執行結果是false
,此時執行程式碼2。- 如果條件表示式1的執行結果是
false
,進入條件表示式3,如果條件表示式3執行結果是true
,此時執行程式碼3。- 如果條件表示式1的執行結果是
false
,進入條件表示式3,如果條件表示式3執行結果是false
,此時執行程式碼4。執行流程圖如下所示:
日常寫程式碼的過程中,儘量保證程式碼巢狀的層數不超過兩層。
案例:輸入三個數,要求輸出是按照從大到小進行排列。例如,輸入三個數為20、30、10,輸出結果為30、20、10
- 第一層條件:比較第一個數和第二個數。
- 第二層條件:比較第二個數和第三個數。
- 第三層條件:比較第一個數和第三個數。
import java.util.Scanner; /** * if的巢狀--三個數位排列 * * @author iCode504 * @date 2023-11-23 */ public class IfDemo5 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("請輸入三個整數: "); int number1 = scanner.nextInt(); int number2 = scanner.nextInt(); int number3 = scanner.nextInt(); if (number1 > number2) { if (number2 > number3) { System.out.println("三個數從大到小的排序是: " + number1 + " " + number2 + " " + number3); } else { if (number1 > number3) { System.out.println("三個數從大到小的排序是: " + number1 + " " + number3 + " " + number2); } else { System.out.println("三個數從大到小的排序是: " + number3 + " " + number1 + " " + number2); } } } else { if (number2 < number3) { System.out.println("三個數從大到小的排序是: " + number3 + " " + number2 + " " + number1); } else { if (number1 > number3) { System.out.println("三個數從大到小的排序是: " + number2 + " " + number3 + " " + number1); } else { System.out.println("三個數從大到小的排序是: " + number2 + " " + number1 + " " + number3); } } } } }
執行結果:
雖然執行結果符合我們的預期,但是我只能說:這樣的程式碼寫的非常糟糕!!!這段程式碼中
if
的巢狀層數達到了三層,事實上if
巢狀兩層以上可讀性就大打折扣了。我們可以使用Java陣列、迴圈和陣列方法對此問題做進一步處理(小白可以跳過這一部分)。
import java.util.Arrays; import java.util.Scanner; /** * 三數比較--按照從大到小的順序排列 * * @author iCode504 * @date 2023-11-23 */ public class IfDemo6 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("請輸入三個數位: "); Integer[] array = new Integer[3]; for (int i = 0; i < array.length; i++) { array[i] = scanner.nextInt(); } // 呼叫Arrays.sort方法對陣列排序,排序規則從大到小(lambda表示式) Arrays.sort(array, (o1, o2) -> o2 - o1); System.out.println("三個數從大到小的排序是: " + array[0] + " " + array[1] + " " + array[2]); } }
執行結果:
五、switch語句
if-else if-else
多分支語句可以用於多個條件表示式的判斷,我們可以寫非常多的else if
,然而過多的else if
可能會導致程式碼的可讀性變差。Java為我們提供了
swtich
語句在一定程度上可以簡化多條件分支。以下是switch
的語法結構:switch (表示式) { case 值1: 執行程式碼1... break; case 值2: 執行程式碼2... // break case 值3: 執行程式碼3... break; ... case 值n: 執行程式碼n... break; default: 上述條件都不適用執行程式碼... }
1.
switch
語句中表示式的計算結果、值1、值2、...、值n的資料型別必須要保持一致。支援的資料型別包括:byte
、short
、int
、char
、字串型別String
(JDK 7新特性)、列舉型別(後續會講到,JDK 7新特性)。2. 如果表示式的計算結果和
case
中某個值相等時,就會執行這個case
內的程式碼。3.
switch
語句中的default
是可選的,它的作用是當表示式的計算結果和所有case
的值都不相等時才會執行default
語句,如果default
語句不存在時,所有的case
對應的值和判定值都不相等時,跳出switch
語句。4.
break
的作用是跳出switch
語句(break
關鍵字還會在迴圈中遇到),在每一個case
對應的程式碼塊後面寫上break
是個好習慣。如果
case
中不加break
,此時switch
語句會出現穿透性,即當某一個case
執行完成後,它會繼續執行下面其他的case
。以下是一個是否使用break
的案例:案例:輸入數位1~7,使用
switch
語句輸出當前日期(假設7代表星期日)import java.util.Scanner; /** * switch語句--不加break--穿透性 * * @author iCode504 * @date 2023-11-15 */ public class SwitchDemo1 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("請輸入星期數(1~7), 7表示星期日"); int number = scanner.nextInt(); switch (number) { case 1: String monday = "星期一"; System.out.println("今天是" + monday); case 2: String tuesday = "星期二"; System.out.println("今天是" + tuesday); case 3: String wednesday = "星期三"; System.out.println("今天是" + wednesday); case 4: String thursday = "星期四"; System.out.println("今天是" + thursday); case 5: String friday = "星期五"; System.out.println("今天是" + friday); case 6: String saturday = "星期六"; System.out.println("今天是" + saturday); case 7: String sunday = "星期日"; System.out.println("今天是" + sunday); default: System.out.println("無效日期"); } } }
執行結果:
很明顯,輸入數位3的時候,由於沒有
break
,當執行case 3
內部程式碼以後,它會向下執行其他case
中的程式碼,直至default
內的程式碼執行完畢為止。並且這段程式碼還有可以進一步修改的空間,以下是加入break
並進行簡化的程式碼:import java.util.Scanner; /** * switch語句--新增break--穿透性 * * @author iCode504 * @date 2023-11-15 */ public class SwitchDemo2 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("請輸入星期數(1~7), 7表示星期日"); int number = scanner.nextInt(); String weekday; switch (number) { case 1: weekday = "星期一"; break; case 2: weekday = "星期二"; break; case 3: weekday = "星期三"; break; case 4: weekday = "星期四"; break; case 5: weekday = "星期五"; break; case 6: weekday = "星期六"; break; case 7: weekday = "星期日"; break; default: weekday = "無效星期"; } System.out.println("今天是" + weekday); } }
執行結果:
從上述結果可以看出,使用
break
以後,就可以阻斷switch
穿透性。
switch
語句執行流程如下圖所示(每個case
都帶上break
語句):在瞭解了
switch
語句的基礎上,我們再來講解一個switch
語句和if-else
語句結合使用的案例:案例:輸入年份和月份,輸出格式如下:xxxx年xx月有xx天。
常識:1、3、5、7、8、10、12恆定是31天;4、6、9、11恆定為30天。這幾個月份我們可以利用
switch
的穿透性替換掉多條件的else if
判斷。需要額外考慮的是:2月份的天數需要考慮年份是閏年還是平年,閏年能被400整除,例如:2000年,1600年是閏年,1900年就不是閏年。此外,如果不能被100整除,而能被4整除的也是閏年,例如:2020,2016,2004,2008年都是閏年。
結合上述分析,我們可以使用程式碼進一步復現:
import java.util.Scanner; /** * switch和if結合使用 * * @author iCode504 * @date 2023-11-15 */ public class SwitchDemo3 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("請輸入年份: "); int year = scanner.nextInt(); System.out.print("請輸入月份: "); int month = scanner.nextInt(); int day = 0; switch (month) { // 利用switch的穿透性 case 1: case 3: case 5: case 7: case 8: case 10: case 12: day = 31; break; case 4: case 6: case 9: case 11: day = 30; break; case 2: // 2月份需要額外針對年份進行判斷 if (year % 400 == 0) { day = 29; } else if (year % 4 == 0 && year % 100 != 0) { day = 28; } break; default: // 不在1-12月份內做出說明 System.out.println("無效的月份"); } System.out.println(year + "年" + month + "月有" + day + "天"); } }
執行結果:
以我個人的開發經驗來看,使用
switch
語句的頻率要比使用if
語句要少很多,二者的執行效率基本上差不太多。
if
可以編寫更加靈活的條件表示式。比如:判斷某個整數在[10, 20]
區間內,此時使用if
條件表示式可以寫成if (number >= 10 && number <= 20)
,如果使用switch
解決此問題會讓程式碼變得更加複雜(因為你要寫很多個case
進行比較)。
switch
更擅長特定型別的值進行比較。以上面根據某年某月求當前月份由多少天為例,事實上完全使用if
語句實現,只不過我們需要寫成: