五、控制流(1)

本章概要

  • true 和 false
  • if-else
  • 迭代语句
    • while
    • do-while
    • for
      • 逗号操作符
  • for-in 语法

程序必须在执行过程中控制它的世界并做出选择。 在 Java 中,你需要执行控制语句来做出选择。

Java 使用了 C 的所有执行控制语句,因此对于熟悉 C/C++ 编程的人来说,这部分内容轻车熟路。大多数面向过程编程语言都有共通的某种控制语句。在 Java 中,涉及的关键字包括 if-else,while,do-while,for,return,break 和选择语句 switch。 Java 并不支持备受诟病的 goto(尽管它在某些特殊场景中依然是最行之有效的方法)。 尽管如此,在 Java 中我们仍旧可以进行类似的逻辑跳转,但较之典型的 goto 用法限制更多。

true和false

所有的条件语句都利用条件表达式的“真”或“假”来决定执行路径。举例:
a == b。它利用了条件表达式 == 来比较 ab 的值是否相等。 该表达式返回 truefalse。代码示例:

// control/TrueFalse.java
public class TrueFalse {
	public static void main(String[] args) {
		System.out.println(1 == 1);
		System.out.println(1 == 2);
	}
}

输出结果:

true false

通过上一章的学习,我们知道任何关系运算符都可以产生条件语句。 注意:在 Java 中使用数值作为布尔值是非法的,即便这种操作在 C/C++ 中是被允许的(在这些语言中,“真”为非零,而“假”是零)。如果想在布尔测试中使用一个非布尔值,那么首先需要使用条件表达式来产生 boolean 类型的结果,例如 if(a != 0)

if-else

if-else 语句是控制程序执行流程最基本的形式。 其中 else 是可选的,因此可以有两种形式的 if。代码示例:

if(Boolean-expression) 
	“statement”

if(Boolean-expression) 
	“statement”
else
  “statement”

布尔表达式(Boolean-expression)必须生成 boolean 类型的结果,执行语句 statement 既可以是以分号 ; 结尾的一条简单语句,也可以是包含在大括号 {} 内的的复合语句 —— 封闭在大括号内的一组简单语句。 凡本书中提及“statement”一词,皆表示类似的执行语句。

下面是一个有关 if-else 语句的例子。test() 方法可以告知你两个数值之间的大小关系。代码示例:

// control/IfElse.java
public class IfElse {
  static int result = 0;
  static void test(int testval, int target) {
    if(testval > target)
      result = +1;
    else if(testval < target) // [1]
      result = -1;
    else
      result = 0; // Match
  }

  public static void main(String[] args) {
    test(10, 5);
    System.out.println(result);
    test(5, 10);
    System.out.println(result);
    test(5, 5);
    System.out.println(result);
  }
}

输出结果:

1
-1
0

注解else if 并非新关键字,它仅是 else 后紧跟的一条新 if 语句。

Java 和 C/C++ 同属“自由格式”的编程语言,但通常我们会在 Java 控制流程语句中采用首部缩进的规范,以便代码更具可读性。

迭代语句

whiledo-whilefor 用来控制循环语句(有时也称迭代语句)。只有控制循环的布尔表达式计算结果为 false,循环语句才会停止。

while

while 循环的形式是:

while(Boolean-expression) 
  statement

执行语句会在每一次循环前,判断布尔表达式返回值是否为 true。下例可产生随机数,直到满足特定条件。代码示例:

// control/WhileTest.java
// 演示 while 循环
public class WhileTest {
    static boolean condition() {
        boolean result = Math.random() < 0.99;
        System.out.print(result + ", ");
        return result;
    }

    public static void main(String[] args) {
        while (condition()) {
            System.out.println("Inside 'while'");
        }
        System.out.println("Exited 'while'");
    }
}

输出结果:

在这里插入图片描述

condition() 方法使用到了 Math 库的静态方法 random()。该方法的作用是产生 0 和 1 之间 (包括 0,但不包括 1) 的一个 double 值。

