3. Java 流程控制语句

在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的。所以,我们必须清楚每条语句的执行流程。而且,很多时候要通过控制语句的执行顺序来实现我们想要的功能。

控制语句分为三类:顺序、选择和循环。

  • “顺序结构”代表“先执行 a,再执行 b”的逻辑。比如,先找个女朋友,再给女朋友打电话;先订婚,再结婚;
  • “选择结构”代表“如果…,则…”的逻辑。比如,如果女朋友来电,则迅速接电话;如果看到红灯,则停车;
  • “循环结构”代表“如果…,则再继续…”的逻辑。比如,如果没打通女朋友电话,则再继续打一次; 如果没找到喜欢的人,则再继续找。

前面讲解的程序都是顺序结构,即按照书写顺序执行每一条语句,这并不是我们的重点,因此本章研究的重点是“选择结构”和“循环结构”。

很神奇的是,三种流程控制语句就能表示所有的事情!不信,你可以试试拆分你遇到的各种事情。实际上,任何软件和程序,小到一个练习,大到一个操作系统,本质上都是由“变量、选择语句、循环语句”组成。

这三种基本逻辑结构是相互支撑的,它们共同构成了算法的基本结构,无论怎样复杂的逻辑结构,都可以通过它们来表达。上述两种结构组成的程序可以解决全部的问题,所以任何一种高级语言都具备上述两种结构。

3.1 顺序结构

顺序结构是程序中最简单最基本的流程控制,没有特定的语法结构,按照代码的先后顺序,依次执行,程序中大多数的代码都是这样执行的。

顺序结构执行流程图:

3.2 选择结构

在还没有知道 Java 选择结构的时候,我们编写的程序总是从程序入口开始,顺序执行每一条语句直到执行完最后一条语句结束,但是生活中经常需要进行条件判断,根据判断结果决定是否做一件事情,这就需要选择结构。

选择结构用于判断给定的条件,然后根据判断的结果来控制程序的流程。

主要的选择结构有:if 选择结构和 switch 多选择结构。有如下结构:

  1. if 单选择结构
  2. if-else 双选择结构
  3. if-else if-else 多选择结构
  4. switch 结构

3.2.1 if 单选择结构

格式:

if (关系表达式) {
    语句体;  
}

执行流程:

①首先计算关系表达式的值

②如果关系表达式的值为 true 就执行语句体

③如果关系表达式的值为 false 就不执行语句体

④继续执行后面的语句内容

img

/**
 * 测试 if 单选则结构
 * @author IZJ
 *
 */
public class TestIf {  
    public static void main(String[] args) {
        // 通过掷三个骰子看看今天的手气如何?
        int i = (int)(6 * Math.random()) + 1; // 通过 Math.random() 产生随机数
        int j = (int)(6 * Math.random()) + 1;
        int k = (int)(6 * Math.random()) + 1;
        int count = i + j + k;
        // 如果三个骰子之和大于 15,则手气不错
        if(count > 15) {
            System.out.println("今天手气不错");
        }
        //如果三个骰子之和在 10 到 15 之间,则手气一般
        if(count >= 10 && count <= 15) { //错误写法:10<=count<=15
            System.out.println("今天手气很一般");
        }
        //如果三个骰子之和小于 10,则手气不怎么样
        if(count < 10) {
            System.out.println("今天手气不怎么样");
        }
        System.out.println("得了" + count + "分");
    }
}

Math 包说明:

  1. java.lang 包中的 Math 类提供了一些用于数学计算的方法。
  2. Math.random() 该方法用于产生一个 0 到 1 区间的 double 类型的随机数,但是不包括 1。
int i = (int) (6 * Math.random()); //产生:[0,5]之间的随机整数

注意:

  1. 如果 if 语句不写 {},则只能作用于后面的第一条语句。
  2. 强烈建议,任何时候都写上 {},即使里面只有一句话!

3.2.2 if-else 双选择结构

格式:

if (关系表达式) {
    语句体1;
} else {
    语句体2;  
}

执行流程:

①首先计算关系表达式的值

②如果关系表达式的值为 true 就执行语句体 1

③如果关系表达式的值为 false 就执行语句体 2

④继续执行后面的语句内容

img

// 随机产生一个 [0.0, 4.0) 区间的半径,并根据半径求圆的面积和周长
double radius = 4 * Math.random();
double circumference = 2 * Math.PI * radius;
double area = Math.PI * Math.pow(radius, 2);
System.out.println("半径:" + radius);
System.out.println("周长:" + circumference);
System.out.println("面积:" + area);
// 如果面积>=周长,则输出"面积大于等于周长",否则,输出"周长大于面积"
if (area >= circumference) {
    System.out.println("面积大于等于周长");
} else {
    System.out.println("周长大于面积");
}

