第7章 程序执行方向—程序控制结构
本章视频教学录像:3小时3分钟
程序之所以能够按照人们的意愿执行,主要依靠的是程序的控制结构。本章重点介绍选择(如if…else语句、switch语句等)与循环结构语句(如while、do-while循环及for循环等)。学习如何利用这些不同的结构编写出有用的程序,让程序的编写更灵活,操控更方便。
本章要点(已掌握的在方框中打钩)
□ 掌握程序结构的设计方法
□ 掌握选择结构的类型与用法
□ 掌握循环结构的类型与用法
□ 掌握循环的跳离语句的运用方法
7.1 程序逻辑
本节视频教学录像:23分钟
结构化程序设计(Structured programming)是一种经典的编程模式,在1960年开始发展,其思想最早是由荷兰著名计算机科学家E.W. Dijkstra提出的,他设计了一套规则,使程序设计具有合理的结构,用以保证程序的正确性。这套规则要求程序设计者按照一定的结构形式来设计和编写程序,而不是“天马行空”地根据程序员的意愿来编写。早期的程序员广泛使用GOTO语句,而自从结构化编程思想推广以来,它已经日益淡出程序设计的舞台。
GOTO语句也称为无条件转移语句,它破坏了程序设计结构性,导致程序流程的混乱,使理解和调试程序都产生困难。1966年5月在著名学术期刊《Communications of the ACM》发表论文,说明任何一个有goto指令的程序,可以改为完全不使用goto指令的程序,即“所有有意义的程序流程都可以使用三种基本的结构来构成”。 1968年Dijkstra等人发表了著名的论文《GOTO语句有害论》(Go To Statement Considered Harmful)。
自此人们的编程方式发生重大变化,每种语言都提供这些基本控制结构的实现方式,并提供把数据访问局部化的能力,以及某种形式的模块化编译机制。正是这个原因,在 Java程序设计中,虽然goto作为关键字保留了下来,但是一直没有启用。
结构化程序设计语言,强调用模块化、积木式的方法来建立程序。采用结构化程序设计方法,可使程序的逻辑结构清晰、层次分明、可读性好、可靠性强,从而提高了程序的开发效率,保证了程序质量,改善了程序的可靠性。
一般来说程序的结构包含以下3种。
⑴ 顺序结构。
⑵ 选择结构。
⑶ 循环结构。
这3种不同的结构有一个共同点,就是它们都只有一个入口,也只有一个运行出口。程序中使用了上面这些结构到底有什么好处呢?这些单一的入口、出口可让程序可控、易读、好维护。
7.1.1 顺序结构
结构化程序的最简单的结构就是顺序结构。所谓顺序结构程序就是按书写顺序执行的语句构成的程序段,其流程如下图a所示。
通常情况下,顺序结构是指按照程序语句出现的先后顺序一句一句地执行。前几个章节的范例,大多数都属于顺序结构程序。有一些程序并不按顺序执行语句,这个过程称为“控制的转移”,它涉及了另外两类程序的控制结构,即分支结构和循环结构。
7.1.2 分支结构
选择结构也称为分支结构,在许多实际问题的程序设计中,根据输入数据和中间结果的不同,需要选择不同的语句组执行。在这种情况下,必须根据某个变量或表达式的值作出判断,以决定执行哪些语句和不执行哪些语句,其流程如下图b所示。
(a) 顺序结构
(b) 选择结构
选择结构是根据给定的条件进行判断,决定执行某个分支的程序段。条件分支不是我们常说的“兵分两路”,“兵分两路”是两条路都有“兵”,而这里的条件分支,在执行时“非此即彼”,不可兼得,其主要用于两个分支的选择,由if 语句和if … else 语句来实现。if语句在6.1.7小节中已经介绍过了。下面介绍一下if…else语句。
if…else语句可以依据判断条件的结果,来决定要执行的语句。当判断条件的值为真时,就运行“语句1”;当判断条件的值为假时,则执行“语句2”。不论执行哪一个语句,最后都会再回到“语句3”继续执行。详细解释请参见7.2.2小节。
7.1.3 循环结构
循环结构是程序中的另一种重要结构,它和顺序结构、选择结构共同作为各种复杂程序的基本构造部件。循环结构的特点是在给定条件成立时,反复执行某个程序段。通常我们称给定条件为循环条件,称反复执行的程序段为循环体。循环体可以是复合语句、单个语句或空语句。在循环体中也可以包含循环语句,实现循环的嵌套。循环结构的流程如下图所示。
7.2 选择结构
本节视频教学录像:60分钟
Java语言中的选择结构提供了以下两种类型的分支结构。
条件分支:根据给定的条件进行判断,决定执行某个分支的程序段。
开关分支:根据给定整型表达式的值进行判断,然后决定执行多路分支中的一支。
条件分支主要用于两个分支的选择,由if 语句和if…else 语句来实现。开关分支用于多个分支的选择,由switch 语句来实现。在语句中加上了选择结构之后,就像是十字路口,根据不同的选择,程序的运行会有不同的结果。
7.2.1 if语句
if 语句(if-then Statement)用于实现条件分支结构,它在可选动作中作出选择,执行某个分支的程序段。if 语句有两种格式在使用中供选择。要根据判断的结构来执行不同的语句时,使用if 语句是一个很好的选择,它会准确地检测判断条件成立与否,再决定是否要执行后面的语句。
if语句的格式如下。
if (判断条件) { 语句1 … 语句n }
若是在if语句主体中要处理的语句只有1个,可省略左、右大括号。但是不建议读者省略,因为这样更易于阅读和不易出错。当判断条件的值不为假时,就会逐一执行大括号里面所包含的语句。if语句的流程如下图所示。
如果表达式的值为真,则执行if 语句中语句块1;否则将执行整个if 语句下面的其他语句。if 语句中的语句可以是一条语句,也可以是复合语句。由于6.1.7小节已列举有例子了,所以这里不再赘述。
7.2.2 if…else语句
if...else语句(if-then-else Statement)是根据判断条件是否成立来执行的。如下图所示,如果条件表达式的值为真,则执行if中的语句块1,判断条件不成立时,则会执行else中的语句块2,然后继续执行整个if语句后面的语句。语句体1和语句体2可以是一条语句,也可以是复合语句。if…else语句的格式如下。
if (条件表达式) { 语句块1 } else { 语句块2 }
若在if语句体或else语句体中要处理的语句只有一个,可以将左、右大括号去除。但是建议读者养成良好的编程习惯,不管if语句或else语句体中有几条语句都加左、右大括号。
if…else语句的流程如下所示。
下面举一个简单的例子:声明一个整型变量a,并对其赋初值5,在程序中判断a是奇数还是偶数,再将判断的结果输出。
【范例7-1】 if语句的使用(ifElseDemo7 1.java)。
01 //以下程序演示了if...else的使用方法 02 public class ifElseDemo7_1 03 { 04 public static void main(String[]args) 05 { 06 int a=5; 07 if(a%2==1||a % 2==-1) 08 { 09 System.out.println(a+"是奇数!");
10 } 11 else 12 { 13 System.out.println(a+"是偶数!"); 14 } 15 } 16 }
【运行结果】
程序运行结果如下所示。
【代码详解】
第07~14行为if…else语句。在第07行中,if的判断条件为a%2 == 1 || a % 2 == -1 ,当a除以2取余数,若得到的结果为1或-1,表示a为奇数,若a除以2取余数得到的结果为0,a则为偶数。
当a除以2取余数的结果为1或-1时,即执行第09行的语句,输出“5是奇数!”;否则执行第13行,输出“5是偶数!”。
读者可以自行更改变量a的初值,再重复执行程序。
从上面的程序中可以发现,程序的正确的缩进在这种选择结构中起着非常重要的作用,它可以使设计者编写的程序结构层次清晰,在维护上也就比较简单。建议读者以后在编写程序时要养成缩进的好习惯。
7.2.3 if…else if…else语句
由于if语句体或else语句体可以是多条语句,所以如果需要在if..else里判断多个条件,可以“随意”嵌套。比较常用的是if…else if … else语句,其流程如下所示。
其格式如下所示。
if (条件判断1) { 语句块1 } else if (条件判断2) { 语句块 2 } …. //多个else if()语句 else { 语句块n }
这种方式用在含有多个判断条件的程序中,请看下面的范例。
【范例7-2】 多分支条件语句的使用(multiplyIfElse7 2.java)。
01 //以下程序演示了多分支条件语句if..else if...else的使用 02 public class multiplyIfElse7_2 03 { 04 public static void main(String[]args) 05 { 06 int a=5; 07 //判断a与0的大小关系 08 if(a>0) 09 { 10 System.out.println("a>0!"); 11 } 12 else if(a<0) 13 { 14 System.out.println("a<0!"); 15 } 16 else 17 { 18 System.out.println("a==0!"); 19 } 20 } 21 }
【运行结果】
程序运行结果如下图所示。
可以看出,if … else if ..else比单纯的if..else语句含有更多的条件判断语句。可是如果有很多条件都要判断的话,这样写是一件很头疼的事情,下面介绍的多重选择语句就可以解决这一问题。
7.2.4 多重选择──switch语句
虽然嵌套的if语句可以实现多重选择处理,但语句较为复杂,并且容易将if与else配对错误,从而造成逻辑混乱。在这种情况下,可使用switch语句来实现多重选择情况的处理。switch结构称为“多路选择结构”,switch语句也叫开关语句,在许多不同的语句组之间作出选择。
switch语句的格式如下,其中default语句和break语句并不是必须的。
switch (表达式) { case 常量选择值1 : 语句体 1 {break ;} case 常量选择值2 : 语句体 2 { break;} ……. case 常量选择值n : 语句体 n { break ;} default: 默认语句体 { break;} }
提示
switch的表达式类型为整型(包括byte、short、char、int等)、字符类型及枚举类型。
在JDK 1.7之后,switch语句增加了对String类型的支持。我们会在后续的章节中讲到它们的具体应用。case(情况)后的常量选择值要和表达式的数据类型一致,并且不能重复。break语句用于转换程序的流程,在switch结构中使用break语句可以使程序立即退出该结构,转而执行该结构后面的第1条语句。
接下来看看switch语句执行的流程。
⑴ switch语句先计算括号中表达式的结果。
⑵ 根据表达式的值检测是否符合执行case后面的选择值,若是所有case的选择值皆不符合,则执行default后面的语句,执行完毕即离开switch语句。
⑶ 如果某个case的选择值符合表达式的结果,就会执行该case所包含的语句,直到遇到break语句后才离开switch语句。
⑷ 若是没有在case语句结尾处加上break语句,则会一直执行到switch语句的尾端才会离开switch语句。break语句在下面的章节中会介绍,读者只要先记住break是跳出语句就可以了。
⑸ 若是没有定义default该执行的语句,则什么也不会执行,而是直接离开switch语句。
根据上面的描述,可以绘制出如下图所示的switch语句流程。
下面的程序是一个简单的赋值表达式,利用switch语句处理此表达式中的运算符,再输出运算后的结果。
【范例7-3】 多分支条件语句的使用(switchDemo7 3.java)。
01 //以下程序演示了多分支条件语句的使用 02 public class switchDemo7_3 03 { 04 public static void main(String[]args) 05 { 06 int a=100; 07 int b=7; 08 char oper='*'; 09 switch(oper) //用switch实现多分支语句 10 { 11 case'+': 12 System.out.println(a+"+"+b+"="+(a+b)); 13 break; 14 case'-': 15 System.out.println(a+"-"+b+"="+(a-b)); 16 break; 17 case'*': 18 System.out.println(a+"*"+b+"="+(a*b)); 19 break; 20 case'/': 21 System.out.println(a+"/"+b+"="+((float)a/b)); 22 break; 23 default:
24 System.out.println("未知的操作!"); 25 break; 26 } 27 } 28 }
【运行结果】
程序运行结果如下图所示。
【代码详解】
第8行,利用变量存放一个运算符号。
第09~26行为switch语句。当oper为字符+、-、*、/时,输出运算的结果后离开switch语句;若所输入的运算符皆不是这些,即执行default所包含的语句,输出“未知的操作!”,再离开switch。
选择值为字符时,必须用单引号将字符包围起来。
可以试着把程序中break语句删除,再运行,看看结果是什么?改变一下oper呢?想一想为什么会这样?关于break语句7.4.1小节会有详细讲解。
7.3 循环结构
本节视频教学录像:36分钟
循环结构是程序中的另一种重要结构。它和顺序结构、选择结构共同作为各种复杂程序的基本构造部件。循环结构的特点是在给定条件成立时,反复执行某个程序段。通常我们称给定条件为循环条件,称反复执行的程序段为循环体。循环体可以是复合语句、单个语句或空语句。
循环结构包括while循环、do…while循环、for循环,还可以使用嵌套循环完成复杂的程序控制操作。
7.3.1 while循环
while循环语句的执行过程是先计算表达式的值,若表达式的值为真,则执行循环体中的语句,继续循环;否则退出该循环,执行while语句后面的语句。循环体可以是一条语句或空语句,也可以是复合语句。while循环的格式如下。
while (判断条件) {
语句1 ; 语句2 ; … 语句n }
当while循环主体有且只有一个语句时,可以将大括号去掉,但同样不建议这样做。在while循环语句中,只有一个判断条件,它可以是任何逻辑表达式。在这里需要注意的是,while中的判断条件必须是布尔类型值(不同于C/C++可以是有关整型数运算的表达式)。下面列出了while循环执行的流程。
⑴ 第1次进入while循环前,必须先对循环控制变量(或表达式)赋起始值。
⑵ 根据判断条件的内容决定是否要继续执行循环,如果条件判断值为真(true),则继续执行循环主体。
⑶ 条件判断值为假(false),则跳出循环执行其他语句。
⑷ 重新对循环控制变量(或表达式)赋值(增加或减少)。由于while循环不会自动更改循环控制变量(或表达式)的内容,所以在while循环中对循环控制变量赋值的工作要由设计者自己来做,完成后再回到步骤2重新判断是否继续执行循环。
while循环流程如下图所示。
下面这个范例是循环计算1累加至10。
【范例7-4】 while循环的使用(whileDemo7 4.java)。
01 //以下程序演示了while循环的使用方法 02 public class whileDemo7_4 03 { 04 public static void main(String[]args) 05 { 06 int i=1; 07 int sum=0; 08 09 while(i<11)
10 { 11 sum+=i; //累加计算 12 ++i; 13 } 14 15 System.out.println("1+2+...+10="+sum); //输出结果 16 } 17 }
【运行结果】
程序运行结果如下图所示。
【代码详解】
在第6行中,将循环控制变量i的值赋值为1。
第9行进入while循环的判断条件为i<11。第1次进入循环时,由于i的值为1,所以判断条件的值为真,即进入循环主体。
第10~13行为循环主体,sum+i后再指定给sum存放,i的值加1,再回到循环起始处,继续判断i的值是否仍在所限定的范围内,直到i大于10即跳出循环,表示累加的操作已经完成,最后再将sum的值输出即可。
7.3.2 do…while循环
上一小节介绍的while循环又称为“当型循环”,即当条件成立时才执行循环体,该小节介绍与“当型循环”不同的“直到型循环”,即先“直到”循环体(执行循环体),再判断条件是否成立,所以“直到型循环”至少会执行一次循环体。该循环由其关键字又称为do…while循环。
do{ 语句1 ; 语句2 ; …. 语句n ; }while (判断条件);
do...while循环的执行过程是先执行一次循环体,然后判断表达式的值,如果是真,则再执行循环体,继续循环;否则退出循环,执行下面的语句。循环体可以是单条语句或是复合语句,在语法上它也可以是空语句,但此时循环没有什么实际意义。
下面列出do…while循环执行的流程。
2 在进入do...while循环前,要先对循环控制变量(或表达式)赋起始值。
⑵ 直接执行循环主体,循环主体执行完毕,才开始根据判断条件的内容决定是否继续执行循环。条件判断值为真(true)时,继续执行循环主体;条件判断值为假(false)时,则跳出循环,执行其他语句。
⑶ 执行完循环主体内的语句后,重新对循环控制变量(或表达式)赋值(增加或减少)。由于do…while循环和while循环一样,不会自动更改循环控制变量(或表达式)的内容,所以在do…while循环中赋值循环控制变量的工作要由自己来做,再回到步骤2重新判断是否继续执行循环。
do...while循环流程如下图所示。
把whileDemo7_4.java(1+2+…+10)的程序稍加修改,用do…while循环重新改写,就是下面的范例TestJava3_29.java。
【范例7-5】 do…while循环语句的使用(doWhileDemo7 5.java)。
01 //演示do...while循环的用法 02 public class doWhileDemo7_5 03 { 04 public static void main(String[]args) 05 { 06 int i=1; 07 int sum=0; 08 //do.while是先执行一次,再进行判断,即循环体至少会被执行一次 09 do 10 { 11 sum+=i; //累加计算 12 ++i; 13 }while(i<=10); 14 System.out.println("1+2+...+10="+sum); //输出结果 15 }
16 }
【运行结果】
程序运行结果如下图所示。
首先,声明程序中要使用的变量i(循环记数及累加操作数)及sum(累加的总和),并将sum设初值为0;由于要计算1+2+…+10,因此在第1次进入循环的时候,将i的值设为1,接着判断i是否小于11,如果i小于11,则计算sum+i的值后再指定给sum存放。i的值已经不满足循环条件时,即会跳出循环,表示累加的操作已经完成,再输出sum的值,程序即结束运行。
【代码详解】
第9~13行利用do…while循环计算1~10的数累加。
第14行输出1~10的数的累加结果:1 + 2 + ...+ 10 = 55。
do...while循环不管条件是什么,都是先做再说,因此循环的主体最少会被执行一次。在日常生活中,如果能够多加注意,并不难找到do…while循环的影子。例如,在利用提款机提款前,会先进入输入密码的画面,让使用者输入3次密码,如果皆输入错误,即会将银行卡吞掉,其程序的流程就是利用do…while循环设计而成的。
7.3.3 for循环
在for循环中,赋初始值语句、判断条件语句、增减标志量语句均可有可无。循环体可以是一条语句或空语句,也可以是复合语句。其语句格式如下。
for ( 赋初始值;判断条件;增减标志量 ) { 语句1 ; …. 语句n ; }
若是在循环主体中要处理的语句只有1个,可以将大括号去掉,但是不建议省略。下面列出for循环的流程。
⑴ 第1次进入for循环时,对循环控制变量赋起始值。
⑵ 根据判断条件的内容检查是否要继续执行循环,当判断条件值为真(true)时,继续执行循环主体内的语句;判断条件值为假(false)时,则会跳出循环,执行其他语句。
⑶ 执行完循环主体内的语句后,循环控制变量会根据增减量的要求,更改循环控制变量的值,再回到步骤2重新判断是否继续执行循环。
for循环流程如下图所示。
通过下面的范例,利用for循环来完成由1至10的数的累加运算,帮助读者熟悉for循环的使用方法。
【范例7-6】 for循环的使用(forDemo7 6.java)。
01 //演示for循环的用法 02 public class forDemo7_6 03 { 04 public static void main(String[]args) 05 { 06 int i=0; 07 int sum=0; 08 //用来计算数字累加之和 09 for(i=1;i<11;i++) 10 { 11 sum+=i; //计算sum=sum+i 12 } 13 System.out.println("1+2+...+10="+sum); 14 } 15 }
【运行结果】
程序运行结果如下图所示。
【代码详解】
第6、7行声明两个变量sum和i,i用于循环的记数控制。
第9~12行做1~10之间的循环累加,执行的结果如上图所示。如果读者不明白的话,可以和范例7-4的程序说明比较一下,相信就可以明白for的用法了。
7.3.4 foreach循环
很多时候,从头到尾遍历操作一个数组(array)、集合框架(collections)等中所有元素,是很常见的需求。有关数组和集合的知识点,我们将会在第8章和第20章分别详细描述,在这里,读者仅需知道它们是承载数据的“容器”即可。
假设,我们定义了一个整型数组numArray。
int[] numArray = { 1, 2, 3, 4, 5, 6 };
如果我们要输出数组中的全部6个元素(即遍历输出),利用7.3.3小节的知识,通常的做法是。
for (int element; element < numArray.length; element ++) { }
System.out.print(numArray[element]);
其中numArray.length是读取数组的长度。上面的写法并没有错,只不过索引信息(数组下标)基本上是不需要的,硬要写上去,虽然语法正确,但是形式繁琐。
在SDK5以后,Java提供了for语句的特殊简化版本foreach语句块(有时也称为增强的for循环)。foreach语句为遍历诸如数组、集合框架等内的元素提供了很大便利。foreach并不是一个关键字,仅是在习惯上将这种特殊的for语句格式称之为“foreach”语句。从英文字面意思理解foreach也就是“为(for)每一个(each)”的意思,其本身的含义都有“遍历”元素的意思。
foreach的语句格式。
for(元素类型type 元素变量var : 遍历对象obj) { 引用了var的Java语句; }
有了上面的认知,在引进foreach语法后,如果要输出数组中的全部6个元素,上面的例子可改写如下。
for (int element: numArray) { }
System.out.print(element);
对比前后两个语句块可以发现,使用foreach遍历数组元素,其形式更加简洁明了。
所有foreach均可用传统的for循环模式代替。由于foreach循环会丢失元素的下标信息,当遍历集合或数组时,如果需要集合或数组元素的下标,推荐使用传统for循环方式。
7.3.5 循环嵌套
当循环语句中又出现循环语句时,就称为循环嵌套。如嵌套for循环、嵌套while循环等。当然读者也可以使用混合嵌套循环,也就是循环中又有其他不同种类的循环。
下面以打印九九乘法表为例,练习嵌套循环的用法。
【范例7-7】 for循环嵌套的使用(forDemo7 6 2.java)。
01 //演示for循环的嵌套使用方法 02 public class forDemo7_6_2 03 { 04 public static void main(String[]args) 05 { 06 //共9行 07 for(int i=1;i<10;++i) 08 { //输出i*1到i*i的乘积 09 for(int j=1;j<=i;++j) 10 { 11 System.out.print(i+"*"+j+"="+(i*j)+"\t"); 12 } 13 System.out.print("\n"); 14 } 15 } 16 }
【运行结果】
程序运行结果如下图所示。
【代码详解】
i为外层循环的循环控制变量,j为内层循环的循环控制变量。
当i为1时,符合外层for循环的判断条件(i<10),进入另一个内层for循环主体;由于是第1次进入内层循环,所以j的初值为1,符合内层for循环的判断条件(j<=i),进入循环主体,输出i*j的值(1*1=1),j再加1等于2,不再符合内层for循环的判断条件(j<=i),离开内层for循环,回到外层循环。此时,i会加1成为2,符合外层for循环的判断条件,继续执行内层for循环主体,直到i的值大于9时即离开嵌套循环。
整个程序到底执行了几次循环?可以看到,当i为1时,内层循环会执行1次(1),当i为2时,内层循环也会执行2次(1~2),依此类推,这个程序会执行45次循环(即1+2+3+4+…+9=45),而显示器上也正好输出45个式子。
7.4 循环的跳转
本节视频教学录像:23分钟
在Java语言中,有一些跳转的语句,如break、continue以及return等语句。break语句、continue语句和return语句都是用来控制程序的流程转向的,适当和灵活地使用它们可以更方便或更简洁地进行程序的设计。
7.4.1 break语句
不知读者是否还记得switch语句中的break语句?其实break语句不仅可以用在switch语句中,在while、for、do-while等循环语句结构中的循环体或语句组中也可以使用break语句,其作用是使程序立即退出该结构,转而执行该结构下面的第1条语句。break语句也称之为中断语句,它通常用来在适当的时候退出某个循环,或终止某个case并跳出switch结构。例如下面的for循环,在循环主体中有break语句时,当程序执行到break,即会离开循环主体,而继续执行循环外层的语句。
for ( 赋初始值;判断条件;增减标志量 ) { 语句1 ; 语句2 ; … break ; … //若执行break语句,则此块内的语句将不会被执行 语句n ; }
break语句有两种用法,最常见的就是不带标签的break语句。另外一种情况就是带标签的break语句,它可以协助跳出循环体,接着运行指定位置语句。下面分别给予介绍。
1. 不带标签的break
以下面的程序为例,利用for循环输出循环变量i的值,当i除以3所取的余数为0时,即使用break语句的跳离循环,并于程序结束前输出循环变量i的最终值。
【范例7-8】 break语句的使用(breakDemo7 8.java)。
01 //演示不带标签的break语句的用法 02 public class breakDemo7_8 03 { 04 public static void main(String[]args)
05 { 06 int i=0; 07 //预计循环9次 08 for(i=1;i<10;++i) 09 { 10 if(i%3==0) 11 break; //当i%3==0时跳出循环体。注意此处通常不使用大括号 12 13 System.out.println("i="+i); 14 } 15 System.out.println("循环中断:i="+i); 16 } 17 }
【运行结果】
程序运行结果如下图所示。
【代码详解】
第9~14行为循环主体,i为循环的控制变量。
当i%3为0时,符合if的条件判断,即执行第11行的break语句,跳离整个for循环。此例中,当i的值为3时,3%3的余数为0,符合if的条件判断,离开for循环,执行第14行:输出循环结束时循环控制变量i的值3。
通常设计者都会设定一个条件,当条件成立时,不再继续执行循环主体。所以在循环中出现break语句时,if语句通常也会同时出现。
另外,或许读者会问为什么第10行的if语句没有用大括号包起来,不是要养成良好的编程风格吗?其实习惯上如果if语句里只含有一条类似于break语句、continue语句或return语句的跳转语句,我们通常会省略if语句的大括号。
此时,读者应该知道前面提到过的switch语句中break语句的作用了吧。想想会不会在某些情况下,有意去掉几个特定位置的break会不会带来一些编程的方便?
2. 带标签的break
不带标签的break只能跳出包围它的最小代码块,如果想跳出包围它的更外层的代码块,可以使用带标签的break语句。
带标签的break语句格式如下。
break 标签名;
当这种形式的break执行时,控制被传递出指定的代码块。标签不需要直接的包围break块,因此可以使用一个加标签的break语句退出一系列的嵌套块。要为一个代码块添加标签,只需要在该语句块的前面加上 “ 标签名: ” 格式代码即可。标签名可以是任何合法有效的Java标识符。给一个块加上标签后,就可以使用这个标签作为break语句的对象。
【范例7-9】 带标签的break语句的使用(breakLabelDemo7 9.java)。
01 //演示带标签break语句的用法 02 public class breakLabelDemo7_9 03 { 04 public static void main(String[]args) 05 { 06 for(int i=0;i<2;i++)//最外层for循环 07 { 08 System.out.println("最外层循环"+i); 09 loop: //中间层for循环标签 10 for(int j=0;j<2;j++) //中间层for循环 11 { 12 System.out.println("中间层循环"+j); 13 for(int k=0;k<2;k++)//最内层for循环 14 { 15 System.out.println("最内层循环"+k); 16 break loop; //跳出中间层for循环 17 } 18 } 19 } 20 } 21 }
【运行结果】
程序运行结果如下图所示。
【代码详解】
带标签的break语句,在本质上是作为goto 语句的一种“文明”形式来使用。Java 中不支持 goto语句,因为goto 语句提供了一种改变程序运行流程的非结构化方式。这使得程序难以理解和难于维护,同时也阻止了某些编译器的优化。但是,在有些场景下,需要从嵌套很深的循环中退出时, goto 语句就很有帮助。因此,Java 定义了带标签的break来处理这种情况。
具体到本例,在代码第16行,loop就是break所要跳出的标签。如果不加break loop这个语句,该程序运行结果有14行,但是加上break loop后,程序运行结果只有6行(如上图所示)。其原因如下:当程序由最外层循环向内层循环执行并输出 “最外层循环0 中间层循环0 最内层循环0”到达“break loop”时,程序跳出中间层循环,执行最外层循环中剩余的代码—i的自增操作,通过判断i仍然符合循环条件,所以再一次从最外层循环向内层循环执行并输出“最外层循环1 中间层循环0 最内层循环0”再次到达“break loop;”语句,再次跳转到最外层循环,i再次加1,已不符合最外层循环控制条件,跳出最外层循环。结束该嵌套循环程序段。
7.4.2 continue语句
在while、do…while和for语句的循环体中,执行continue语句将结束本次循环而立即测试循环的条件,以决定是否进行下一次循环。例如下面的for循环,在循环主体中有continue语句,当程序执行到continue,会执行设增减量,然后执行判断条件……也就是说会跳过continue下面的语句。
for (初值赋值;判断条件;设增减量) { 语句1 ; 语句2 ; … continue … //若执行continue语句,则此处将不会被执行 语句n; }
类似于break语句有两种用法,continue语句也有两种用法:一种是最常见的就是不带标签的continue语句。另外一种情况就是带标签的continue语句,它可以协助跳出循环体,接着运行指定位置语句。下面分别给予介绍。
1. 不带标签的continue语句
将程序breakDemo7_8中的break语句改成continue语句,就形成了程序continueDemo7_9. java。读者可以观察一下这两种跳出语句的不同之处。break语句是跳出当前层循环,终结的是整个循环,也不再判断循环条件是否成立;相比而言,continue语句则是结束本次循环(即continue语句之后的语句不再执行),然后重新回到循环的起点,判断循环条件是否成立,如果成立,则再次进入循环体,若不成立,跳出循环。
【范例7-10】 continue语句的使用(continueDemo7 10.java)。
01 //演示不带标签的continue语句的用法 02 public class continueDemo7_10 03 { 04 public static void main(String[]args)
05 { 06 int i=0; 07 //预计循环9次 08 for(i=1;i<10;++i) 09 { 10 if(i % 3==0) //当i%3==0时跳过本次循环,直接执行下一次循环。 11 continue; 12 13 System.out.println("i="+i); 14 } 15 System.out.println("循环结束:i="+i); 16 } 17 }
【运行结果】
程序运行结果如下图所示。
【代码详解】
第9~14行为循环主体,i为循环控制变量。
当i%3为0时,符合if的条件判断,即执行第11行的continue语句,跳离目前的for循环(不再执行循环体内的其他语句),而是先执行++i,再回到i<10处判断是否执行循环。此例中,当i的值为3、6、9时,取余数为0,符合if判断条件,离开当前层的for循环,回到循环开始处继续判断是否执行循环。
当i的值为10时,不符合循环执行的条件,此时执行程序第15行,输出循环结束时循环控制变量i的值10。
当判断条件成立时,break语句与continue语句会有不同的执行方式。break语句不管情况如何,先离开循环再说;而continue语句则不再执行此次循环的剩余语句,而是直接回到循环的起始处。
2. 带标签的continue语句
continue语句和break语句一样可以和标签搭配使用,其作用也是用于跳出深度循环。其格式为。
continue 标签名;
continue后的标签,必须标识在循环语句之前,使程序的流程在遇到continue之后,立即结束当次循环,跳入标签所标识的循环层次中,进行下一轮循环。
【范例7-11】 带标签的continue语句的使用(continueLabelDemo7 11. java)。
01 //演示带标签的continue语句的用法 02 public class ContinueLabelDemo7_11 03 { 04 05 public static void main(String[]args) 06 { 07 for(int i=0;i<2;i++) 08 { 09 System.out.println("最外层循环"+i); 10 loop: 11 for(int j=0;j<2;j++) 12 { 13 System.out.println("中间层循环"+j); 14 for(int k=0;k<2;k++) 15 { 16 System.out.println("最内层循环"+k); 17 continue loop; //进入中层循环的下一次循环 18 } 19 } 20 } 21 } 22 23 }
【运行结果】
程序运行结果如下图所示。
【代码详解】
在程序第17行,continue语句带有标签loop,程序的输出结果有10行之多(见上图)。假设将continue语句后的标签loop删除,整个程序的输出结果为14行。原因在于:从最外层for循环向内层for循环执行并先后输出“最外层循环0 中间层循环0 最内层循环0 ”(这里的“”表示回车换行),到达“continue loop;”然后程序跳出最内层for循环至标签处(第10行),然后在中间层for循环体内执行j自加操作,判断j仍然符合循环条件,从而从中间层for循环向内层执行并输出“中间层循环1 最内层循环0 ”,再次执行到“continue loop:”,然后程序再次从最内层for循环跳至标签处(第10行),再次进入中间层for循环,执行j自加操作,此时j已不符合循环条件,跳出中间层循环,进入最外层循环,在i=1时重复以上操作,从而输出10行。
7.4.3 return语句
return语句可以使程序的流程离开return语句所在的方法,到目前为止我们所写的程序都只有一个main方法,所以读者目前可以简单的认为return语句就是使程序结束的语句。到第10章《重复调用的代码块——方法》章节,我们会重新介绍return语句更常用的用法。
return语句的语法为。
return 返回值;
其中返回值根据方法的定义的不同以及我们的需求不同而不同,目前我们的程序使用return语句的形式为。
return;
将程序breakDemo7_8中的break语句改成return语句,就成了下面的范例7-12。
【范例7-12】 break语句的使用(returnDemo7 12.java)。
01 //演示return语句的用法 02 public class returnDemo7_12 03 { 04 public static void main(String[]args) 05 { 06 int i=0; 07 //预计循环9次 08 for(i=1;i<10;++i) 09 { 10 if(i%3==0) 11 return; //当i%3==0时结束程序 12 System.out.println("i="+i); 13 } 14 System.out.println("循环结束:i="+i); 15 } 16 }
【运行结果】
程序运行结果如下图所示。
【代码详解】
程序的大体构架与前面一样,这里不再赘述。需要读者注意的是,运行结果并没有输出 "循环结束:i = 10" 之类的字样,是不是Eclipse出问题了?当然不是。return语句的作用是结束本方法,对于这个程序而言相当于结束程序,所以当执行return语句之后程序就结束了,自然无法输出那串字符串了。
7.5 高手点拨
本节视频教学录像:22分钟
1. 三元运算符与if…else语句的关系
或许读者已经看出来了,三元运算符就相当于if…else语句,只不过三元运算符有返回值。不过还是得提醒读者,为了程序的清晰明了,只有在if…else语句的主体部分很少时才使用三元运算符。
2. switch中并不是每个case后都需要break语句
在某些情况下,在switch结构体中,可以有意的减少一些的特定位置的break语句,这样可以简化程序。参见实战练习2。
3. 三种循环的关系
7.3节讲到的三种循环结构其实是可以互相转化的,通常我们只是使用其中一种结构,因为这样可以使程序结构更加清晰。例如,下面的三段代码功能是等同的,读者可根据自己的习惯取舍其中的一种方式。
⑴ 使用for 循环
for( int i=0; i<10; ++i ) { System.out.println( "i = " + i ); }
⑵ 使用while循环
int i = 0; while( i < 10 ) { System.out.println( "i = " + i ); ++i;
}
⑶ 使用do-while循环
int i = 0; do { System.out.println( "i = " + i ); ++i; }while( i < 10 )
4. 循环的区间控制
在习惯上,我们在循环中通常使用半开区,即从第一个元素开始,到最后一个元素的下一个位置之前。同样是循环十次,我们推荐使用
for( int i = 0; i < 10; ++i )
而非
for( int i =0; i <= 9; ++i )
来循环十次。前者更具有可读性,而后者也能达到相同功能,读者可根据自己的代码风格进行取舍。
7.6 实战练习
本节视频教学录像:19分钟
1. 编写程序,使用循环控制语句计算“1+2+3+…+100”的值。
2. 编写程序,使用程序产生1-12之间的某个整数(包括1和12),然后输出相应月份的天数(2月按28天算)。运行结果如下图所。
3. 编写程序,判断某一年是否是闰年。