第3章 流程控制 一般来说,程序的执行通常是按照语句书写的顺序从前往后一条一条执行的,流程控制 语句可以用来改变程序执行的顺序。程序利用流程控制语句有条件地执行语句、循环性地 执行语句,或者跳转性地执行语句,从而实现一些复杂的算法。Java语言的流程控制语句 分为分支语句、循环语句和跳转语句。 3.1 分支语句 分支结构是根据布尔条件表达式的值采取不同的动作,应用在程序中能够使程序更灵 活。在Java语言中使用的分支语句有if…else语句和switch语句。 3.1.1 if…else语句 if…else语句可以使程序根据条件执行不同的语句。if…else语句包括单独的if语句、 if…else语句和嵌套的if语句。 1.if语句 if语句的语法形式如下: if (表达式){ 语句块; } 这是最简单的if语句,为单分支结构。当表达式的值为true时,执行语句块,否则忽略 语句块,执行其后的语句,如图3.1所示。 2.if…else语句 if…else语句的语法形式如下: if (表达式) { 语句块1; }e lse { 语句块2; } 当if表达式为true时,程序执行语句块1,执行完后跳出if语句;如果表达式为false, 程序跳过语句块1,执行else后的语句块2,执行完后结束if语句,如图3.2所示。 例3.1 if…else应用实例,根据成绩判断并输出是否及格(ch03\IfDemo.java)。 public class IfDemo { public static void main(String[] args) { ·40· 图3.1 if语句图3.2 if…else语句 int score=71; if (score>=60) System.out.println("成绩及格!"); else System.out.println("成绩不及格"); } } 运行结果: 成绩及格! 3.嵌套的if语句 对于超过二分支情况的选择,可以使用嵌套的if…else语句。语法形式如下: if (表达式1) { 语句块1; } else if (表达式2) {语句块2; } else if (表达式3) {语句块3; } … else if (表达式m ) {语句块m ; } else {语句块m +1; } 这种嵌套的if语句,将会依次计算表达式的值,如果某个表达式的值为true,则执行其 后的语句块,执行完后跳出if语句;若所有表达式都为false,则执行最后一个else后的语句 块,如图3.3所示。 例3.2 嵌套的if语句的应用,根据成绩判断并输出所在级别(ch03\IfElseDemo .java)。 public class IfElseDemo { public static void main(String[] args) { int score=83; if (score<60) { System.out.println("成绩为不及格!"); } else if (score<70) { System.out.println("成绩为及格!"); } else if (score<80) { System.out.println("成绩为中!"); } else if (score<90) { System.out.println("成绩为良!"); } else if (score<100) { System.out.println("成绩为优!"); } else if (score==100) { System.out.println("成绩为满分!"); } else ·41· 图3.3 嵌套的if语句 System.out.println("Error!"); } } 运行结果: 成绩为良! 3.1.2 switch语句 虽然嵌套的if语句可以实现多分支处理,但是语句较为复杂,并且容易混乱和出错,因 此可以使用switch语句来实现多分支情况的处理。switch语句也叫开关语句,当条件有很 多选项时,switch语句可以很容易写出判断条件,使得编写的程序结构较为清晰。 switch的语法形式如下: switch(表达式) { case 常量值1: 语句块1; [ break; ] case 常量值2: 语句块2; [ break; ] case 常量值3: 语句块3; [ break; ] … case 常量值m : 语句块m ; [ break; ] [default: 语句块m +1;[break;]] } 其中,switch表达式的类型可以是整型(byte、short、int、long)、字符类型、枚举类型,在JDK 1.7版本中又新增了表达式的类型可以是String类型。各case后面的常量值必须与switch 后面的表达式类型一致,而且每个case后面的值不能有重复;switch语句中的break语句 为可选项,根据需要进行设置。switch语句的执行顺序是:首先计算表达式的值,如果表达 式的值和某个case后的常量值相同,就执行该case后的语句块,直到遇到break语句结束 整个switch语句,如果该case后没有设置break语句,则程序将继续执行下面的各case语 ·42· 句里的语句块,直到遇到break语句跳出switch语句。若没有一个case的常量值与表达式 的值相同,则执行default后面的语句块。default语句是可选项,根据需要选用。如果程序 没有设置default,则当没有一个case的常量值与表达式的值相同时,那么switch语句将不 做任何处理。switch语句可以嵌套使用,即在一个case语句块中可以使用另一个switch语 句结构,对另一个表达式的值进行判断。 例3.3 switch分支结构的应用,根据数值判断并输出为星期几(ch03\SwitchDemo .java)。 public class SwitchDemo { public static void main(String[] args) { int day=6; switch(day){ case 1: System.out.println("Monday"); break; case 2: System.out.println("Tuesday"); break; case 3: System.out.println("Wednesday"); break; case 4: System.out.println("Thursday"); break; case 5: System.out.println("Friday"); break; case 6: System.out.println("Saturday"); break; case 7: System.out.println("Sunday"); break; default: System.out.println("Error!"); } } } 运行结果: Saturday 在JDK1.7之前的版本中,switch表达式的类型不能是String类型,而在JDK1.7中新 ·43· 增了switch表达式可以是String类型这一新特性,如例3.4所示。 例3.4 从控制台输入星期几,输出该日处于一周的哪个阶段(ch03\SwitchString- Demo.java)。 public class SwitchStringDemo { public static void main(String[] args) { String typeOfDay; switch(args[0]) { case "Monday": typeOfDay="Start of work week"; break; case "Tuesday": case "Wednesday": case "Thursday": typeOfDay="Midweek"; break; case "Friday": typeOfDay="End of work week"; break; case "Saturday": case "Sunday": typeOfDay="Weekend"; break; default: typeOfDay="Invalid day of the week: "+args[0]; } System.out.println(typeOfDay); } } 以上例子可以使用“caseL->”形式来简化,不再需要break语句来中断分支,default 也是可选的。如例3.3可以改为如下代码片段: int day=6; switch(day){ case 1->System.out.println("Monday"); case 2->System.out.println("Tuesday"); case 3->System.out.println("Wednesday"); case 4->System.out.println("Thursday"); case 5->System.out.println("Friday"); case 6->System.out.println("Saturday"); case 7->System.out.println("Sunday"); default->System.out.println("Error!"); } ·44· 代码中可以同时判断多个条件,如下为判断某个月有多少天的代码片段(其中out为静 态导入的System.out): int month=2; switch(month) { case 1, 3, 5, 7, 8, 10, 12-> { out.println(31); out.println("多行代码"); } case 2->out.println(28); case 4, 6, 9, 11->out.println(30); default->throw new IllegalArgumentException("没有这个月份:" +month); } 3.1.3 switch表达式 switch可以用作表达式计算值,形式如下: case label_1, label_2, …, label_n ->表达式;|throw 语句;|代码块 注意: . 不需要break中断分支。 . 箭头右边如果是一个表达式,则该表达式的值为该分支的值。 . 必须要有default,除非为enum 类型且列举了所有值。 . 代码块中可以用yield为该分支指定值。 如下为计算某个月有多少天的代码片段: int month=2; int days=2*switch(month) { case 1, 3, 5, 7, 8, 10, 12->31; case 2->28; case 4, 6, 9, 11->30; default->throw new IllegalArgumentException("没有这个月份" +month); }; out.println(days); 如果箭头右边为代码块,可以用yield来返回该分支的值,代码如下: int month=3; int days=2*switch(month) { case 1, 3, 5, 7, 8, 10, 12->{ out.println("仅做示例"); yield 31; } case 2->28; case 4, 6, 9, 11->30; default->throw new IllegalArgumentException("没有这个月份:" +month); ·45· }; out.println(days); 用在enum 类型的例子见第7章枚举相关内容。 3.2 循环语句 循环结构可以根据一定的条件使某一段程序能够重复执行多次,直到满足终止条件为 止。Java提供了3种形式的循环语句:while语句、do…while语句和for语句。 3.2.1 while语句 while语句的语法形式如下: while(表达式){ 循环体 } while语句首先计算表达式的值,它将返回一个布尔值true或false。如果值为true,执 行循环体的语句,然后计算表达式的值,判断是否执行循环体,直到表达式的值为false,跳 出while循环,执行其后的程序,如图3.4所示。 例3.5 应用while循环语句,求1+2+3+…+100的值(ch03\WhileDemo.java)。 public class WhileDemo { public static void main(String[] args) { int i, sum=0; i=1; while (i<=100) { sum+=i++; } System.out.println("1~100 的和为"+sum); } } 运行结果: 1~100 的和为5050 使用while循环语句时应注意,在循环体内要有使循环趋于结束的语句,即每执行一次 循环,循环条件要发生相应的变化,使得表达式的值能够最终为false,否则将出现死循环的 情况,这在程序设计应尽量避免。 3.2.2 do…while语句 do…while循环与while循环不同,do…while循环先执行循环体中的语句,然后计算表达 式的值,若值为true,则继续执行下一轮循环,当表达式的值为false时,跳出循环,如图3.5 所示。 ·46· 图3.4 while循环语句图3.5 do…while循环语句 do…while循环的语法形式如下: do { 循环体 } while(表达式); 例3.6 使用do…while循环语句计算1+2+3+…+100的值(ch03\DoWhileDemo .java)。 public class DoWhileDemo { public static void main(String[] args) { int i, sum=0; i=1; do { sum+=i++; } while (i<=100); System.out.println("1~100 的和为"+sum); } } 运行结果: 1~100 的和为5050 由此可见,while循环与do…while循环可以实现同一个程序功能,但是它们也有区别, 前者是先判断后执行,后者是先执行后判断。do…while循环可以保证循环至少执行一次, 而while循环有可能一次都不执行,这是do…while循环与while循环最大的区别。 3.2.3 for语句 for语句提供了一种更为灵活的循环方式,可以使程序按照指定的次数进行循环。其语 法形式如下: for (初始表达式; 逻辑表达式; 增量表达式) { 循环体; } for循环语句执行时,首先执行初始表达式,然后计算逻辑表达式来判断是否终止,当逻 辑表达式的值为true时,执行循环体中的语句,然后执行增量表达式。完成一次循环后,重 ·47· 图3.6 for循环语句 新判断终止条件,直到终止表达式的值为false时,跳出整个for 循环,如图3.6所示。 因此,上述程序中计算1~100的和的循环可设计如下: int i, sum=0; for (i=1;i<=100;i++) { sum+=i; }S ystem.out.println("1 到100 的和为"+sum); for循环语句通常用来执行循环次数确定的情况。初始表 达式在循环开始的时候被执行一次;逻辑表达式决定什么时候 终止循环,该表达式在每次循环的过程中都被执行一次;增量表 达式决定了循环一次增加的步长值。以上三部分都可以按照情 况设置为空语句(但“;”不能省),三者均为空的时候,相当于一个无限循环,在程序设计中应 避免出现无限循环的情况。 例3.7 应用for语句,对数组中的数据进行冒泡排序(从小到大)并输出(ch03\ ForDemo.java)。 public class ForDemo{ public static void main(String[] args){ int i; int intArray[]={39, 12, 5, 102,76}; int n=intArray.length; for (i=0; i<n-1; i++){ for (int j=0; j<n-1; j++){ if (intArray[j]>intArray[j+1]) { int temp=intArray[j]; intArray[j]=intArray[j+1]; intArray[j+1]=temp; } } } for (i=0; i<n; i++) { System.out.println(intArray[i]); } } } 运行结果: 512 39 76 102 ·48· 3.2.4 for…each语句 for…each语句是for语句的特殊简化版本,也称为增强的for循环,是JDK1.5的新特 性之一,在遍历数组和集合方面,为开发人员提供了极大的方便。for…each语句并不能完 全取代for语句,但所有for…each语句都可以改写为for语句。 for…each不是一个关键字,其语法格式如下: for (部分类型变量名:整体){ 循环体; } 其中,整体由多个值组成,每次从整体中取出一部分,并赋值给冒号前的变量。例如: int[] months={1,3,5,7,8,10,12}; for (int month:months){ //months 由多个int 组成,每次从中取出一个,赋值给month System.out.println(month); } for…each语句经JDK编译后成为普通的for语句。 例3.8 应用for…each语句,遍历数组,输出其中各元素(ch03\ForEachDemo.java)。 public class ForEachDemo{ public static void main(String[] args){ String[] str={"a","b","c"}; int arr[][]={{9, 6}, {3, 1}}; for (String s: str){ System.out.print(s+" "); //逐个输出数组元素的值 } System.out.print("\n"); for (int x[] : arr) { for (int i : x) { System.out.print(i+" "); } System.out.print("\n"); } } } 运行结果: a b c 9 6 3 1 for…each语句的局限性:当要引用数组或者集合的指定元素(例如给某个数组元素赋 值等)时,for…each语句无法做到,仍需使用基本的for语句。 ·49· 3.2.5 嵌套循环 嵌套循环是指在某个循环语句的循环体中又包含另一个循环语句,也称多重循环。 Java语言提供的3种循环语句可以互相嵌套使用。例3.9即为一种嵌套循环。 例3.9 应用嵌套循环,输出九九乘法表(ch03\WhileAndForDemo.java)。 public class WhileAndForDemo { public static void main(String[] args) { for (int i=1;i<=9;i++) { int j=1; while (j<=i) { System.out.print(j+"*"+i+"="+j*i+"\t"); j++; } System.out.print("\r\n"); //输出一个回车换行符 } } } 运行结果: 1*1=1 1*2=2 2*2=4 1*3=3 2*3=6 3*3=9 1*4=4 2*4=8 3*4=12 4*4=16 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81 设计嵌套循环时应注意,内层循环语句必须完整地被包含在外层循环的循环体中,切不 可出现内外层循环交叉的情况。 3.2.6 循环语句的对比 Java的3种循环语句可以解决同一问题,在很多情况下,3种循环语句可以相互转换使 用,但在实际应用中,也有一定的区别。 (1)while语句和do…while语句只在while后面指定循环条件,但是需要在循环体中 包括使循环趋于结束的语句,而for语句则可以在增量表达式中包含使循环趋于结束的 语句。 (2)用while语句和do…while语句时,对循环变量的初始化操作应该放在while语句 和do…while语句之前,而for语句则可以在初始表达式中完成。 (3)while语句和do…while语句实现的功能相同,唯一的区别就是do…while语句先 执行后判断,无论表达式的值是否为true,都将执行一次循环;而while语句则是首先判断 ·50· 表达式的值是否为true,如果为true则执行循环语句,否则不执行循环语句。 (4)for循环语句一般用在对于循环次数已知的情况,而while语句和do…while语句 则一般用在对于循环次数不确定的情况。 3.3 跳转语句 跳转语句可以无条件地改变程序的执行顺序。Java语言提供了三种跳转语句,分别为 break语句、continue语句和return语句。 3.3.1 break语句 1.无标签break语句 break语句应用于switch、for、while及do语句中,可以立即终止执行包含break语句 所在的一个程序块。 例3.10 使用无标签的break语句(ch03\BreakDemo.java)。 public class BreakDemo { public static void main(String[] args) { boolean test=true; int i=1; while (test) { System.out.println("i="+i); if (i==5) break; i++; } System.out.println("i 为5 时结束循环!"); } } 运行结果: i=1 i=2 i=3 i=4 i=5 i 为5 时结束循环! 在上述程序中,尽管while循环的判断条件一直为true,但是当i等于5时,break语句 结束整个循环,接着执行循环后续的代码。因此,在循环语句中,可以使用break语句中断 正在执行的循环。 在实际的代码中,结构往往会因为逻辑比较复杂,而存在循环语句的嵌套,如果break 语句出现在循环嵌套的内部时,则只结束break语句所在的循环,对于其他的循环没有影 响,示例代码如下: ·51· for (int i=0;i<2;i++) { for (int j=0;j<5;j++) { System.out.println(i+","+j); if (j==2) break; } } 运行结果: 0,0 0,1 0,2 1,0 1,1 1,2 该break语句因为出现在循环变量为j的循环内部,则执行到break语句时,只中断循 环变量为j的循环,而对循环变量为i的循环没有影响。 使用break语句应注意,一个循环中可以有一个以上的break语句,但是太多的break 语句会破坏代码结构。另外,switch语句中的break仅影响所在的switch语句,不会影响 外层的任何循环。 2.带标签的break语句 break语句只终止执行包含它的最小语句块,如果希望终止更外层的语句块,可以用带 标签的break语句。 带标签的break语句的格式如下: break 标签; 当break语句以这种形式执行时,可控制传递出指定的代码块。被加标签的代码块必 须包含break语句,但是它不需要是直接包含break块。因此可以使用一个加标签的break 语句退出一系列的嵌套块。 要为一个代码块加标签,在其开头加一个标签即可。标签可以是任何合法有效的Java 标识符,标签后跟一个“:”。给一个块加上标签后,就可以使用这个标签作为break 语句的 对象。例 3.11 使用带标签的break语句(ch03\LabelBreakDemo.java)。 public class LabelBreakDemo { public static void main(String[] args) { loop: for (int i=1;i<=9;i++) { for (int j=1;j<=i;j++) { if (j*i==16) { break loop; } System.out.print(j+"*"+i+"="+j*i+" "); ·52· } System.out.print("\r\n"); //输出一个回车换行符 } } } 运行结果: 1*1=1 1*2=2 2*2=4 1*3=3 2*3=6 3*3=9 1*4=4 2*4=8 3*4=12 3.3.2 continue语句 continue语句只能用于循环结构中,用于跳过当次循环的剩余语句,重新开始下一轮循 环。continue语句也有两种形式,即无标签的和带标签的。 1.无标签的continue语句 无标签的continue语句可以结束当次循环,即跳过continue语句后面剩余部分,根据 条件判断是否进入下一轮循环。 例3.12 使用无标签的continue语句,输出100 以内能够被7 整除的数(ch03\ ContinueDemo.java)。 public class ContinueDemo { public static void main(String[] args) { for (int i=1;i<100;i++) { if (i%7!=0) { //当i 的值不能被7 整除时 continue; } System.out.print(i+" "); //输出i 的值 } } } 运行结果: 7 14 21 28 35 42 49 56 63 70 77 84 91 2.带标签的continue语句 continue语句和break语句一样,也可以和标签搭配使用。其作用也是用于跳出深层 循环。带标签的continue语句也是通常用在嵌套循环的内循环中,其语句格式如下: continue 标签; continue后的标签必须标识在循环语句之前,使程序的流程在遇到continue之后,立即 结束当次循环,转入标签所标识的循环层次,进行下一轮循环。 例3.13 使用带标签的continue语句(ch03\LabelContinueDemo.java)。 ·53· public class LabelContinueDemo { public static void main(String[] args) { loop: for (int i=1;i<=9;i++){ for (int j=1;j<=i;j++){ if (j==2){ continue loop; //跳出双层for 循环语句 } System.out.print(j+"*"+i+"="+j*i+"\t"); } System.out.print("\r\n"); //输出一个回车换行符 } } } 运行结果: 1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 1*6=6 1*7=7 1*8=8 1*9=9 3.3.3 return语句 return语句用来返回方法的值,其功能是从当前方法退出,返回调用该方法的语句,语 句格式如下: return 表达式; return语句通常位于一个方法体的最后一行,return语句退出方法并返回一个值。由 return返回的表达式的值必须与方法的返回值类型一致。当方法用void声明时,说明方法 无返回值,return语句省略。 例3.14 使用return语句,计算圆面积(ch03\ReturnDemo.java)。 public class ReturnDemo { final static double PI=3.14; public static void main(String[] args) { double r1=3.0, r2=5.0; System.out.println("半径为"+r1+"的圆面积为"+area(r1)); System.out.println("半径为"+r2+"的圆面积为"+area(r2)); } static double area(double r) { return (PI*r*r); } } 运行结果: 半径为3.0 的圆面积为28.259999999999998 半径为5.0 的圆面积为78.5 ·54· 本章小结 本章介绍了Java流程控制语句,包括分支结构的if…else语句和switch语句,循环结 构的while语句、do…while语句和for语句,以及跳转语句break、continue和return,通过 对语句的介绍,说明了语句的特点及应用。 习 题 3 1.有如下函数,要求输入x 的值,求y 的值。(要求用if…else语句实现)编辑、编译、运 行该程序,分别使用数据-5、0、5、10、100做测试,写出相应的结果。 y = x2, x <0 2x -1, 0≤x <10 3x -11, x ≥10 ì . í .. .. 2.求一元二次方程ax2+bx+c=0的根(考虑b2-4ac 的值的3种情况)。 3.将一个数组中的值按逆序重新存放。 4.求两个整数的最大公约数和最小公倍数。 5.求100~200的全部素数。 6.输出100~999所有的“水仙花数”。 7.求Fibonacci数列的前40个数。即F1=1,F2=1,Fn =Fn-1+Fn-2(n≥3)。 8.用π 4≈1-13 +15 -17 +…公式求π的近似值,直到最后一项的绝对值小于10-6。 9.一个数如果恰好等于它的因子之和,这个数就是完数。例如6的因子为1、2、3,而 6=1+2+3,因此6是一个完数。编程求出1000之内的所有完数。并按如下格式输出: 6,因子是1,2,3 … 10.用迭代法求出x= a 。求平方根的迭代公式为 xn+1 =12 xn +a xn . è . . . ÷ 其中,x0=a/2,要求前后两次求出的x 的差的绝对值小于10-5。 11.一球从100m 的高度自由落下,每次落地后反跳回原来高度的一半,再落下。求它 在第10次落地时共经过多少米,第10次反弹多高。 12.有一堆零件(100~200个),如果以4个零件为一组进行分组,则多2个零件;如果 以7个零件为一组进行分组,则多3个零件;如果以9个零件为一组进行分组,则多5个零 件。编程求解这堆零件的总数。 13.已知大公鸡三文钱一只,大母鸡两文钱一只,小鸡一文钱买三只。现有100文钱, 想买100只鸡,请编写程序解决这个问题。 14.《孙子算经》中记载了这样的一道题:“今有雉兔同笼,上有三十五头,下有九十四 ·55· 足,问雉兔各几何? ” 这四句话的意思就是:有若干只鸡和兔在同一个笼子里,从上面数,有 35 个头;从下面数,有94 只脚。求笼中各有几只鸡和兔。 15. 猴子吃桃问题。猴子第一天摘了若干桃子,当即吃了一半,还不过瘾,又多吃了一 个,第二天早上又吃了剩下的一半多一个,以后每天早上如此,到第10 天早上想再吃时,只 剩下一个桃子了。求第一天共摘了多少桃子。 16. 洗碗(中国古题):有一位妇女在河边洗碗,过路人问她为什么洗这么多碗 ? 她回答 说:家中来了很多客人,他们每两人合用一个饭碗,每三人合用一个汤碗,每四人合用一个 菜碗,共用了65 个碗。你能通过她家的用碗情况算出她家来了多少客人吗 ? ·56 ·