3.2.3 if-else if-else 多选择结构

格式:

if (关系表达式 1) {
    语句体 1;
} else if (关系表达式 2) {
    语句体 2;
} else if (关系表达式 n) {
    语句体 n;
} else {
    语句体 n + 1;
}

执行流程:

①首先计算关系表达式 1 的值

②如果值为 true 就执行语句体 1;如果值为 false 就计算关系表达式2的值

③如果值为 true 就执行语句体 2;如果值为 false 就计算关系表达式3的值

④…

⑤如果没有任何关系表达式为 true,就执行语句体 n+1。

img

示例:

/**
 * 测试 if-else if-else 多选择结构
 *
 * @author IZJ
 */
public class TestIfElseIfElse {
    public static void main(String[] args) {
        int age = (int) (100 * Math.random());
        System.out.println("年龄:" + age);
        if (age < 15) {
            System.out.println("儿童, 喜欢玩!");
        } else if (age < 25) {
            System.out.println("青年, 要学习!");
        } else if (age < 45) {
            System.out.println("中年, 要工作!");
        } else if (age < 65) {
            System.out.println("中老年, 要补钙!");
        } else if (age < 85) {
            System.out.println("老年, 多运动!");
        } else {
            System.out.println("老寿星, 古来稀!");
        }
    }
}

练习: 随机生成一个 100 以内的成绩,当成绩在 85 及以上的时候输出“等级 A”,70 到 84 之间输出“等级B”,60 到 69 之间输出“等级 C”,60 以下输出“等级 D”。


/**
 * 测试 if-else if-else 多选择结构
 *
 * @author IZJ
 */
public class TestIfElseIfElse2 {
    public static void main(String[] args) {
        /*
        随机生成一个 100 以内的成绩,
        当成绩在 85 及以上的时候输出“等级 A”,
        70 到 84 之间输出“等级 B”,
        60 到 69 之间输出“等级 C”,
        60 以下输出“等级 D”。
        */
        int score = (int) (101 * Math.random());
        System.out.println("成绩:" + score);
        if (score < 60) {
            System.out.println("等级 D");
        } else if (score <= 69) {
            System.out.println("等级 C");
        } else if (score <= 84) {
            System.out.println("等级 B");
        } else {
            System.out.println("等级 A");
        }
    }
}

3.2.4 switch多选择结构

格式:

switch (表达式) {
    case 1:
        语句体 1;
        break;
    case 2:
        语句体 2;
        break;
    case n:
        语句体 n;
        break;
    default:
        语句体 n + 1;
        break;
}

执行流程:

  • 首先计算出表达式的值
  • 其次,和 case 依次比较,一旦有对应的值,就会执行相应的语句,在执行的过程中,遇到 break 就会结束。
  • 最后,如果所有的 case 都和表达式的值不匹配,就会执行 default 语句体部分(如果存在 default 语句的情况),然后程序结束掉。

根据表达式值的不同可以执行许多不同的操作。switch 语句中 case 标签在 JDK1.5 之前必须是整数(long 类型除外)或者枚举,不能是字符串,在 JDK1.7之后允许使用字符串(String)。

大家要注意,当布尔表达式是等值判断(类似 a == 5)的情况,可以使用 if-else if-else 多选择结构或者 switch 结构,如果布尔表达式区间判断(类似a ≤ 5)的情况,则只能使用 if-else if-else 多选择结构。

import java.util.Scanner;
/**
 * 测试 Switch 语句
 *
 * @author IZJ
 */
public class TestSwitch {
    public static void main(String[] args) {
        // 键盘录入月份数据,使用变量接收
        System.out.print("请输入月份:");
        Scanner sc = new Scanner(System.in);
        int month = sc.nextInt();
        System.out.print(month + "月属于");
        // case 穿透
        switch (month) {
            case 12:
            case 1:
            case 2:
                System.out.println("Winter");
                break;
            case 3:
            case 4:
            case 5:
                System.out.println("Spring");
                break;
            case 6:
            case 7:
            case 8:
                System.out.println("Summer");
                break;
            case 9:
            case 10:
            case 11:
                System.out.println("Autumn");
                break;
            default:
                System.out.println("Wrong Month!!");
        }
    }
}

注意:如果 switch 中的 case,没有对应 break 的话,则会出现 case 穿透的现象。

3.3 循环结构

