程序逻辑
一般来说程序的结构包含以下3种。
⑴ 顺序结构。
⑵ 选择结构。
⑶ 循环结构。
这3种不同的结构有一个共同点,就是它们都只有一个入口,也只有一个运行出口。程序中使用了上面这些结构到底有什么好处呢?这些单一的入口、出口可让程序可控、易读、好维护。
顺序结构
结构化程序的最简单的结构就是顺序结构。所谓顺序结构程序就是按书写顺序执行的语句构成的程序段,其流程如下图a所示。
通常情况下,顺序结构是指按照程序语句出现的先后顺序一句一句地执行。在我之前的文章里,大多数都属于顺序结构程序。有一些程序并不按顺序执行语句,这个过程称为“控制的转移”,它涉及了另外两类程序的控制结构,即分支结构和循环结构。
分支结构
选择结构也称为分支结构,在许多实际问题的程序设计中,根据输入数据和中间结果的不同,需要选择不同的语句组执行。在这种情况下,必须根据某个变量或表达式的值作出判断,以决定执行哪些语句和不执行哪些语句,其流程如下图b所示。
选择结构是根据给定的条件进行判断,决定执行某个分支的程序段。条件分支不是我们常说的“兵分两路”,“兵分两路”是两条路都有“兵”,而这里的条件分支,在执行时“非此即彼”,不可兼得,其主要用于两个分支的选择,由if 语句和if … else 语句来实现。if语句在之前已经介绍过了。下面介绍一下if…else语句。
if…else语句可以依据判断条件的结果,来决定要执行的语句。当判断条件的值为真时,就运行“语句1”;当判断条件的值为假时,则执行“语句2”。不论执行哪一个语句,最后都会再回到“语句3”继续执行。
循环结构
循环结构是程序中的另一种重要结构,它和顺序结构、选择结构共同作为各种复杂程序的基本构造部件。循环结构的特点是在给定条件成立时,反复执行某个程序段。通常我们称给定条件为循环条件,称反复执行的程序段为循环体。循环体可以是复合语句、单个语句或空语句。在循环体中也可以包含循环语句,实现循环的嵌套。循环结构的流程如下图所示。
选择结构
Java语言中的选择结构提供了以下两种类型的分支结构。
条件分支:根据给定的条件进行判断,决定执行某个分支的程序段。
开关分支:根据给定整型表达式的值进行判断,然后决定执行多路分支中的一支。
件分支主要用于两个分支的选择,由if 语句和if…else 语句来实现。开关分支用于多个分支的选择,由switch 语句来实现。在语句中加上了选择结构之后,就像是十字路口,根据不同的选择,程序的运行会有不同的结果。
if语句
if 语句(if-then Statement)用于实现条件分支结构,它在可选动作中作出选择,执行某个分支的程序段。if 语句有两种格式在使用中供选择。要根据判断的结构来执行不同的语句时,使用if 语句是一个很好的选择,它会准确地检测判断条件成立与否,再决定是否要执行后面的语句。
if语句的格式如下。
若是在if语句主体中要处理的语句只有1个,可省略左、右大括号。但是不建议读者省略,因为这样更易于阅读和不易出错。当判断条件的值不为假时,就会逐一执行大括号里面所包含的语句。if语句的流程如下图所示。
如果表达式的值为真,则执行if 语句中语句块1;否则将执行整个if 语句下面的其他语句。if 语句中的语句可以是一条语句,也可以是复合语句。
if…else语句
if…else语句(if-then-else Statement)是根据判断条件是否成立来执行的。如下图所示,如果条件表达式的值为真,则执行if中的语句块1,判断条件不成立时,则会执行else中的语句块2,然后继续执行整个if语句后面的语句。语句体1和语句体2可以是一条语句,也可以是复合语句。if…else语句的格式如下。
若在if语句体或else语句体中要处理的语句只有一个,可以将左、右大括号去除。但是建议读者养成良好的编程习惯,不管if语句或else语句体中有几条语句都加左、右大括号。
if…else语句的流程如下所示。
if语句的使用(ifElseDemo7 1.java)
第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是偶数!”。
从上面的程序中可以发现,程序的正确的缩进在这种选择结构中起着非常重要的作用,它可以使设计者编写的程序结构层次清晰,在维护上也就比较简单。建议以后在编写程序时要养成缩进的好习惯。
if…else if…else语句
由于if语句体或else语句体可以是多条语句,所以如果需要在if…else里判断多个条件,可以“随意”嵌套。比较常用的是if…else if … else语句,其流程如下所示。
其格式如下所示。
多分支条件语句的使用(multiplyIfElse7 2.java)
可以看出,if … else if …else比单纯的if…else语句含有更多的条件判断语句。可是如果有很多条件都要判断的话,这样写是一件很头疼的事情,下面介绍的多重选择语句就可以解决这一问题。
多重选择──switch语句
虽然嵌套的if语句可以实现多重选择处理,但语句较为复杂,并且容易将if与else配对错误,从而造成逻辑混乱。在这种情况下,可使用switch语句来实现多重选择情况的处理。switch结构称为“多路选择结构”,switch语句也叫开关语句,在许多不同的语句组之间作出选择。
switch语句的格式如下,其中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语句流程。
多分支条件语句的使用(switchDemo7 3.java)
第8行,利用变量存放一个运算符号。
第09~26行为switch语句。当oper为字符+、-、*、/时,输出运算的结果后离开switch语句;若所输入的运算符皆不是这些,即执行default所包含的语句,输出“未知的操作!”,再离开switch。
选择值为字符时,必须用单引号将字符包围起来。
循环结构
循环结构是程序中的另一种重要结构。它和顺序结构、选择结构共同作为各种复杂程序的基本构造部件。循环结构的特点是在给定条件成立时,反复执行某个程序段。通常我们称给定条件为循环条件,称反复执行的程序段为循环体。循环体可以是复合语句、单个语句或空语句。
循环结构包括while循环、do…while循环、for循环,还可以使用嵌套循环完成复杂的程序控制操作。
while循环
while循环语句的执行过程是先计算表达式的值,若表达式的值为真,则执行循环体中的语句,继续循环;否则退出该循环,执行while语句后面的语句。循环体可以是一条语句或空语句,也可以是复合语句。while循环的格式如下。
当while循环主体有且只有一个语句时,可以将大括号去掉,但同样不建议这样做。在while循环语句中,只有一个判断条件,它可以是任何逻辑表达式。在这里需要注意的是,while中的判断条件必须是布尔类型值(不同于C/C++可以是有关整型数运算的表达式)。下面列出了while循环执行的流程。
⑴ 第1次进入while循环前,必须先对循环控制变量(或表达式)赋起始值。
⑵ 根据判断条件的内容决定是否要继续执行循环,如果条件判断值为真(true),则继续执行循环主体。
⑶ 条件判断值为假(false),则跳出循环执行其他语句。
⑷ 重新对循环控制变量(或表达式)赋值(增加或减少)。由于while循环不会自动更改循环控制变量(或表达式)的内容,所以在while循环中对循环控制变量赋值的工作要由设计者自己来做,完成后再回到步骤2重新判断是否继续执行循环。
while循环流程如下图所示。
while循环的使用(whileDemo7 4.java)
在第6行中,将循环控制变量i的值赋值为1。
第9行进入while循环的判断条件为i<11。第1次进入循环时,由于i的值为1,所以判断条件的值为真,即进入循环主体。
10~13行为循环主体,sum+i后再指定给sum存放,i的值加1,再回到循环起始处,继续判断i的值是否仍在所限定的范围内,直到i大于10即跳出循环,表示累加的操作已经完成,最后再将sum的值输出即可。
do…while循环
while循环又称为“当型循环”,即当条件成立时才执行循环体,该小节介绍与“当型循环”不同的“直到型循环”,即先“直到”循环体(执行循环体),再判断条件是否成立,所以“直到型循环”至少会执行一次循环体。该循环由其关键字又称为do…while循环。
do…while循环的执行过程是先执行一次循环体,然后判断表达式的值,如果是真,则再执行循环体,继续循环;否则退出循环,执行下面的语句。循环体可以是单条语句或是复合语句,在语法上它也可以是空语句,但此时循环没有什么实际意义。
下面列出do…while循环执行的流程。
(1)在进入do…while循环前,要先对循环控制变量(或表达式)赋起始值。
⑵ 直接执行循环主体,循环主体执行完毕,才开始根据判断条件的内容决定是否继续执行循环。条件判断值为真(true)时,继续执行循环主体;条件判断值为假(false)时,则跳出循环,执行其他语句。
⑶ 执行完循环主体内的语句后,重新对循环控制变量(或表达式)赋值(增加或减少)。由于do…while循环和while循环一样,不会自动更改循环控制变量(或表达式)的内容,所以在do…while循环中赋值循环控制变量的工作要由自己来做,再回到步骤2重新判断是否继续执行循环。
do…while循环流程如下图所示。
do…while循环语句的使用(doWhileDemo7 5.java)
首先,声明程序中要使用的变量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循环设计而成的。
for循环
在for循环中,赋初始值语句、判断条件语句、增减标志量语句均可有可无。循环体可以是一条语句或空语句,也可以是复合语句。其语句格式如下。
若是在循环主体中要处理的语句只有1个,可以将大括号去掉,但是不建议省略。下面列出for循环的流程。
⑴ 第1次进入for循环时,对循环控制变量赋起始值。
⑵ 根据判断条件的内容检查是否要继续执行循环,当判断条件值为真(true)时,继续执行循环主体内的语句;判断条件值为假(false)时,则会跳出循环,执行其他语句。
⑶ 执行完循环主体内的语句后,循环控制变量会根据增减量的要求,更改循环控制变量的值,再回到步骤2重新判断是否继续执行循环。
for循环流程如下图所示。
for循环的使用(forDemo7 6.java)
第6、7行声明两个变量sum和i,i用于循环的记数控制。
第9~12行做1~10之间的循环累加,执行的结果如上图所示。
foreach循环
很多时候,从头到尾遍历操作一个数组(array)、集合框架(collections)等中所有元素,是很常见的需求。有关数组和集合的知识点,我会在后期的文章中分别详细描述,在这里,大家仅需知道它们是承载数据的“容器”即可。
假设,我们定义了一个整型数组numArray。
如果我们要输出数组中的全部6个元素(即遍历输出),利用之前的知识,通常的做法是。
其中numArray.length是读取数组的长度。上面的写法并没有错,只不过索引信息(数组下标)基本上是不需要的,硬要写上去,虽然语法正确,但是形式繁琐。
在SDK5以后,Java提供了for语句的特殊简化版本foreach语句块(有时也称为增强的for循环)。foreach语句为遍历诸如数组、集合框架等内的元素提供了很大便利。foreach并不是一个关键字,仅是在习惯上将这种特殊的for语句格式称之为“foreach”语句。从英文字面意思理解foreach也就是“为(for)每一个(each)”的意思,其本身的含义都有“遍历”元素的意思。
foreach的语句格式。
有了上面的认知,在引进foreach语法后,如果要输出数组中的全部6个元素,上面的例子可改写如下。
对比前后两个语句块可以发现,使用foreach遍历数组元素,其形式更加简洁明了。
所有foreach均可用传统的for循环模式代替。由于foreach循环会丢失元素的下标信息,当遍历集合或数组时,如果需要集合或数组元素的下标,推荐使用传统for循环方式。
循环嵌套
当循环语句中又出现循环语句时,就称为循环嵌套。如嵌套for循环、嵌套while循环等。当然读者也可以使用混合嵌套循环,也就是循环中又有其他不同种类的循环。
下面以打印九九乘法表为例,练习嵌套循环的用法。
for循环嵌套的使用(forDemo7 6 2.java)
i为外层循环的循环控制变量,j为内层循环的循环控制变量。
当i为1时,符合外层for循环的判断条件(i<10),进入另一个内层for循环主体;由于是第1次进入内层循环,所以j的初值为1,符合内层for循环的判断条件(j<=i),进入循环主体,输出ij的值(11=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个式子。
循环的跳转
在Java语言中,有一些跳转的语句,如break、continue以及return等语句。break语句、continue语句和return语句都是用来控制程序的流程转向的,适当和灵活地使用它们可以更方便或更简洁地进行程序的设计。
break语句
其实break语句不仅可以用在switch语句中,在while、for、do-while等循环语句结构中的循环体或语句组中也可以使用break语句,其作用是使程序立即退出该结构,转而执行该结构下面的第1条语句。break语句也称之为中断语句,它通常用来在适当的时候退出某个循环,或终止某个case并跳出switch结构。例如下面的for循环,在循环主体中有break语句时,当程序执行到break,即会离开循环主体,而继续执行循环外层的语句。
break语句有两种用法,最常见的就是不带标签的break语句。另外一种情况就是带标签的break语句,它可以协助跳出循环体,接着运行指定位置语句。下面分别给予介绍。
1. 不带标签的break
break语句的使用(breakDemo7 8.java)
第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语句的大括号。
2. 带标签的break
不带标签的break只能跳出包围它的最小代码块,如果想跳出包围它的更外层的代码块,可以使用带标签的break语句。
带标签的break语句格式如下。
当这种形式的break执行时,控制被传递出指定的代码块。标签不需要直接的包围break块,因此可以使用一个加标签的break语句退出一系列的嵌套块。要为一个代码块添加标签,只需要在该语句块的前面加上 “ 标签名: ” 格式代码即可。标签名可以是任何合法有效的Java标识符。给一个块加上标签后,就可以使用这个标签作为break语句的对象。
带标签的break语句的使用(breakLabelDemo7 9.java)
带标签的break语句,在本质上是作为goto 语句的一种“文明”形式来使用。Java中不支持 goto语句,因为goto 语句提供了一种改变程序运行流程的非结构化方式。这使得程序难以理解和难于维护,同时也阻止了某些编译器的优化。但是,在有些场景下,需要从嵌套很深的循环中退出时, goto 语句就很有帮助。因此,Java 定义了带标签的break来处理这种情况。
具体到本例,在代码第16行,loop就是break所要跳出的标签。如果不加breakloop这个语句,该程序运行结果有14行,但是加上break loop后,程序运行结果只有6行(如上图所示)。其原因如下:当程序由最外层循环向内层循环执行并输出 “最外层循环0 中间层循环0 最内层循环0”到达“break loop”时,程序跳出中间层循环,执行最外层循环中剩余的代码—i的自增操作,通过判断i仍然符合循环条件,所以再一次从最外层循环向内层循环执行并输出“最外层循环1 中间层循环0 最内层循环0”再次到达“break loop;”语句,再次跳转到最外层循环,i再次加1,已不符合最外层循环控制条件,跳出最外层循环。结束该嵌套循环程序段。
continue语句
在while、do…while和for语句的循环体中,执行continue语句将结束本次循环而立即测试循环的条件,以决定是否进行下一次循环。例如下面的for循环,在循环主体中有continue语句,当程序执行到continue,会执行设增减量,然后执行判断条件……也就是说会跳过continue下面的语句。
类似于break语句有两种用法,continue语句也有两种用法:一种是最常见的就是不带标签的continue语句。另外一种情况就是带标签的continue语句,它可以协助跳出循环体,接着运行指定位置语句。下面分别给予介绍。
1. 不带标签的continue语句
break语句是跳出当前层循环,终结的是整个循环,也不再判断循环条件是否成立;相比而言,continue语句则是结束本次循环(即continue语句之后的语句不再执行),然后重新回到循环的起点,判断循环条件是否成立,如果成立,则再次进入循环体,若不成立,跳出循环。
continue语句的使用(continueDemo7 10.java)
第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语句的使用(continueLabelDemo7 11.java)。
在程序第17行,continue语句带有标签loop,程序的输出结果有10行之多(见上图)。假设将continue语句后的标签loop删除,整个程序的输出结果为14行。原因在于:从最外层for循环向内层for循环执行并先后输出“最外层循环0[回车] 中间层循环0[回车] 最内层循环0[回车] ”,到达“continueloop;”然后程序跳出最内层for循环至标签处(第10行),然后在中间层for循环体内执行j自加操作,判断j仍然符合循环条件,从而从中间层for循环向内层执行并输出“中间层循环1[回车] 最内层循环0[回车] ”,再次执行到“continue loop:”,然后程序再次从最内层for循环跳至标签处(第10行),再次进入中间层for循环,执行j自加操作,此时j已不符合循环条件,跳出中间层循环,进入最外层循环,在i=1时重复以上操作,从而输出10行。
return语句
return语句可以使程序的流程离开return语句所在的方法,到目前为止我们所写的程序都只有一个main方法,所以目前可以简单的认为return语句就是使程序结束的语句。后面,我会重新介绍return语句更常用的用法。
return语句的语法为。
其中返回值根据方法的定义的不同以及我们的需求不同而不同,目前我们的程序使用return语句的形式为。
break语句的使用(returnDemo7 12.java)
程序的大体构架与前面一样,这里不再赘述。需要注意的是,运行结果并没有输出 “循环结束:i = 10” 之类的字样,是不是Eclipse出问题了?当然不是。return语句的作用是结束本方法,对于这个程序而言相当于结束程序,所以当执行return语句之后程序就结束了,自然无法输出那串字符串了。
1. 三元运算符与if…else语句的关系
或许大家已经看出来了,三元运算符就相当于if…else语句,只不过三元运算符有返回值。不过还是得提醒读者,为了程序的清晰明了,只有在if…else语句的主体部分很少时才使用三元运算符。
2. switch中并不是每个case后都需要break语句
在某些情况下,在switch结构体中,可以有意的减少一些的特定位置的break语句,这样可以简化程序。
3. 三种循环的关系
之前讲到的三种循环结构其实是可以互相转化的,通常我们只是使用其中一种结构,因为这样可以使程序结构更加清晰。例如,下面的三段代码功能是等同的,大家可根据自己的习惯取舍其中的一种方式。
⑴ 使用for 循环
⑵ 使用while循环
⑶ 使用do-while循环
4. 循环的区间控制
在习惯上,我们在循环中通常使用半开区,即从第一个元素开始,到最后一个元素的下一个位置之前。同样是循环十次,我们推荐使用
而非
来循环十次。前者更具有可读性,而后者也能达到相同功能,大家可根据自己的代码风格进行取舍。