分支结构

1. 第一个分支结构的例子

程序经常需要在不同的情况下选择做不同的事情,比如

用户输入一个数,判断是不是偶数,并给出相应结论。

Guidelines.

  1. 右键单击src,新建一个包,起名为flowcontrol: new -> Package -> 填入包名flowcontrol,确定;
  2. 右键单击包flowcontrolnew -> Class -> 填入类名CheckEvenNumber, 确定。
    例1. 判断输入的整数是奇数还是偶数

CheckEvenNumber.java

package flowcontrol; // 申明该类所在的包
import java.util.Scanner;   // 引入java提供的用于console输入的类:Scanner

public class CheckEvenNumber {
	public static void main(String[] args) {

            // 用Scanner类创建一个对象,用来捕获输入,System.in是通过console输入
            Scanner input = new Scanner(System.in);
            System.out.print("Enter a number: ");   // 输出一个提示

            // 读入一个整数,这里java已经自动将输入转化为整数
            int num = input.nextInt(); 

            input.close();  // 关闭input对象

            if (num % 2 == 0) { // 如果输入的数字是个偶数(可以被2整除),执行{}里的代码
                System.out.println(num + " is even.");
            } else {    // 否则, otherwise,执行{}内的代码
                System.out.println(num + " is odd.");
	        }

	  }
}

