Java笔记-循环语句
循环语句
循环是程序中的重要流程结构之一。循环语句能够使程序代码重复执行,适用于需要重复一段代码直到满足特定条件为止的情况。
所有流行的编程语言中都有循环语句。Java 中采用的循环语句与C语言中的循环语句相似,主要有 while、do-while 和 for。另外 Java 5 之后推出了 for-each 循环语句,for-each 循环是 for 循环的变形,它是专门为集合遍历而设计的。for-each 并不是一个关键字。
循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被重复执行的代码被称为循环体。当反复执行这个循环体时,需要在合适的时候把循环条件改为假,从而结束循环,否则循环将一直执行下去,形成死循环。
循环语句可能包含如下 4 个部分。
- 初始化语句(init statement): 一条或多条语句,这些语句用于完成一些初始化工作,初始化语句在循环开始之前执行。
- 循环条件(test_expression):这是一个 boolean 表达式,这个表达式能决定是否执行循环体。
- 循环体(body_statement):这个部分是循环的主体,如果循环条件允许,这个代码块将被重复执行。如果这个代码块只有一行语句,则这个代码块的花括号是可以省略的。
- 迭代语句(iteration_statement):这个部分在一次循环体执行结束后,对循环条件求值之前执行,通常用于控制循环条件中的变量,使得循环在合适的时候结束。
while语句
while 语句是 Java 最基本的循环语句,是一种先判断的循环结构,可以在一定条件下重复执行一段代码。该语句需要判断一个测试条件,如果该条件为真,则执行循环语句(循环语句可以是一条或多条),否则跳出循环。
1 | while(条件表达式) { |
其中语句块中的代码可以是一条或者多条语句,而条件表达式是一个有效的 boolean 表达式,它决定了是否执行循环体。当条件表达式的值为 true 时,就执行大括号中的语句块。
执行完毕,再次检查表达式是否为 true,如果还为 true,则再次执行大括号中的代码,否则就跳出循环,执行 while 循环之后的代码。图 1 表示了 while 循环语句的执行流程。
例 1
使用 while 语句计算 10 的阶乘,其具体代码如下所示。
1 | public static void main(String[] args) { |
在上述代码中,定义了两个变量 i 和 n,循环每执行一次 i 值就加 1,判断 i 的值是否小于等于 10,并利用 n=n*i 语句来实现阶乘。当 i 的值大于 10 之后,循环便不再执行并退出循环。
运行程序,执行的结果如下所示:
1 | 10 的阶乘结果为:3628800 |
do-while 语句
如你刚才所见,如果 while 循环一开始条件表达式就是假的,那么循环体就根本不被执行。然而,有时需要在开始时条件表达式即使是假的情况下,while 循环至少也要执行一次。换句话说,有时你需要在一次循环结束后再测试中止表达式,而不是在循环开始时。
幸运的是,Java就提供了这样的循环:do-while循环。do-while 循环语句也是 Java 中运用广泛的循环语句,它由循环条件和循环体组成,但它与 while 语句略有不同。do-while 循环语句的特点是先执行循环体,然后判断循环条件是否成立。
do-while 语句的语法格式如下:
1 | do { |
以上语句的执行过程是,首先执行一次循环操作,然后再判断 while 后面的条件表达式是否为 true,如果循环条件满足,循环继续执行,否则退出循环。while 语句后必须以分号表示循环结束.
例2
编写一个程序,计算 10 的阶乘。使用 do-while 循环的实现代码如下所示。
1 | public static void main(String[] args) { |
程序运行后输出结果如下:
1 | 10 阶乘结果是:3628800 |
例 3
在一个图书系统的推荐图书列表中保存了 50 条信息,现在需要让它每行显示 10 条,分 5 行进行显示。下面使用 do-while 循环语句来实现这个效果,其具体代码如下所示。
1 | public static void main(String[] args) { |
在上述代码中, 声明一个变量 bookIndex 用来保存图书的索引,该变量赋值为 1 表示从第一本开始。在 do-while 循环体内,首先输出了 bookIndex 的值,然后判断 bookIndex 是否能被 10 整除,如果可以则说明当前行已经输出 10 条,用 System.out.println() 语句输出了一个换行符。之后使 bookIndex 加 1,相当于更新当前的索引。最后在 while 表达式中判断是否超出循环的范围,即 50 条以内。
运行程序,执行的结果如下所示。
1 | 1 2 3 4 5 6 7 8 9 10 |
while和do-while的比较
while 循环和 do-while 循环的相同处是:都是循环结构,使用 while(循环条件) 表示循环条件,使用大括号将循环操作括起来。
while 循环和 do-while 循环的不同处如下:
- 语法不同:与 while 循环相比,do-while 循环将 while 关键字和循环条件放在后面,而且前面多了 do 关键字,后面多了一个分号。
- 执行次序不同:while 循环先判断,再执行。do-while 循环先执行,再判断。
- 一开始循环条件就不满足的情况下,while 循环一次都不会执行,do-while 循环则不管什么情况下都至少执行一次。
for语句
for 语句是应用最广泛、功能最强的一种循环语句。大部分情况下,for 循环可以代替 while 循环、do while 循环。
for 语句是一种在程序执行前就要先判断条件表达式是否为真的循环语句。假如条件表达式的结果为假,那么它的循环语句根本不会执行。for 语句通常使用在知道循环次数的循环中。
for 语句语法格式如下所示。
1 | for(条件表达式1;条件表达式2;条件表达式3) { |
for 循环中 3 个条件表达式的含义如表 1 所示。
表达式 | 形式 | 功能 | 举例 |
---|---|---|---|
条件表达式 1 | 赋值语句 | 循环结构的初始部分,为循环变量赋初值 | int i=1 |
条件表达式 2 | 条件语句 | 循环结构的循环条件 | i>40 |
条件表达式 3 | 迭代语句,通常使用 ++ 或 – 运算符 | 循环结构的迭代部分,通常用来修改循环 变量的值 | i++ |
for 关键字后面括号中的 3 个条件表达式必须用“;”隔开。for 循环中的这 3 部分以及大括号中使循环体必需的 4 个组成部分完美地结合在一起,简单明了。
for 循环语句执行的过程为:首先执行条件表达式 1 进行初始化,然后判断条件表达式 2 的值是否为 true,如果为 true,则执行循环体语句块;否则直接退出循环。最后执行表达式 3,改变循环变量的值,至此完成一次循环。接下来进行下一次循环,直到条件表达式 2 的值为 false,才结束循环,其运行流程如图 1 所示。
值得指出的是,for 循环的循环迭代语句并没有与循环体放在一起,因此即使在执行循环体时遇到 continue 语句结束本次循环,循环迭代语句也一样会得到执行。
for 循环和 while、do while 循环不一样:由于 while、do while 循环的循环迭代语句紧跟着循环体,因此如果循环体不能完全执行,如使用 continue 语句来结束本次循环,则循环迭代语句不会被执行。但 for 循环的循环迭代语句并没有与循环体放在一起,因此不管是否使用 continue 语句来结束本次循环,循环迭代语句一样会获得执行。
与前面循环类似的是,如果循环体只有一行语句,那么循环体的大括号可以省略。
例如,同样是计算 5 的阶乘,使用 for 循环的实现代码如下:
1 | public static void main(String[] args) { |
上述语句的含义可以理解为,将 number 变量的值从 1 开始,每次递增 1,直到大于 5 时终止循环。在循环过程中,将 number 的值与当前 result 的值进行相乘。
for 语句中初始化、循环条件以及迭代部分都可以为空语句(但分号不能省略),三者均为空的时候,相当于一个无限循环。下面对这些情况依次进行介绍。
1.条件表达式 1 为空
for 语句中条件表达式 1 的作用可以在程序的其他位置给出,所以当条件表达式 1 为空时,for 语句后面括号内其他条件表达式执行的顺序不变。
例如,使用 for 语句的这种形式计算 1~100 所有奇数的和。
1 | public static void main(String[] args) { |
执行后的输出结果如下:
1 | 100 以内所有奇数和为:2500 |
2.条件表达式 2 为空
当 for 语句中条件表达式 2 为空时,将没有循环的终止条件。此时 for 语句会认为条件表达式 2 的值总是为真,循环无限制执行下去。因此,为了使循环达到某种条件时退出,需要在语句块中进行逻辑判断,并使用 break 语句来跳出循环,否则将产生死循环。
同样是计算 1~100 所有奇数的和,使用这种方式的代码如下。
1 | public static void main(String[] args) { |
3.条件表达式 3 为空
当 for 语言中条件表达式 3 为空时,也就没有设置控制变量的表达式,即每次循环之后无法改变变量的值,此时也无法保证循环正常结束。
同样是计算1~100 所有奇数的和,使用这种方式的代码如下:
1 | public static void main(String[] args) { |
如果没有循环体语句,number 变量的值为 1,永远小于 101,因此将无法结束循环,形成无限循环。在上面代码中将 number 的递增语句放在 for 循环体内,效果与完整 for 语句功能相同。
4.3 个条件表达式都为空
在 for 循环语句中,无论缺少哪部分条件表达式,都可以在程序的其他位置补充,从而保持 for 循环语句的完整性,使循环正常进行。
当 for 语句中循环体全为空时,即没有循环初值,不判断循环条件,循环变量不增值,此时无条件执行循环体,形成无限循环或者死循环。对于这种情况,读者在使用时应该尽量避免。
例如,计算 1~100 所有奇数的和,使用这种方式的代码如下:
1 | public static void main(String[] args) { |
例1
编写一个 Java 程序,统计某超市上半年的总销售量,要求由用户输入每月的销量。使用 for 循环的实现代码如下。
1 | public static void main(String[] args) { |
在该程序中, 声明循环变量 i,控制循环的次数,它被初始化为 1。每执行一次循环,都要对 i 进行判断,看其值是否小于等于 6,条件成立则继续累加成绩,否则退出循环。
每执行完一次循环体,都会对 i 累加 1。如此循环重复,直到 i 的值大于 6 时停止循环。此时退出 for 循环体,执行最下方的语句输出累加的销售总量。
for循环嵌套
和其他编程语言一样,Java 允许循环嵌套。如果把一个循环放在另一个循环体内,那么就可以形成嵌套循环。
嵌套循环既可以是 for循环嵌套 while 循环,也可以是 while 循环嵌套 do-while 循环 …… 即各种类型的循环都可以作为外层循环,也可以作为内层循环。
当程序遇到嵌套循环时,如果外层循环的循环条件允许,则开始执行外层循环的循环体,而内层循环将被外层循环的循环体来执行——只是内层循环需要反复执行自己的循环体而已。
当内层循环执行结束,且外层循环的循环体执行结束时,则再次计算外层循环的循环条件,决定是否再次开始执行外层循环的循环体。
根据上面分析,假设外层循环的循环次数为 n 次,内层循环的循环次数为 m 次,那么内层循环的循环体实际上需要执行 n×m 次。嵌套循环的执行流程如图所示。
从图 1 来看,嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为 false 时,才会完全跳出内层循环,才可以结束外层循环的当次循环,开始下一次循环。下面是一个使用嵌套循环输出九九乘法表实例。
1 | public static void main(String[] args) { |
运行上面程序,看到如下运行结果:
1 | 乘法口诀表: |
从上面运行结果可以看出,进入嵌套循环时,循环变量 i 开始为 1,这时即进入了外层循环。进入外层循环后,内层循环把 i 当成一个普通变量,其值为 0。在外层循环的当次循环里,内层循环就是一 个普通循环。
实际上,嵌套循环不仅可以是两层嵌套,而且可以是三层嵌套、四层嵌套 …… 不论循环如何嵌套, 总可以把内层循环当成外层循环的循环体来对待,区别只是这个循环体里包含了需要反复执行的代码。
for each语句
foreach 循环语句是 Java 1.5 的新特征之一,在遍历数组、集合方面,foreach 为开发者提供了极大的方便。foreach 循环语句是 for 语句的特殊简化版本,主要用于执行遍历功能的循环。
foreach 循环语句的语法格式如下:
1 | for(类型 变量名:集合) { |
其中,“类型”为集合元素的类型,“变量名”表示集合中的每一个元素,“集合”是被遍历的集合对象或数组。每执行一次循环语句,循环变量就读取集合中的一个元素,其执行流程如图所示。
例1
假设有一个数组,采用 for 语句遍历数组的方式如下:
1 | // 声明并初始化数组 |
上述第 2 行语句声明并初始化了 7 个元素数组集合,目前大家只需要知道当初始化数组时,要把相同类型的元素放到 {…} 中并且用逗号分隔(,)即可。
关于数组集合会在教程后面详细介绍,这里我们只需要大概了解就可以。numbers.length 是获得数组的长度,length 是数组的属性,numbers[i] 是通过数组下标访问数组元素。
那么采用 for-each 循环语句遍历数组的方式如下:
1 | // 声明并初始化int数组 |
从示例中可以发现,item 不是循环变量,它保存了集合中的元素,for-each 语句将集合中的元素一一取出来,并保存到 item 中,这个过程中不需要使用循环变量,通过数组下标访问数组中的元素。可见 for-each 语句在遍历集合的时候要简单方便得多。
return语句
return 关键字并不是专门用于结束循环的,return 语句用于终止函数的执行或退出类的方法,并把控制权返回该方法的调用者。如果这个方法带有返回类型,return 语句就必须返回这个类型的值;如果这个方法没有返回值,可以使用没有表达式的 return 语句。
return 语句的一般语法格式如下:
1 | return 与方法相同类型的变量; |
如果一个方法使用了 return 语句并且后面跟有该方法返回类型的值,那么调用此方法后,所得到的结果为该方法返回的值。
例 1
当用户输入两个 double 类型的数值后需要计算这两个数值之和,而这两个操作数是可以变动的,但是求和的功能是不变的,这时就需要定义一个方法,只要在需要求和时调用该方法即可,该方法将计算之后的结果返回。代码如下:
1 | import java.util.Scanner; |
当用户输入两个 double 类型的数值后,程序调用 sum() 方法。该方法有两个参数,分别表示用户输入的操作数 1 和操作数 2。在调用该方法时,只需要将用户输入的两个数值传递过去即可,然后程序会执行 sum() 方法,对这两个数求和,并使用 return 语句将计算得出的结果返回。
在 main() 方法中,同样需要使用 double 类型的变量来接收 sum() 方法返回的值,即变量 d 为计算后的结果。
break语句
某些时候需要在某种条件出现时强行终止循环,而不是等到循环条件为 false 时才退出循环。此时,可以使用 break 来完成这个功能。
break 用于完全结束一个循环,跳出循环体。不管是哪种循环,一旦在循环体中遇到 break,系统将完全结束该循环,开始执行循环之后的代码。
在 Java 中,break 语句有 3 种作用,分别是:在 switch 语句中终止一个语句序列、使用 break 语句直接强行退出循环和使用 break 语句实现 goto 的功能。
使用 break 语句直接强行退出循环(break 不带标签)
可以使用 break 语句强行退出循环,忽略循环体中的任何其他语句和循环的条件判断。在循环中遇到 break 语句时,循环被终止,在循环后面的语句重新开始。
例1
小明参加了一个 1000 米的长跑比赛,在 100 米的跑道上,他循环地跑着,每跑一圈,剩余路程就会减少 100 米,要跑的圈数就是循环的次数。但是,在每跑完一圈时,教练会问他是否要坚持下去,如果回答 y,则继续跑,否则表示放弃。
使用 break 语句直接强行退出循环的示例如下:
1 | public static void main(String[] args) { |
该程序运行后的效果如下所示:
1 | 跑的是第1圈 |
尽管 for 循环被设计为从 0 执行到 10,但是当小明的回答不是 y 时,break 语句终止了程序的循环,继续执行循环体外的代码,输出“加油!继续!”。
break 语句能用于任何 Java 循环中,包括人们有意设置的无限循环。在一系列嵌套循环中使用 break 语句时,它将仅仅终止最里面的循环。例如:
1 | public static void main(String[] args) { |
该程序运行结果如下所示:
1 | 第1次循环:内循环的第1次循环 内循环的第2次循环 内循环的第3次循环 |
从程序运行结果来看,在内部循环中的 break 语句仅仅终止了所在的内部循环,外部循环没有受到任何的影响。
注意:一个循环中可以有一个以上的 break 语句,但是过多的 break 语句会破坏代码结构。switch 循环语句中的 break 仅影响 switch 语句,不会影响循环。
例 2
编写一个 Java 程序,允许用户输入 6 门课程成绩,如果录入的成绩为负则跳出循环;如果录入 6 门合法成绩,则计算已有成绩之和。
使用 break 语句的实现代码如下:
1 | public static void main(String[] args) { |
运行程序,当用户录入的分数低于 0 时,则输出“抱歉,分数录入错误,请重新录入!”信息,否则打印学生的总成绩。输出结果如下所示。
1 | 请输入学生的姓名: |
1 | 请输入学生的姓名: |
在该程序中,当录入第 3 门课的成绩时,录入的成绩为负数,判断条件“score<0”为 true,执行“con=false”,用 con 来标记录入是否有误。接着执行 break 语句,执行完之后程序并没有继续执行条件语句后面的语句,而是直接退出 for 循环。之后执行下面的条件判断语句,判断 boolean 变量的 con 是否为 true,如果为 true,则打印总成绩;否则打印“抱歉,分数录入错误,请重新录入!”。
使用 break 语句实现 goto 的功能(break 带标签)
有时候,在嵌套很深的循环语句中会发生一些不可预料的事情。此时可能更加希望跳到嵌套的所有循环语句之外。通过添加一些额外的条件判断实现各层循环的检测很不方便。
幸运的是,Java 与 C++ 不同,Java 提供了一种带标签的 break 语句,用于跳出多重嵌套的循环语句。
break 语句可以实现 goto 的功能,并且 Java 定义了 break 语句的一种扩展形式来处理退出嵌套很深的循环这个问题。
通过使用扩展的 break 语句,可以终止执行一个或者几个任意代码块,这些代码块不必是一个循环或一个 switch 语句的一部分。同时这种扩展的 break 语句带有标签,可以明确指定从何处重新开始执行。
break 除了具有 goto 退出深层循环嵌套作用外,还保留了一些程序结构化的特性。
标签 break 语句的通用格式如下:
1 | break label; |
label 是标识代码块的标签。当执行这种形式的 break 语句时,控制权被传递出指定的代码块。被加标签的代码块必须包围 break 语句,但是它不需要直接包围 break 的块。也就是说,可以使用一个加标签的 break 语句来退出一系列的嵌套块,但是不能使用 break 语句将控制权传递到不包含 break 语句的代码块。
用标签(label)可以指定一个代码块,标签可以是任何合法有效的 Java 标识符,后跟一个冒号。加上标签的代码块可以作为 break 语句的对象,使程序在加标签的块的结尾继续执行。
下面是使用带标签的break 语句的示例。
1 | public class GotoDemo { |
以上程序的执行结果为:
1 | 0 |
这里的 label 是标签的名称,可以为 Java 语言中任意合法的标识符。标签语句必须和循环匹配使用,使用时书写在对应的循环语句的上面,标签语句以冒号结束。如果需要中断标签语句对应的循环,可以采用 break 后面跟标签名的方式。
如在上面代码中,当 j 为 1 时,“j%2!=0”条件表达式成立,则 label 标签所代表的最外层循环终止。
注意:通常紧跟 break 之后的标签,必须在 break 所在循环的外层循环之前定义才有意义。
continue语句
有时强迫一个循环提早反复是有用的,也就是,你可能想要继续运行循环,但是要忽略这次重复剩余的循环体的语句,所以 Java 提供了 continue 语句。continue 语句是 break 语句的补充。
与 break 语句一样, 它将中断正常的控制流程。continue 语句将控制转移到最内层循环的首部。
continue 语句是跳过循环体中剩余的语句而强制执行下一次循环,其作用为结束本次循环,即跳过循环体中下面尚未执行的语句,接着进行下一次是否执行循环的判定。
continue 语句类似于 break 语句,但它只能出现在循环体中。它与 break 语句的区别在于:continue 并不是中断循环语句,而是中止当前迭代的循环,进入下一次的迭代。简单来讲,continue 是忽略循环语句的当次循环。
注意:continue 语句只能用在 while 语句、for 语句或者 foreach 语句的循环体之中,在这之外的任何地方使用它都会引起语法错误。
在循环体中使用 continue 语句有两种方式可以带有标签,也可以不带标签。语法格式如下:
1 | continue //不带标签 |
下面看一个示例,代码如下:
1 | int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; |
在上述程序代码中,当条件 i==3 的时候执行 continue 语句,continue 语句会终止本次循环,循环体中 continue 之后的语句将不再执行,接着进行下次循环,所以输出结果中没有 3。程序运行结果如下:
1 | Count is: 0 |
带标签的 continue 语句示例代码如下:
1 | public static void main(String[] args) { |
默认情况下,continue 只会跳出最近的内循环(代码第 3 行的 for 循环),如果要跳出代码第 2 行的外循环,可以为外循环添加一个标签 label1,然后在第 5 行的 continue 语句后面指定这个标签 label1,这样当条件满足执行 continue 语句时,程序就会跳转出外循环。
程序运行结果如下:
1 | 0,5 |
由于跳过了 x == y,因此下面的内容没有输出。
1 | 1,1 |
输出杨辉三角形
杨辉三角形由数字进行排列,可以把它看作是一个数字表,其基本特性是两侧数值均为 1,其他位置的数值是其左上方数值与右上角数值之和。打印杨辉三角形时需要使用到 for 循环语句。
打印杨辉三角形的实现思路是:每一行前面都是空格,而每行空格的个数需要根据总行数来确定,这个可以通过找规律归纳出来。关键是数值的实现,每一行的数值(除了第一列和最后一列)都是上一行两个数值之和,因此可以通过上一行来获取。
实现步骤如下:
(1) 创建名称为 Test28 的类,在该类中声明 num() 方法,在 num() 方法中传入两个参数,即 x 和 y。其中,x 表示行,y 表示列。num() 方法用于计算第 x 行第 y 列的数值。代码如下:
1 | public static int num(int x, int y) { |
(2) 创建名称为 calculate 的方法,在该方法中传入一个 int 类型的参数,该参数表示打印杨辉三角形的行数。代码如下:
1 | public static void calculate(int row) { |
(3) 在 main() 方法中添加代码,首先接收用户在控制台输入的打印行数,然后将行数作为参数传入到调用的 calculate() 方法中。代码如下:
1 | public static void main(String[] args) { |
(4) 运行代码进行测试,其运行结果如下所示:
1 | 打印杨辉三角形的行数:7 |