Java从入门到精通(第2版)
上QQ阅读APP看书,第一时间看更新

第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. 编写程序,判断某一年是否是闰年。