本系列文章經補充和完善,已修訂整理成書《Java編程的邏輯》,由機械工業出版社華章分社出版,於2018年1月上市熱銷,讀者好評如潮!各大網店和書店有售,歡迎購買,京東自營鏈接:http://item.jd.com/12299018.html

循環
上節我們介紹了流程控制中的條件執行,根據具體條件不同執行不同操作。本節我們介紹流程控制中的循環,所謂循環就是多次重復執行某些類似的操作,這個操作一般不是完全一樣的操作,而是類似的操作。都有哪些操作呢?這個例子太多了。
- 展示照片,我們查看手機上的照片,背后的程序需要將照片一張張展示給我們。
- 播放音樂,我們聽音樂,背后程序按照播放列表一首首給我們放。
- 查看消息,我們瀏覽朋友圈消息,背后程序將消息一條條展示給我們。
循環除了用於重復讀取或展示某個列表中的內容,日常中的很多操作也要靠循環完成。
- 在文件中,查找某個詞,程序需要和文件中的詞逐個比較(當然可能有更高效方式,但也離不開循環)。
- 使用Excel對數據進行匯總,比如求和或平均值,需要循環處理每個單元的數據
- 群發祝福消息給好友,程序需要循環給每個好友發。
當然,以上這些例子只是冰山一角,計算機程序運行時大概只能順序執行、條件執行和循環執行,順序和條件其實沒什么特別,而循環大概才是程序強大的地方。憑借 循環,計算機能夠非常高效的完成人很難或無法完成的事情,比如說,在大量文件中查找包含某個搜索詞的文檔,對幾十萬條銷售數據進行統計匯總等。
在Java中,循環有四種形式,分別是 while, do/while, for, foreach,下面我們分別來看一下。
while
while的語法為:
while(條件語句){ 代碼塊 }
或
while(條件語句) 代碼;
while和if的語法很像,只是把if換成了while,它表達的含義也非常簡單,只要條件語句為真,就一直執行后面的代碼,為假就停止不做了。例如:
Scanner reader = new Scanner(System.in); System.out.println("please input password"); int num = reader.nextInt(); int password = 6789; while(num!=password){ System.out.println("please input password"); num = reader.nextInt(); } System.out.println("correct"); reader.close();
以上代碼中,我們使用類型為Scanner的reader變量從屏幕控制台接收數字,reader.nextInt()從屏幕接收一個數字,如果數字不是 6789,就一直提示輸入,否則才跳出循環。(以上代碼Scanner我們還沒有介紹過,可以忽略其細節,另外代碼只用於解釋語法,不應看做是實際良好代 碼)
while循環中,代碼塊中會有代碼影響循環條件,但也經常不知道什么時候循環會退出。如上例所示,匹配的時候會退出但什么時候能匹配取決於用戶的輸入。
do/while
如果不管條件語句是什么,代碼塊都會至少執行一次,則可以使用do/while循環。do/while的語法是:
do{ 代碼塊; }while(條件語句)
這個也很容易理解,先執行代碼塊,然后再判斷條件語句,如果成立,則繼續循環,否則退出循環。也就是,不管條件語句是什么,代碼塊都會至少執行一次。用上面的例子,其do/while循環是:
Scanner reader = new Scanner(System.in); int password = 6789; int num = 0; do{ System.out.println("please input password"); num = reader.nextInt(); }while(num!=password); System.out.println("correct"); reader.close();
for
實際中應用最為廣泛的循環語法可能是for了,尤其是在循環次數已知的情況下。for的語法是:
for(初始化語句; 循環條件; 步進操作){ 循環體 }
for 后面的括號中有兩個分號;,分隔了三條語句,除了循環條件必須返回一個boolean類型外,其他語句沒有什么要求,但通常情況下第一條語句用於初始化, 尤其是循環的索引變量,第三條語句修改循環變量,一般是步進,即遞增或遞減索引變量,循環體是在循環中執行的語句。
for循環簡化了書寫,但執行過程對初學者而言不是那么明顯,實際上,它執行的流程是這樣的:
- 執行初始化指令
- 檢查循環條件是否為true,如果為false,跳轉到第6步
- 循環條件為真,執行循環體
- 執行步進操作
- 步進操作執行完后,跳轉到第2步,即繼續檢查循環條件。
- for循環后面的語句
下面是一個簡單的for循環:
int[] arr = {1,2,3,4}; for(int i=0;i<arr.length;i++){ System.out.println(arr[i]); }
順序打印數組中的每個元素,初始化語句初始化索引i為0,循環條件為索引小於數組長度,步進操作為遞增索引i,循環體打印數組元素。
在for中,每個語句都是可以為空的,也就是說:
for(;;){}
是有效的,這是個死循環,一直在空轉,和while(true){}的效果是一樣的。可以省略某些語句,但分號;不能省。如:
int[] arr = {1,2,3,4}; int i=0; for(;i<arr.length;i++){ System.out.println(arr[i]); }
索引變量在外面初始化了,所以初始化語句可以為空。
foreach
foreach的語法如下代碼所示:
int[] arr = {1,2,3,4}; for(int element : arr){ System.out.println(element); }
foreach使用冒號 : ,冒號前面是循環中的每個元素,包括數據類型和變量名稱,冒號后面是要遍歷的數組或集合(關於集合我們后續文章介紹),每次循環element都會自動更新。對於不需要使用索引變量,只是簡單遍歷的情況,foreach語法上更為簡潔。
循環控制 - break
在循環的時候,會以循環條件作為是否結束的依據,但有時候可能會根據別的條件提前結束循環。比如說,在一個數組中查找某個元素的時候,循環條件可能是到數組結束,但如果找到了元素,可能就會想提前結束循環,這時候可以使用break。
我們在介紹switch的時候提到過break,它用於跳轉到switch外面。在循環的循環體中也可以使用break,它的含義和switch中類似,用於跳出循環,開始執行循環后面的語句。以在數組中查找元素作為例子,代碼可能是:
int[] arr = ... ; //在該數組中查找元素 int toSearch = 100; //要查找的元素 int i = 0; for(;i<arr.length;i++){ if(arr[i]==toSearch){ break; } } if(i!=arr.length){ System.out.println("found"); }else{ System.out.println("not found"); }
如果找到了,會調用break, break執行后會跳轉到循環外面,不會再執行i++語句,所以即使是最后一個元素匹配,i也小於arr.length,而如果沒有找到,i最后會變為arr.length,所以可根據i是否等於arr.length來判斷是否找到了。
以上代碼中,也可以將判斷是否找到的檢查放到循環條件中,但通常情況下,使用break可能會使代碼更清楚一些。
循環控制 - continue
在循環的過程中,有的代碼可能不需要每次循環都執行,這時候,可以使用continue語句,continue語句會跳過循環體中剩下的代碼,然后執行步進操作。我們看個例子,以下代碼統計一個數組中某個元素的個數:
int[] arr = ... //在該數組中查找元素 int toSearch = 2; //要查找的元素 int count = 0; for(int i=0;i<arr.length;i++){ if(arr[i]!=toSearch){ continue; } count++; } System.out.println("found count "+count);
上面代碼統計數組中值等於toSearch的元素個數,如果值不等於toSearch,則跳過剩下的循環代碼,執行i++。以上代碼也可以不用 continue,使用相反的if判斷也可以得到相同的結果,這只是個人偏好的問題,如果類似要跳過的情況比較多,使用continue可能會更簡潔。
循環嵌套
和if類似,循環也可以嵌套,在一個循環體中開啟另一個循環。在嵌套循環中,break語句只會跳出本層循環,continue也一樣。
循環本質
和if一樣,循環內部也是靠條件轉移和無條件轉移指令實現的。比如說下面的代碼:
int[] arr = {1,2,3,4}; for(int i=0;i<arr.length;i++){ System.out.println(arr[i]); }
其對應的跳轉過程可能為:
1. int[] arr = {1,2,3,4};
2. int i=0;
3. 條件跳轉:如果i>=arr.length,跳轉到第7行
4. System.out.println(arr[i]);
5. i++
6. 無條件跳轉,跳轉到第3行
7. 其他代碼
在if中,跳轉只會往后面跳,而for會往前面跳,第6行就是無條件跳轉指令,跳轉到了前面的第3行。break/continue語句也都會轉換為跳轉指令。
循環小結
循環的語法總體上也是比較簡單的,初學者需要注意的是for的執行過程,以及break和continue的含義。
雖然循環看起來只是重復執行一些類似的操作而已,但它其實是計算機程序解決問題的一種基本思維方式,憑借循環(當然還有別的),計算機程序可以發揮出強大的能力,比如說批量轉換數據,查找過濾數據,統計匯總等。
使用基本數據類型、數組、基本運算、加上條件和循環,其實已經可以寫很多程序了,但使用基本類型和將代碼都放在一起,程序難以理解,尤其是程序邏輯比較復雜的時候。
解決復雜問題的基本策略是分而治之,將復雜問題分解為若干不那么復雜的子問題,然后子問題再分解為更小的子問題……程序由數據和指令組成,大程序可以分解為小程序,小程序接着分解為更小的程序。那如何表示子程序,以及子程序之間如何協調呢?
----------------
未完待續,查看最新文章,敬請關注微信公眾號“老馬說編程”(掃描下方二維碼),深入淺出,老馬和你一起探索Java編程及計算機技術的本質。原創文章,保留所有版權。

-----------
更多相關原創文章