循环结构分两大类,一类是当型,一类是直到型。

  • **当型:**当布尔表达式条件为 true 时,反复执行某语句,当布尔表达式的值为 false 时才停止循环,比如:whilefor 循环。

  • **直到型:**先执行某语句, 再判断布尔表达式,如果为 true,再执行某语句,如此反复,直到布尔表达式条件为 false 时才停止循环,比如 do-while 循环。

3.3.1 while 循环

  • while循环完整格式:
初始化语句;
while (条件判断语句) {
    循环体语句;
    条件控制语句;
}
  • while循环执行流程:

    img

练习 1:求 1 到 100 累加和

/**
 * 测试 while 循环
 *
 * @author IZJ
 */
public class TestWhile {
    public static void main(String[] args) {
        // 求 1 到 100 之间的累加和
        // 方式一:
        int i = 1;
        int sum = 0;
        while (i <= 100) {
            sum += i;
            i++; // 一定要写!!!不然会死循环
        }
        System.out.println("1~100 的和为" + sum);

        // 方式二:
        i = 1;
        sum = 0;
        while(i <= 100) {
            sum += i++;
        }
        System.out.println("1~100 的和为" + sum);

        // 方式三:
        i = 0;
        sum = 0;
        while(i++ < 100) {
            sum += i;
        }
        System.out.println("1~100 的和为" + sum);
    }
}

练习 2:珠穆朗玛峰

需求:世界最高山峰是珠穆朗玛峰(8844.43 米=8844430 毫米),假如我有一张足够大的纸,它的厚度是 0.1 毫米。请问,我对折多少次,可以折成珠穆朗玛峰的高度?

int count = 0;
double paper = 0.1;
int zf = 8844430;
while (paper < zf) {
    paper *= 2;
    count++;
}
System.out.println("需要折叠" + count + "次");

3.3.2 do-while 循环

初始化语句;
do {
  循环体语句;
  条件控制语句;
} while (条件判断语句);

do-while 循环结构会先执行循环体,然后再判断布尔表达式的值,若条件为真,执行循环体,当条件为假时结束循环。**do-while 的循环体至少执行一次。**do-while 循环结构流程图如图所示。

img

3.3.3 for 循环

for (初始化语句; 条件判断语句; 条件控制语句) {
    循环体语句;
}
  1. 初始化语句:设置循环变量的初值

  2. 条件判断语句:为任意布尔表达式

  3. 条件控制语句:控制循环变量的增减

for 循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。for 循环在第一次反复之前要进行初始化,即执行初始表达式;随后,对布尔表达式进行判定,若判定结果为 true,则执行循环体,否则,终止循环;最后在每一次反复的时候,进行某种形式的“步进”,即执行迭代因子。

for 循环在执行条件判定后,先执行的循环体部分,再执行步进。

/**
 * 测试 for 循环
 *
 * @author IZJ
 */
public class TestFor {
    public static void main(String[] args) {
        // 1.求 1-100 之间的累加和
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum += i;
        }
        System.out.println("Sum = " + sum);
    }
}

Java 里能用到逗号运算符的地方屈指可数,其中一处就是 for 循环的控制表达式。在控制表达式的初始化和步进控制部分,我们可以使用一系列由逗号分隔的表达式,而且那些表达式均会独立执行。

for(int i = 1, j = i + 10; i < 5; i++, j = i * 2) {
    System.out.println("i= " + i + " j= " + j); 
} 
  1. 无论在初始化还是在步进部分,语句都是顺序执行的。

  2. 尽管初始化部分可设置任意数量的定义,但都属于同一类型。

  3. 约定:只在 for 语句的控制表达式中写入与循环变量初始化,条件判断和迭代因子相关的表达式。

初始化部分、条件判断部分和迭代因子可以为空语句,但必须以“;”分开。

  • 无限循环
for (;;) { // 无限循环: 相当于 while(true)
    System.out.println("爱你到永远");
}

编译器将 while(true) 与 for(;;) 看作同一回事,都指的是无限循环。

练习1:求 100 以内偶数和

int sum = 0;
for (int i = 2; i <= 100; i++) {
    if (i % 2 == 0) {
        sum += i;
    }
}
System.out.println("Sum = " + sum);

练习2: 水仙花数

  • 需求:在控制台输出所有的“水仙花数”
  • 解释:什么是水仙花数?
    • 水仙花数,指的是一个三位数,个位、十位、百位的数字立方和等于原数
      • 例如 153 就是水仙花数:3*3*3 + 5*5*5 + 1*1*1 = 153