在上面的例子中,我们引入了一些新的概念:

  • 包(Package:类似于文件夹,用来将类(class)分类存放。如果一个类属于一个包,那么这个类的开头必须声明, 在src根目录下的类不需要申明其所属包。上面的例子中,CheckEvenNumber.java是放在flowcontrol包里的,那么在类的开头需要声明:package flowcontrol;
  • 引入其他类:通常我们需要使用其他类和它提供的方法(比如Java自带的方法Math类和其提供的方法Math.sqrt()用来计算平方根),在使用之前需要先引入这个类,比如这个例子中的:'import java.util.Scanner;' 申明将Java提供的捕获输入的类引入到CheckEvenNumber.java类中使用。

该例子的核心内容是这一片代码

    if (num % 2 == 0) { // 如果输入的数字是个偶数(可以被2整除),执行{}里的代码
        System.out.println(num + " is even.");
    } else {    // 否则, otherwise,执行{}内的代码
        System.out.println(num + " is odd.");
    }

这里的if else代码块

if (关系表达式){
    代码块1;
} else {
    代码块2;
}

就是一个分支结构, 其表示:

  • 如果关系表达式true,那么执行代码块1
  • 否则执行代码块2.
  • 例子中关系表达式num % 2 == 0是先计算num % 2的结果,然后判断其结果是否等于0.

Tips.

  • 关系表达式有 a>b, a<b, a==b, a!=b, a>=b, a<=b等均是关系运算,结果是true或者false;
  • 判断两个数是否相等是 ===是赋值。判断两个是是否不等是!=
  • 关系运算的优先级在数学运算之下,Java会先计算关系运算符(>, <,==等)两边的结果,再进行关系判断。

Extension:构建一个方法,将判断奇偶的任务分配出去

public static void main(String[] args) {
    Scanner input = new Scanner(System.in);
    System.out.print("Enter a number: ");
    int num = input.nextInt();
    input.close();
    checkEvenNumber(num);
    }

static void checkEvenNumber(int aNum) {
    if (aNum % 2 == 0) {
        System.out.println(aNum + " is even.");
    } else {
        System.out.println(aNum + " is odd.");
    }
}

extension中构建了一个方法checkEvenNumber(int aNum), 需要一个int类型的参数(需要判断的数)。在main方法中调用这个方法并传入console输入的数进行判断。


2. 分支结构和流程图

我们可以用程序流程图(flow chart)来描述以上分支结构,流程图在程序设计的时候也很有用。一个基本的包含ifelse statement的流程图如下:

基本分支结构

图1. if-else分支结构流程图

例1就是一个同时包含ifelse的分支结构程序。
在一些情况下,可能只存在if代码块,而不存在else代码块:如果条件成立,就做点事情;要是不成立就算了。比如:

例2. 检查是否是VIP

模拟用户登陆后,判断是否是VIP:

  • 如果是则显示欢迎VIP的信息(同时可以赋予VIP权限);
  • 如果不是此处不做任何处理。

Guidlines.

  1. 右键单击flowcontrol包,包名设置为flowcontrol.vip
  2. 右键单击flowcontrol.vip包,新建一个class,命名为CheckVIP。

Tips.

Java中包和子包(文件夹和子文件夹)的关系是用"."来描述的,flowcontrol.vip是说在flowcontrol包下建立一个子包,命名为vip

CheckVIP.java

package flowcontrol.vip;

public class CheckVIP {

	public static void main(String[] args) {
		String username = "customer_1";
		boolean isVIP = checkVIP(username); // 调用检验VIP的方法
		if(isVIP) {
			System.out.printf("Welcome back, VIP user %s \n\n", username);
			// 实际中这里可以给VIP用户授与使用VIP服务的权限
		}
		System.out.println("Display the personal page for the user.");
	}
	
	static boolean checkVIP(String uname) {
		boolean isVIP = true; // 实际中可以通过传入的参数uname从数据库获取
		return isVIP; // 返回是否是VIP的信息
	}
}

程序说明:

  • 实际检验是否是VIP的工作交给了方法checkVIP(String uname)去实现;
  • main方法中,调用这个方法,获取是否是VIP的信息(true 或者 false),然后赋值给main方法的局部变量isVIP
  • 如果isVIP等于true,则打印欢迎信息; 如果isVIP等于false,则跳过if后面{ }代码块的内容。
  • 继续执行后续程序。

其对应的流程图为: 只有if statement的流程图

图2. 只有if statement的分支结构


3. 链式分支 & 嵌套分支结构:Lucky Draw的例子

玩一个Lucky Draw的游戏:

一等奖二等奖不中奖。一等奖中奖概率10%, 二等奖40%,三等奖50%;

设计一个程序,来实现这个lucky draw的游戏。

3.1 链式分支的方案

Guidelines.

右键单击包flowcontrol,创建一个名为luckydraw的包; 右键单击这个包,创建一个类:new -> Class -> 填入类名LuckyDraw01, 确定。

例3. 链式分支结构的LuckyDraw

LuckyDraw01.java

package flowcontrol.luckydraw;

import java.util.Random;

public class LuckyDraw01 {
    static final firstPrizeProb = 0.1;  // 中一等奖概率,小于这个概率的中奖
    static double secondPrizeProb = 0.3; // 中二等奖概率,小于这个概率且大于一等奖概率的中奖

	public static void main(String[] args) {

        // 用Java提供的Random类,创建一个叫randomNumGenerator的对象,用来产生随机数
		Random randomNumGenerator = new Random();

        // 生成一个double类型的随机数,保存到youDraw变量里
		double yourDraw = randomNumGenerator.nextDouble(); 
        // 显示随机数。%.2f 为有‘两位小数’的小数占位符
		System.out.printf("Your draw is %.2f. \n", yourDraw);

		if(yourDraw < firstPrizeProb) {
			System.out.println("Congratulations! You won the first prize!");
		} else if(yourDraw < firstPrizeProb + secondPrizeProb){
			System.out.println("You won the second prize!");
		} else {
			System.out.println("Oops! What a bad luck.");
		}
	}
}

上面例子中判断中几等奖的代码结构是

if(condition 1) {
    代码块1;
} else if(condition 2) {
    代码块2;
} else {
    代码块3;
}

说明:

  • 如果condition 1true,则执行代码块1
  • 否则,继续判断condition 2, 如果condition 2true,则执行代码块2
  • 否则执行代码块3.

上面的例子中,把0-1的数分成了三段:0~0.1, 0.1~0.5, 其他。当(0到1以内的)随机数落在0~0.1是一等奖,落在0.1~0.5是二等奖,否则不得奖。很直观,上述程序满足对中奖概率的要求。

3.1 嵌套分支的方案

上面例子中分支部分可以更改为:

例4. 嵌套分支结构的LuckyDraw

    if (yourDraw > 1 - firstPrizeProb - secondPrizeProb) { // 未获奖
        System.out.println("Oops! What a bad luck.");
    } else { // 否则,即获奖,再在内层if-else判断获几等奖
            if(yourDraw < firstPrizeProb) { // 获得一等奖
                System.out.println("Congratulations! You won the first prize!");
            } else {
                System.out.println("You won the second prize!");
            }
    }

以上例子的代码结构为

if (condition 1) {
    外层代码块1;
} else {

    //内层if-else代码块
        if (condition 2) {
            内层代码块1;
        } else {
            内层代码块2;
        }

}

Tips.

因为{ }是包围一个相对完整的代码块,所以里面包含什么结构都不奇怪。