Java 的流程控制是什么樣子的
本文轉載自微信公眾號「蝸牛互聯網」,作者蝸牛互聯網。轉載本文請聯系蝸牛互聯網公眾號。
本文大綱:
為什么需要流程控制
平時我們做一件事,一般都會有個固定流程。
比如你想吃蘋果,你需要找到冰箱,打開冰箱門,取出蘋果,回到沙發上,然后開吃。
這個順序基本不能調換,你不能在打開冰箱門之前去取蘋果。按順序來控制,這是一種流程。
那如果你想吃香蕉,你會發現流程類似,只是從冰箱里取出香蕉就好了。
在這個過程里,你會發現你最終吃什么,取決于你的選擇。你要吃蘋果,你從冰箱里取蘋果,你要吃香蕉,你從冰箱里取香蕉。按選擇來控制,這也是一種流程。
那還有種情況,一根香蕉不夠你吃,你還想吃幾根,直到你不想吃,那你就會重復上面的流程,當你吃飽的時候,就終止了。這種重復執行按照某個條件來終止的控制,也是一種流程。
計算機是現實世界的電子化表達,那么在計算機的世界里,程序運行也需要這樣的流程控制。
無論是機器語言,還是匯編語言,還是高級程序設計語言,都會涉及這個概念,它決定了你寫的代碼會按照怎樣的路徑運行,也決定著計算機和用戶之間的交互方式。
我們看看 Java 語言的流程控制是什么樣的?
輸入和輸出
我們編程都是為了解決某個實際問題,比如寫一個加法程序,我們是為了獲得兩個數的和是多少。
那你會發現,程序有個重要的特點,就是接收輸入,然后進行處理,最后輸出結果。
那 Java 是怎么接收輸入的呢?
Scanner 介紹
Java 提供了 Scanner 工具類,我們可以通過這個工具類來獲取用戶的輸入。基本的語法如下:
- // 用標準的輸入流構建一個 Scanner 對象
- Scanner scanner = new Scanner(System.in);
- // 讀取輸入的一行并獲取字符串
- String nextLineStr = scanner.nextLine();
- // 讀取輸入的字符串,會忽略掉字符串兩邊的空格,因為空格起分隔符或結束符的作用
- String nextStr = scanner.next();
- // 讀取輸入的整數,非整數會拋異常(InputMismatchException)
- int nextInt = scanner.nextInt();
System.in 是標準的輸入流,使用它可以接收鍵盤輸入或其他指定數據源的數據。
Scanner 是一個簡單的文本掃描器,通過它可以解析基本類型和字符串。new Scanner(System.in) 可以構建出一個掃描器對象,scanner.nextLine()可以讀取輸入的一行并獲取字符串,scanner.next() 也可以獲取字符串,不過不能支持兩邊有空格的字符串,scanner.nextInt() 可以讀取輸入的整數,int 換成其他基本類型同樣也適用。
Scanner 使用
我們可以看下樣例代碼:
- package cn.java4u.flowcontrol;
- import java.util.Scanner;
- /**
- * 輸入演示
- *
- * @author 蝸牛
- * @from 公眾號:蝸牛互聯網
- */
- public class InputDemo {
- public static void main(String[] args) {
- // 用標準的輸入流構建一個 Scanner 對象
- Scanner scanner = new Scanner(System.in);
- // 讀取輸入的一行并獲取字符串
- String nextLineStr = scanner.nextLine();
- // 讀取輸入的字符串,會忽略掉字符串兩邊的空格,因為空格起分隔符或結束符的作用
- String nextStr = scanner.next();
- // 讀取輸入的整數,非整數會拋異常(InputMismatchException)
- int nextInt = scanner.nextInt();
- System.out.println("---以下為打印值---");
- System.out.println("nextLineStr:" + nextLineStr);
- System.out.println("nextStr:" + nextStr);
- System.out.println("nextInt:" + nextInt);
- }
- }
你會發現,樣例代碼里有一個 import java.util.Scanner; ,Scanner 是 Java 類庫里的一個類,所以需要 import 語法引入一下,才能使用。
樣例代碼有三次控制臺輸入,我們輸入以下數據看下輸出:
- 我是蝸牛
- 蝸牛666
- 8
第一行輸入的字符串后邊有空格,第二行輸入的字符串前后都有空格。輸出如下:
---以下為打印值---
- ---以下為打印值---
- nextLineStr:我是蝸牛
- nextStr:蝸牛666
- nextInt:8
你會發現 nextLineStr 后邊的空格還在,nextStr 前后的空格都沒有了。
我們再看一種輸入:
- 我是蝸牛
- 蝸牛666 7
當我們輸入兩行后,再回車,程序就直接輸出結果了:
- nextLineStr:我是蝸牛
- nextStr:蝸牛666
- nextInt:7
由此可見 nextLine()和 next() 之間的不同,nextInt() 是 next() 基礎上的類型轉換,特點可以認為和 next() 一致。
- 起始符 分隔符 特點
- nextLine() 任何字符 回車(Enter) 可以獲得帶空格的字符串
- next() 非空白字符 空格 不能獲得帶空格的字符串
輸出
在之前的代碼中,我們都是通過 System.out.println() 的方式,把內容輸出到控制臺的。
其中 System.out 是標準的輸出流,通過它不只可以做顯示輸出,也可以寫入到指定的輸出目標,比如文件。
println 是 print line 的縮寫,表示輸出并換行。如果輸出不想換行,可以使用 print() 。此外,Java 也支持用 printf() 進行格式化輸出,以方便閱讀。
以下是示例代碼:
- package cn.java4u.flowcontrol;
- /**
- * 輸出演示
- * @author 蝸牛
- * @from 公眾號:蝸牛互聯網
- */
- public class OutputDemo {
- public static void main(String[] args) {
- // 輸出并換行
- System.out.println("---開始演示---");
- // 輸出不換行
- System.out.print("打印不換行[");
- System.out.print("1 ");
- System.out.print("2 ");
- System.out.print("3");
- System.out.print("]");
- // 只換行
- System.out.println();
- // 格式化輸出
- double d = 66600000.8888;
- // 不進行格式化的處理結果:6.66000008888E7
- System.out.println("不進行格式化的處理結果:" + d);
- System.out.printf("默認格式化:%f", d);
- System.out.printf("; 無小數格式化:%.0f", d);
- System.out.printf("; 一位小數格式化:%.1f", d);
- System.out.printf("; 兩位小數格式化:%.2f", d);
- }
- }
輸出結果如下:
- ---開始演示---
- 打印不換行[1 2 3]
- 不進行格式化的處理結果:6.66000008888E7
- 默認格式化:66600000.888800; 無小數格式化:66600001; 一位小數格式化:66600000.9; 兩位小數格式化:66600000.89
%f 就是 Java 為浮點數提供的格式化功能的占位符,系統默認會把浮點數格式化成 6 位小數輸出,當然你也可以仿照樣例指定小數位輸出。
除了浮點數,Java 的格式化功能還提供了多種占位符,可以把各種數據類型格式化成指定的字符串,以下是常用的占位符:
占位符 | 說明 |
---|---|
%d | 格式化輸出整數 |
%x | 格式化輸出十六進制整數 |
%f | 格式化輸出浮點數 |
%e | 格式化輸出科學計數法表示的浮點數 |
%s | 格式化字符串 |
注意,由于 % 表示占位符,因此,連續兩個 %% 表示一個 % 字符本身。
三種流程控制結構
知道了輸入和輸出在 Java 世界里的表達方式,我們再看下在程序處理中涉及到的流程控制有哪些。
順序結構
程序基本的流程結構就是順序結構,Java 也是如此。如果沒有特別指明,程序都是按照順序一行一行執行。
選擇結構
但很多時候,我們需要判斷一個東西是否可行,然后才去執行一段邏輯。比如加法程序,我們得要求參與運算的值是數字而不能是字符串。
那這樣的流程控制可以通過選擇結構來實現。
if單選擇結構
如果只是想針對某個條件特殊處理下,處理前后的邏輯不變,此時可以使用if單選擇結構。
語法如下:
- if(布爾表達式){
- //布爾表達式結果為 true 時執行的語句
- }
以下是打印兩個整數的最大值的示例代碼:
- package cn.java4u.flowcontrol;
- import java.util.Scanner;
- /**
- * if 單選擇結構
- *
- * @author 蝸牛
- * @from 公眾號:蝸牛互聯網
- */
- public class IfSingleChoiceDemo {
- public static void main(String[] args) {
- // 用標準的輸入流構建一個 Scanner 對象
- Scanner scanner = new Scanner(System.in);
- System.out.println("請輸入整數a:");
- int a = scanner.nextInt();
- System.out.println("請輸入整數b:");
- int b = scanner.nextInt();
- // 初始化最大值為 a 的值
- int max = a;
- // b 比 a 大的請求下,把 b 的值賦給 max
- if (a < b) {
- max = b;
- }
- System.out.println("max:" + max);
- }
- }
我們用數字 a 初始化了變量 max,只有發現 b 比 a 大的時候,才會把 b 的值賦給 max。也就是 當 a=10 并且 b=9 時,if 花括號里的邏輯是走不到的,當 a=10 并且 b=11 時,if 花括號里的邏輯會走到。
if雙選擇結構
有時候我們遇到某個條件,會有兩種不同的邏輯,此時可以使用if雙選擇結構。
語法如下:
- if(布爾表達式){
- //布爾表達式結果為 true 時執行的語句
- }else{
- //布爾表達式結果為 false 時執行的語句
- }
以下是打印整數絕對值的示例代碼:
- package cn.java4u.flowcontrol;
- import java.util.Scanner;
- /**
- * if雙選擇結構
- *
- * @author 蝸牛
- * @from 公眾號:蝸牛互聯網
- */
- public class IfDoubleChoiceDemo {
- public static void main(String[] args) {
- // 用標準的輸入流構建一個 Scanner 對象
- Scanner scanner = new Scanner(System.in);
- System.out.println("請輸入整數a:");
- //10,-10 切換
- int a = scanner.nextInt();
- // 初始化絕對值變量
- int abs;
- if (a < 0) {
- abs = -a;
- } else {
- abs = a;
- }
- System.out.println("abs:" + abs);
- }
- }
我們用 abs 初始化了絕對值變量,針對待判定的整數 a,當它的值是 10 或者 -10 時,會走 if 的不同分支執行不一樣的邏輯。
if多選擇結構
當我們遇到的條件不只一個的時候,我們執行邏輯的情況可能會超過兩個,此時可以使用if多選擇結構。
語法如下:
- if(布爾表達式1){
- //布爾表達式1結果為 true 時執行的語句
- }else if(布爾表達式2){
- //布爾表達式2結果為 true 時執行的語句
- }
- else{
- //布爾表達式結果為 false 時執行的語句
- }
以下是百分制成績評優良差的示例代碼:
- package cn.java4u.flowcontrol;
- import java.util.Scanner;
- /**
- * if 多選擇結構
- *
- * @author 蝸牛
- * @from 公眾號:蝸牛互聯網
- */
- public class IfMultiChoiceDemo {
- public static void main(String[] args) {
- // 用標準的輸入流構建一個 Scanner 對象
- Scanner scanner = new Scanner(System.in);
- System.out.println("請輸入你的成績(百分制):");
- //58,68,88,96,120 切換
- int score = scanner.nextInt();
- if (score > 0 && score < 60) {
- System.out.println("不合格");
- } else if (score >= 60 && score < 80) {
- System.out.println("合格");
- } else if (score >= 80 && score < 90) {
- System.out.println("良好");
- } else if (score >= 90 && score <= 100) {
- System.out.println("優秀");
- } else {
- System.out.println("非法輸入");
- }
- }
- }
成績分數評優良差的程序存在區間多級判斷,比較適合if多選擇結構。
if嵌套選擇結構
當我們遇到的條件里,又能拆出多個條件,有不同的執行邏輯時,可以使用if嵌套選擇結構。if嵌套選擇結構可以認為是if多選擇結構的變種。
語法如下:
- if(布爾表達式1){
- //布爾表達式1結果為 true 時執行的語句
- if(布爾表達式2){
- //布爾表達式2結果為 true 時執行的語句
- }
- }
以下是百分制成績評優良差變形后的示例代碼:
- package cn.java4u.flowcontrol;
- import java.util.Scanner;
- /**
- * if嵌套選擇結構
- *
- * @author 蝸牛
- * @from 公眾號:蝸牛互聯網
- */
- public class IfNestChoiceDemo {
- public static void main(String[] args) {
- // 用標準的輸入流構建一個 Scanner 對象
- Scanner scanner = new Scanner(System.in);
- System.out.println("請輸入你的成績(百分制):");
- //58,68,88,96,120 切換
- int score = scanner.nextInt();
- if (score >= 0 && score <= 100) {
- if (score < 60) {
- System.out.println("不合格");
- } else if (score < 80) {
- System.out.println("合格");
- } else if (score < 90) {
- System.out.println("良好");
- } else {
- System.out.println("優秀");
- }
- } else {
- System.out.println("非法輸入");
- }
- }
- }
和普通的 if多選擇結構 的代碼不同在于,if嵌套選擇做了兩層選擇,第一層是輸入的合法性,第二層是對成績做分級。
switch選擇結構
我們有時候遇到的條件比較有限,并且就是判斷一個變量與一系列中某個值是否相等,然后命中不同的值,會走向不同的邏輯。此時就可以使用switch選擇結構。
語法如下:
- switch(var){
- case value1:
- // var 命中 value1 時執行的語句
- break;
- case value2:
- // var 命中 value2 時執行的語句
- break;
- //可以有任意數量的case語句
- // 默認的請求,上邊都沒命中,會走到該分支
- default:
- //以上 case 都未命中或者未 break 會走到這里
- }
我們如果把上邊提到的幾個程序打包給用戶使用,那就可以通過 switch 來提供統一的入口,引導用戶鍵入1來路由到求最大值的程序里,鍵入2路由到求絕對值的程序里,鍵入3路由到成績分數評優良差的程序里。示例代碼如下:
- package cn.java4u.flowcontrol;
- import java.util.Scanner;
- /**
- * switch選擇結構
- *
- * @author 蝸牛
- * @from 公眾號:蝸牛互聯網
- */
- public class IfSwitchChoiceDemo {
- public static void main(String[] args) {
- // 用標準的輸入流構建一個 Scanner 對象
- Scanner scanner = new Scanner(System.in);
- System.out.println("請選擇你要運行的程序(鍵入1表示求最大值,鍵入2表示求絕對值,鍵入3表示成績分數評優良差):");
- int choice = scanner.nextInt();
- switch (choice) {
- case 1:
- System.out.println("--開始求兩個數的最大值--");
- IfSingleChoiceDemo.main(null);
- break;
- case 2:
- System.out.println("--開始求絕對值--");
- IfDoubleChoiceDemo.main(null);
- break;
- case 3:
- System.out.println("--開始成績分數評優良差--");
- IfMultiChoiceDemo.main(null);
- break;
- default:
- System.out.println("非法輸入");
- }
- }
- }
跑一下這個程序,鍵入 1 你會發現開始執行求最大值的子程序里,最大值打印后整個程序就結束了,這說明 break 起到了當前分支阻斷程序的作用。一旦命中 break 代碼,后邊的 case 2、case 3、default 都不會走到。
當然不是每個 case 都需要有 break 的,當你有兩個 case 的邏輯一致,就可以忽略 break 進行 case 合并,比如當鍵入 4 的時候,我要求和 3 效果一致,可以改成下面這樣:
- case 3:
- case 4:
- System.out.println("--開始成績分數評優良差--");
- IfMultiChoiceDemo.main(null);
- break;
沒有 break 的 case 邏輯會穿透到下一個 case,使用下一個 case 的代碼邏輯。
注意,switch選擇結構是if多選擇結構特殊場景下的變種,JavaSE 8 支持的變量類型有 byte、short、int、char、String、ENUM。
循環結構
程序有時候會重復運行一段邏輯,如果按順序結構+選擇結構來組織代碼的話,這種情況下需要寫很多重復的代碼才能實現。比如我要得到從 1 到 5 的和:
- 1+2+3+4+5=?
我的代碼可能就是這樣:
- package cn.java4u.flowcontrol;
- /**
- * while 循環結構演示
- *
- * @author 蝸牛
- * @from 公眾號:蝸牛互聯網
- */
- public class WhileCircleDemo {
- public static void main(String[] args) {
- int a = 1;
- int sum = 0;
- System.out.println("當前a的值為:" + a);
- // a 累加到 sum 中
- sum = sum + a;
- // a 自身加一
- a = a + 1;
- System.out.println("當前a的值為:" + a);
- sum = sum + a;
- a = a + 1;
- System.out.println("當前a的值為:" + a);
- sum = sum + a;
- a = a + 1;
- System.out.println("當前a的值為:" + a);
- sum = sum + a;
- a = a + 1;
- System.out.println("當前a的值為:" + a);
- sum = sum + a;
- System.out.println("sum:" + sum);
- }
- }
你會發現重復邏輯很多,在 a = 5 的時候,累加才結束,結果才輸出。那如果有種機制能把這些重復的邏輯用簡潔的方式表達,那寫代碼就會方便很多。
這種機制就是循環結構。
while循環結構
最常用的循環結構是 while 循環,語法如下:
- while(布爾表達式){
- //循環內容
- }
- 只要布爾表達式為 true,循環就會一直執行下去。
- 我們大多數情況是會讓循環停止下來的,因此需要一個讓布爾表達式為 false 的方式來停止循環。
- 少部分情況時需要循環一直執行,比如服務器的請求響應監聽等。
- 循環條件如果一直是 true,就會造成無限循環,應盡量避免這種情況,否則會造成程序卡死崩潰。
用 while 來表達求和代碼如下:
- // 初始化值
- a = 1;
- sum = 0;
- while (a <= 5) {
- // a 累加到 sum 中
- sum += a;
- // a 自身加一
- a++;
- }
- System.out.println("while sum:" + sum);
do while循環結構
觀察 while 語句,你會發現,只要不滿足條件,就不能進入循環。但有時候我們需要即使不滿足條件,也至少要執行一次。那此時用 do while 循環就比較合適,語法如下:
- do{
- //循環內容
- }where(布爾表達式)
- 不同于 while 循環結構的先判斷后執行的方式,do while 循環結構是先執行后判斷。
- do while 中的循環內容會被至少執行一次。
用 do while 來表達求和代碼如下:
- // 初始化值
- a = 1;
- sum = 0;
- do {
- // a 累加到 sum 中
- sum += a;
- // a 自身加一
- a++;
- } while (a <= 5);
- System.out.println("do while sum:" + sum);
for循環結構
在求和代碼中,我們會發現,a 就像一個計數器,通過 a = 1 初始化一個值,然后在每次循環中加一來當成我們求和時要加的那個數,a <= 5 作為計數器循環檢測條件,決定了我們的累加是加到 5 還是 100,只要改成 a <= 100,累加到 100 就不會再執行循環。
這種其實是迭代處理的通用結構:初始值、終止條件和計數器。于是 Java 提供了 for 循環結構,用來簡化這種場景下的 while 循環,語法如下:
- for(計數器初始化; 布爾表達式; 循環后更新計數器){
- //循環內容
- }
用 for 來表達求和代碼如下:
- sum = 0;
- for (a = 1; a <= 5; a++) {
- sum += a;
- }
- System.out.println("for sum:" + sum);
for each循環結構
有些時候,我們拿到一堆數處理,其實并不關心他們的次序,只要能遍歷到就可以。比如數組里的幾個值,我不關心值的索引,我只想知道這些值的總和是多少。此時就可以用 for each 循環結構,它可以很簡單的遍歷數組,語法如下:
- for(元素類型 元素變量 : 數組或迭代器){
- //循環內容
- }
- for each 是對 for 特殊場景下的簡化,處理對象是數組或者迭代器對象。
- 和 for 循環結構相比,for each 循環結構不再體現計數器的初始化和更新,因此也無法指定遍歷順序,也不能獲取數組或迭代器索引。
用 for each 來表達求和代碼如下:
- int[] array = {1, 2, 3, 4, 5};
- sum = 0;
- for (int temp : array) {
- sum += temp;
- }
- System.out.println("for each sum:" + sum);
循環結構的中斷
循環結構都會有個布爾表達式作為循環檢測條件,如果布爾表達式為 false 時,就會終止循環,這是循環中斷的一種方式。
除此之外,Java 還提供了另外兩種循環結構中斷的方式。
一種是 break。語法如下:
- 循環結構{
- //中斷前代碼
- if(中斷布爾表達式){
- break;
- }
- //中斷后代碼
- }
- 中斷布爾表達式返回 true 時,命中 break ,直接退出整個循環結構,中斷后代碼不再執行。
求和示例代碼如下:
- int a = 1;
- int sum = 0;
- while (a <= 5) {
- // a 為 3 的時候中斷
- if (a == 3) {
- break;
- }
- // a 累加到 sum 中
- sum += a;
- // a 自身加一
- a++;
- }
- System.out.println("while sum:" + sum);
實際對 1 和 2 進行求和,因為 a 為 3 的時候退出了循環。
注意:循環結構如果存在嵌套,break 只會退出當前層循環結構,不會退出外層循環結構。
另一種是 continue,語法如下:
- 循環結構{
- //中斷前代碼
- if(中斷布爾表達式){
- continue;
- }
- //中斷后代碼
- }
- 中斷布爾表達式返回 true 時,命中 continue ,該循環結構當次調用中斷,中斷后代碼當次不再執行,進入循環結構的下次調用。
示例代碼如下:
- int i = 0;
- while (i <= 5) {
- i++;
- // i 為 3 的時候中斷
- if (i == 3) {
- System.out.println("命中 continue");
- continue;
- }
- System.out.println("i=" + i);
- }
輸出:
- i=1
- i=2
- 命中 continue
- i=4
- i=5
- i=6
會發現 i 的值為 3 的時候命中 continue 邏輯,當次循環不會繼續往下走,但會進入下一次循環。
簡單講,break 跳出當前層循環,循環結構中止,continue 跳出當次循環調用,當次調用中止。二者都要配合 if 使用。
小結
本文從現實案例引出了流程控制的概念,映射到編程領域,我們把一個程序的執行,抽象成輸入-處理-輸出的過程。然后介紹了在 Java 的世界里,輸入和輸出的實現方式,接著講解了在處理的過程中,常用的三種流程控制結構:順序結構、選擇結構和循環結構,并列出了演示代碼。讀者可以仿照案例實踐一把,相信你會有更深刻的印象。感謝你的閱讀和分享,歡迎留言互動和點贊!