int g = 0;
int s = 0;
int b = 0;
for(int i = 100; i <= 999; i++) {
    g = i % 10;
    s = i / 10 % 10;
    b = i / 100;
    if (Math.pow(g, 3) + Math.pow(s, 3) + Math.pow(b, 3) == i) {
        System.out.println(i);
    }
}

3.3.4 嵌套循环

在一个循环语句内部再嵌套一个或多个循环,称为嵌套循环。while、do-while 与 for 循环可以任意嵌套多层。

for (int i = 1; i <= 5; i++) {
    for(int j = 1; j <= 5; j++){
        System.out.print(i + "  ");
    }
    System.out.println();
}

使用嵌套循环实现九九乘法表

/**
 * 测试嵌套循环
 *
 * @author IZJ
 */
public class TestNestedCycle {
    public static void main(String[] args) {
        for(int i = 1; i < 10; i++) {
            for(int j = 1; j <= i; j++) {
                System.out.print("" + j + " x " + i + " = " + j * i + "\t");
            }
            System.out.println();
        }
    }
}

用 while 循环分别计算 100 以内的奇数及偶数的和,并输出。

int i = 0;
int oddSum = 0;
int evenSum = 0;
while (i++ < 100) {
  if(i % 2 == 0) {
    evenSum += i;
  } else {
    oddSum += i;
  }
}
System.out.println("奇数和:" + oddSum);
System.out.println("偶数和:" + evenSum);

用 while 循环或其他循环输出 1-1000 之间能被 5 整除的数,且每行输出 5 个。

int count = 0;
for(int i = 1; i <= 1000; i++) {
    if(i % 5 == 0) {
        System.out.print(i + "\t");
        count ++;
    }
    if (count == 5) {
        System.out.println();
        count = 0;
    }
}

3.3.5 跳转控制语句

1. break 语句

在任何循环语句的主体部分,均可用 break 控制循环的流程。break 用于强行退出循环,不执行循环中剩余的语句。

/**
 * 测试循环语句中的 break
 * @author IZJ
 *
 */
public class TestBreak {
    public static void main(String[] args) {
        int total = 0; // 定义计数器
        System.out.println("Begin");
        while (true) {
            total++; // 每循环一次计数器加 1
            int i = (int) Math.round(100 * Math.random());
            // 当 i 等于 88 时,退出循环
            if (i == 88) {
                break;
            }
        }
        // 输出循环的次数
        System.out.println("Game over, used " + total + " times.");
    }
}

2. continue 语句

continue 语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。

  • 注意事项
    1. continue 用在 while,do-while 中,continue 语句立刻跳到循环首部,越过了当前循环的其余部分。
    2. continue 用在 for 循环中,跳到 for 循环的迭代因子部分,也就是跳过本次剩余执行流程的意思。

把 100~150 之间不能被 3 整除的数输出,并且每行输出 5 个

/**
 * 测试循环语句中的 continue
 *
 * @author IZJ
 */
public class TestContinue {
    public static void main(String[] args) {
        int count = 0;
        for (int i = 100; i < 150; i++) {
            if(i % 3 == 0) {
                continue;
            }
            count++;
            System.out.print(i + "\t");
            if(count == 5) {
                count = 0;
                System.out.println();
            }
        }
    }
}

3. 带标签的 break 和 continue

goto 关键字很早就在程序设计语言中出现。尽管 goto 仍是Java的一个保留字,但并未在 Java 语言中得到正式使用;Java 没有 goto 语句。然而,在 break 和 continue 这两个关键字的身上,我们仍然能看出一些 goto 的影子 —— 带标签的 break 和 continue。

“标签”是指后面跟一个冒号的标识符,例如:“label:”。对 Java 来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于 break 和 continue 关键字通常只中断当前循环,但若随同标签使用,它们就会中断到存在标签的地方。

在 “goto 有害”论中,最有问题的就是标签,而非 goto, 随着标签在一个程序里数量的增多,产生错误的机会也越来越多。 但 Java 标签不会造成这方面的问题,因为它们的活动场所已被限死,不可通过特别的方式到处传递程序的控制权。由此也引出了一个有趣的问题:通过限制语句的能力,反而能使一项语言特性更加有用。

打印 101-150 之间所有的质数

/**
 * 测试带标签的 break 和 continue
 *
 * @author IZJ
 */
public class TestLabelContinue {
    public static void main(String[] args) {
        // 打印 101-150 之间所有的质数
        outer:for (int i = 101; i <= 150; i++) {
            for (int j = 2; j < i / 2; j++) {
                if (i % j == 0) { // 如果是合数,不打印,继续循环
                    continue outer; // 如果单单 continue,则会在内层 for 里面 continue
                }
            }
            System.out.print(i + " ");
        }
    }
}