result 的值是通过比较运算符 < 产生的 boolean 类型的结果。当控制台输出 boolean 型值时,会自动将其转换为对应的文字形式 truefalse。此处 while 条件表达式代表:“仅在 condition() 返回 false 时停止循环”。

do-while

do-while 的格式如下:

do 
	statement
while(Boolean-expression);

whiledo-while 之间的唯一区别是:即使条件表达式返回结果为 falsedo-while 语句也至少会执行一次。 在 while 循环体中,如布尔表达式首次返回的结果就为 false,那么循环体内的语句不会被执行。实际应用中,while 形式比 do-while 更为常用。

for

for 循环可能是最常用的迭代形式。 该循环在第一次迭代之前执行初始化。随后,它会执行布尔表达式,并在每次迭代结束时,进行某种形式的步进。for 循环的形式是:

for(initialization; Boolean-expression; step)
  statement

初始化 (initialization) 表达式、布尔表达式 (Boolean-expression) ,或者步进 (step) 运算,都可以为空。每次迭代之前都会判断布尔表达式的结果是否成立。一旦计算结果为 false,则跳出 for 循环体并继续执行后面代码。 每次循环结束时,都会执行一次步进。

for 循环通常用于“计数”任务。代码示例:

// control/ListCharacters.java
public class ListCharacters {
    public static void main(String[] args) {
        for (char c = 0; c < 128; c++) {
            if (Character.isLowerCase(c)) {
                System.out.println("value: " + (int) c + " character: " + c);
            }
        }
    }
}

输出结果(前 10 行):

在这里插入图片描述

注意:变量 c 是在 for 循环执行时才被定义的,并不是在主方法的开头。c 的作用域范围仅在 for 循环体内。

传统的面向过程语言如 C 需要先在代码块(block)前定义好所有变量才能够使用。这样编译器才能在创建块时,为这些变量分配内存空间。在 Java 和 C++ 中,我们可以在整个块使用变量声明,并且可以在需要时才定义变量。 这种自然的编码风格使我们的代码更容易被人理解。

上例使用了 java.lang.Character 包装类,该类不仅包含了基本类型 char 的值,还封装了一些有用的方法。例如这里就用到了静态方法 isLowerCase() 来判断字符是否为小写。

逗号操作符

在 Java 中逗号运算符(这里并非指我们平常用于分隔定义和方法参数的逗号分隔符)仅有一种用法:在 for 循环的初始化和步进控制中定义多个变量。我们可以使用逗号分隔多个语句,并按顺序计算这些语句。注意:要求定义的变量类型相同。代码示例:

// control/CommaOperator.java
public class CommaOperator {
    public static void main(String[] args) {
        for (int i = 1, j = i + 10; i < 5; i++, j = i * 2) {
            System.out.println("i = " + i + " j = " + j);
        }
    }
}

输出结果:

i = 1 j = 11
i = 2 j = 4
i = 3 j = 6
i = 4 j = 8

上例中 int 类型声明包含了 ij。实际上,在初始化部分我们可以定义任意数量的同类型变量。注意:在 Java 中,仅允许 for 循环在控制表达式中定义变量。 我们不能将此方法与其他的循环语句和选择语句中一起使用。同时,我们可以看到:无论在初始化还是在步进部分,语句都是顺序执行的。

for-in 语法

Java 5 引入了更为简洁的“增强版 for 循环”语法来操纵数组和集合。(更多细节,可参考 数组 和 集合 章节内容)。大部分文档也称其为 for-each 语法,但因为了不与 Java 8 新添的 forEach() 产生混淆,因此这里称之为 for-in 循环。 (Python 已有类似的先例,如:for x in sequence)。注意:你可能会在其他地方看到不同叫法。

for-in 无需你去创建 int 变量和步进来控制循环计数。 下面我们来遍历获取 float 数组中的元素。代码示例:

import java.util.*;

// control/ForInFloat.java
public class ForInFloat {
    public static void main(String[] args) {
        Random rand = new Random(47);
        float[] f = new float[10];
        for (int i = 0; i < 10; i++) {
          f[i] = rand.nextFloat();
        }
        for (float x : f) {
          System.out.println(x);
        }
    }
}

输出结果:

在这里插入图片描述

上例中我们展示了传统 for 循环的用法。接下来再来看下 for-in 的用法。代码示例:

for(float x : f) {}

这条语句定义了一个 float 类型的变量 x,继而将每一个 f 的元素赋值给它。

任何一个返回数组的方法都可以使用 for-in 循环语法来遍历元素。例如 String 类有一个方法 toCharArray(),返回值类型为 char 数组,我们可以很容易地在 for-in 循环中遍历它。代码示例:

// control/ForInString.java
public class ForInString {
    public static void main(String[] args) {
        for (char c : "An African Swallow".toCharArray()) {
            System.out.print(c + " ");
        }
    }
}

输出结果:

A n   A f r i c a n   S w a l l o w

很快我们能在 集合 章节里学习到,for-in 循环适用于任何可迭代(iterable)的 对象。

通常,for 循环语句都会在一个整型数值序列中步进。代码示例:

for(int i = 0; i < 100; i++){}

正因如此,除非先创建一个 int 数组,否则我们无法使用 for-in 循环来操作。为简化测试过程,已在 onjava 包中封装了 Range 类,利用其 range() 方法可自动生成恰当的数组。

Range.java

public class Range {
    // Produce sequence [start..end) incrementing by step
    public static int[] range(int start, int end, int step) {
        if (step == 0) {
            throw new IllegalArgumentException("Step cannot be zero");
        }
        int sz = Math.max(0, step >= 0 ? (end + step - 1 - start) / step : (end + step + 1 - start) / step);
        int[] result = new int[sz];
        for (int i = 0; i < sz; i++) {
            result[i] = start + (i * step);
        }
        return result;
    }  // Produce a sequence [start..end)

    public static int[] range(int start, int end) {
        return range(start, end, 1);
    }

    // Produce a sequence [0..n)
    public static int[] range(int n) {
        return range(0, n);
    }
}

在 封装(Implementation Hiding)这一章里我们介绍了静态导入(static import),无需了解细节就可以直接使用。 有关静态导入的语法,可以在 import 语句中看到:

import static BASE0002.Range.range;
// control/ForInInt.java
public class ForInInt {
    public static void main(String[] args) {
        for (int i : range(10)) // 0..9
        {
            System.out.print(i + " ");
        }
        System.out.println();
        for (int i : range(5, 10)) // 5..9
        {
            System.out.print(i + " ");
        }
        System.out.println();
        for (int i : range(5, 20, 3)) // 5..20 step 3
        {
            System.out.print(i + " ");
        }
        System.out.println();
        for (int i : range(20, 5, -3)) // Count down
        {
            System.out.print(i + " ");
        }
        System.out.println();
    }
}

输出结果:

0 1 2 3 4 5 6 7 8 9
5 6 7 8 9
5 8 11 14 17
20 17 14 11 8

range() 方法已被 重载(重载:同名方法,参数列表或类型不同)。上例中 range() 方法有多种重载形式:第一种产生从 0 至范围上限(不包含)的值;第二种产生参数一至参数二(不包含)范围内的整数值;第三种形式有一个步进值,因此它每次的增量为该值;第四种 range() 表明还可以递减。range() 无参方法是该生成器最简单的版本。

range() 的使用提高了代码可读性,让 for-in 循环在本书中适应更多的代码示例场景。

请注意,System.out.print() 不会输出换行符,所以我们可以分段输出同一行。

for-in 语法可以节省我们编写代码的时间。 更重要的是,它提高了代码可读性以及更好地描述代码意图(获取数组的每个元素)而不是详细说明这操作细节(创建索引,并用它来选择数组元素) 本书推荐使用 for-in 语法。(注:本人曾看过一篇文章,是测试不同迭代方式的效率问题,最后结论为使用 for(int i = 0; i < 100; i++){} 方式为最优,个人推荐此方法)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小熊猫呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值