《Java编程思想》阅读笔记之第4章-控制执行流程

Posted 二木成林

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java编程思想》阅读笔记之第4章-控制执行流程相关的知识,希望对你有一定的参考价值。

目录

 

第4章 控制执行流程

4.1 true和false

4.2 if-else

4.3 迭代

4.3.1 do-while

4.3.2 for

4.3.3 逗号操作符

4.4 foreach语法

4.5 return

4.6 break和continue

4.7 臭名昭著的goto

4.8 switch


第4章 控制执行流程

4.1 true和false

所有条件语句都利用条件表达式的真或假来决定执行路径。这里有一个条件表达式的例子:a==b。它用条件操作符“==”来判断a值是否等于b值。该表达式返回true或false。

本章前面介绍的所有关系操作符,都可拿来构造条件语句。注意Java不允许我们将一个数字作为布尔值使用,虽然这在C和C++里是允许的(在这些语言里,“真”是非零,而“假”是零)。

如果想在布尔测试中使用一个非布尔值,比如在if(a)中,那么首先必须用一个条件表达式将其转换成布尔值,例如if(a!=0)。

4.2 if-else

if-else语句是控制程序流程的最基本形式。其中if必需,而else是可选的。

语法如下:

if(Boolean-expression)
    statement

if(Boolean-expression)
    statement
else
    statement

布尔表达式Boolean-expression必须产生一个布尔结果(true或false),statement指用分号结尾的简单语句,或复合语句─--封闭在花括号内的一组简单语句。

package 第4章_控制执行流程.第2节_if和else;

public class IfElse {
    static int result = 0;

    static void test(int testval, int target) {
        if (testval > target) {
            result = +1;
        } else if (testval < target) {
            result = -1;
        } else {
            result = 0;
        }
    }

    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语句。

4.3 迭代

while、do-while和for用来控制循环,有时将它们划分为迭代语句(iteration statement)。语句会重复执行,直到起控制作用的布尔表达式(Booleanexpression)得到“假”的结果为止。while循环的格式如下:

while(Boolean-expression)
    statement

在循环刚开始时,会计算一次布尔表达式的值﹔而在语句的下一次迭代开始前会再计算一次。

下面这个简单的例子可产生随机数,直到符合特定的条件为止:

package 第4章_控制执行流程.第3节_迭代;

public class WhileTest {
    static boolean condition() {
        // Math.random()产生0到1之间(包括0但不包括1)的一个double值
        boolean result = Math.random() < 0.99;
        System.out.println(result + ", ");
        return result;
    }

    public static void main(String[] args) {
        while (condition()) {
            System.out.println("Inside 'while'");
        }
        System.out.println("Exited 'while'");
    }
}
/**
 * 打印结果:
 * 自己执行查看结果
 */

4.3.1 do-while

do-while的格式如下:

do 
    statement
while(Boolean-expression);

while和do-while唯一的区别就是do-while中的语句至少会执行一次,即便表达式第一次就被计算为false。而在while循环结构中,如果条件第一次就为false,那么其中的语句根本不会执行。在实际应用中,while比do-while更常用一些。

package 第4章_控制执行流程.第3节_迭代.第1目_dowhile;

public class DoWhile {
    public static void main(String[] args) {
        /* 1.do-while的使用 */
        System.out.println("do-while的使用: ");
        int i = 5;
        do {
            System.out.println(i);
            i--;
        } while (i != -1);

        /* 2.while的使用 */
        System.out.println("while的使用: ");
        int j = 5;
        while (j != -1) {
            System.out.println(j);
            j--;
        }
    }
}
/**
 * 打印结果:
 * do-while的使用:
 * 5
 * 4
 * 3
 * 2
 * 1
 * 0
 * while的使用:
 * 5
 * 4
 * 3
 * 2
 * 1
 * 0
 */

4.3.2 for

for循环可能是最经常使用的迭代形式,这种在第一次迭代之前要进行初始化。随后,它会进行条件测试,而且在每一次迭代结束时,进行某种形式的“步进”。for循环的格式如下:

for(initialization; Boolean-expression; step)
    statement

初始化(initialization)表达式、布尔表达式(Boolean-expression),或者步进(step)运算,都可以为空。每次迭代前会测试布尔表达式。若获得的结果是false,就会执行for语句后面的代码行。每次循环结束,会执行一次步进。

package 第4章_控制执行流程.第3节_迭代.第2目_for;

public class ListCharacters {
    public static void main(String[] args) {
        // for循环常用于执行"计数"任务
        for (char c = 0; c < 128; c++) {
            if (Character.isLowerCase(c)) {
                System.out.println("value: " + (int) c + " character: " + c);
            }
        }
    }
}
/**
 * 打印结果:
 * value: 97 character: a
 * value: 98 character: b
 * value: 99 character: c
 * value: 100 character: d
 * value: 101 character: e
 * value: 102 character: f
 * value: 103 character: g
 * value: 104 character: h
 * value: 105 character: i
 * value: 106 character: j
 * ...
 */

注意:变量c是定义在for循环的控制表达式里的,而不是在main()开始的地方,也就是说变量c的作用域在for控制的表达式的范围内。

isLowerCase()方法来检查问题中的字符是否为小写字母。

4.3.3 逗号操作符

注意是逗号操作符,不是逗号分隔符(逗号分隔符用来分隔函数的不同参数)。

Java里唯一用到逗号操作符的地方就是for循环的控制表达式。在控制表达式的初始化和步进控制部分,可以使用一系列由逗号分隔的语句,而且那些语句均会独立执行。

通过使用逗号操作符,可以在for语句内定义多个变量,但是它们必须具有相同的类型。

package 第4章_控制执行流程.第3节_迭代.第3目_逗号操作符;

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
 */

for语句中的int定义涵盖了i和j,在初始化部分实际上可以拥有任意数量的具有相同类型的变量定义。在一个控制表达式中,定义多个变量的这种能力只限于for循环适用,在其他任何选择或迭代语句中都不能使用这种方式。

4.4 foreach语法

foreach语法,不必创建int变量去对访问项构成的序列进行计数,会自动产生每一项。

package 第4章_控制执行流程.第4节_foreach语法;

import java.util.Random;

public class ForEachFloat {
    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);
        }
    }
}
/**
 * 打印结果:
 * 0.72711575
 * 0.39982635
 * 0.5309454
 * 0.0534122
 * 0.16020656
 * 0.57799757
 * 0.18847865
 * 0.4170137
 * 0.51660204
 * 0.73734957
 */

其中可以看到foreach的语句语法:

for(float x: f){

}

其中f可以是一个数组,也可以是一个容器如List,而float x可以是一个基本类型的变量,也可以是对象。

任何返回一个数组的方法都可以使用foreach,例如String类有一个toCharArray()方法返回一个char数组:

package 第4章_控制执行流程.第4节_foreach语法;

public class ForEachString {
    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
 */

在后面还会接触到foreach用于任何Iterable对象。

许多for语句会在一个整型值序列中步进,如

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

但foreach不能起作用,所以可以使用net.mindview.util.Range包中创建的一个名为range()的方法,可以自动生成数组。

package 第4章_控制执行流程.第4节_foreach语法;

import static net.mindview.util.Print.print;
import static net.mindview.util.Print.printnb;
import static net.mindview.util.Range.range;

public class ForEachInt {
    public static void main(String[] args) {
        for (int i : range(10)) {// 0~9
            printnb(i + " ");
        }
        print();

        for (int i : range(5, 10)) { // 5~9
            printnb(i + " ");
        }
        print();

        for (int i : range(5, 20, 3)) { // 5~20 step 3
            printnb(i + " ");
        }
        print();
    }
}
/**
 * 打印结果:
 * 0 1 2 3 4 5 6 7 8 9
 * 5 6 7 8 9
 * 5 8 11 14 17
 */
  • range(n):表示从0开始产生值,直至范围的上限n,但不包括该上限。如range(10)表示0到9。
  • range(start, end):表示从第一个值start开始产生值,直至比第二个值end小1的值为止。如range(5,10)表示5到9。
  • range(start, end, step):在第二个方法上有一个步进值step,是增量。如range(5,20,3)表示从5到17,每次以加3步进。
  • printnb():表示打印不换行。

推荐使用foreach语法。

4.5 return

return关键词有两方面的用途:

  • 一方面指定一个方法返回什么值(假设它没有void返回值)。
  • 另一方面它会导致当前的方法退出,并返回那个值。

改写上面的test()方法,使用return返回值:

package 第4章_控制执行流程.第5节_return;

public class IfElse2 {
    static int test(int testval, int target) {
        if (testval > target) {
            return +1;
        } else if (testval < target) {
            return -1;
        } else {
            return 0;
        }
    }

    public static void main(String[] args) {
        System.out.println(test(10, 5));
        System.out.println(test(5, 10));
        System.out.println(test(5, 5));
    }
}
/**
 * 打印结果:
 * 1
 * -1
 * 0
 */

注意:如果在返回void的方法中没有return语句,那么在该方法的结尾处会有一个隐式的return,因此在方法中并非总是必须要有一个return语句。但是,如果一个方法声明它将返回void之外的其他东西,那么必须确保每一条代码路径都将返回一个值。

4.6 break和continue

break和continue语句用于迭代语句中控制循环的流程,其中

  • break用于强制退出循环,不执行循环中剩余的语句。
  • continue则停止执行当前的迭代,然后退出循环起始处,开始下一次迭代。
package 第4章_控制执行流程.第6节_break和continue;

import static net.mindview.util.Range.range;

public class BreakAndContinue {
    public static void main(String[] args) {
        // 1.break和continue在for中的使用
        for (int i = 0; i < 100; i++) {
            if (i == 74) break;// 直接退出循环,不再执行剩余语句
            if (i % 9 != 0) continue; // 跳出当前循环,执行下一次迭代
            System.out.print(i + " ");
        }
        System.out.println();

        // 2.break和continue在foreach中的使用
        for (int i : range(100)) {
            if (i == 74) break;// 直接退出循环,不再执行剩余语句
            if (i % 9 != 0) continue; // 跳出当前循环,执行下一次迭代
            System.out.print(i + " ");
        }
        System.out.println();

        // 3.break和continue在while中的使用
        int i = 0;
        while (true) {
            i++;
            int j = i * 27;
            if (j == 1269) break; // 直接退出循环,不再执行剩余语句
            if (i % 10 != 0) continue; // 跳出当前循环,执行下一次迭代
            System.out.print(i + " ");
        }
    }
}
/**
 * 打印结果:
 * 0 9 18 27 36 45 54 63 72
 * 0 9 18 27 36 45 54 63 72
 * 10 20 30 40
 */

代码解释:在这个for循环中,i的值永远不会达到100,因为一旦i到达74,break语句就会中断循环。通常,只有在不知道中断条件何时满足时,才需要这样使用break。只要i不能被9整除,continue语句就会使执行过程返回到循环的最开头〈(这使i值递增)。如果能够整除,则将值显示出来。

无穷循环的两种形式:while(true)for(;;)

4.7 臭名昭著的goto

goto起源于汇编语言的程序控制:“若条件A成立,则跳到这里,否则跳到那里”。goto是源码级别的跳转,所以若一个程序总数从一个地方跳转到另一个地方,不方便识别程序的控制流程。

下面是把break和continue纳入一起讨论,因为使用了相同的机制:标签。

标签是后面跟有冒号的标识符。

label1:

在Java中,标签起作用的唯一的地方刚好是在迭代语句之前。由于break和continue关键词通常只中断当前循环,但若随同标签一起使用,它们就会中断循环,直到标签所在的地方:

label1:
outer-iteration {
    inner-iteration {
        //...
        break; // (1)
        //...
        continue; // (2)
        //...
        continue label1; // (3)
        //...
        break label1; // (4)
    }
}

代码解释:

  • 在(1)中,break中断内部迭代,回到外部迭代。

  • 在(2)中,continue使执行点移回内部迭代的起始处。

  • 在(3)中,continue label1同时中断内部迭代以及外部迭代,直接转到label1处;随后,它实际上是继续迭代过程,但却从外部迭代开始。

  • 在(4)中,break label1也会中断所有迭代,并回到label1处,但并不重新进入迭代。也就是说,它实际是完全中止了两个迭代。

标签在for循环中的使用:

package 第4章_控制执行流程.第7节_臭名昭著的goto;

public class LabeledFor {
    public static void main(String[] args) {
        int i = 0;
        outer:
// 不能有其他语句在后面,这是一个标签
        for (; true; ) {// 无限循环
            inner:
            // 第二个标签,不能有其他语句在这里
            for (; i < 10; i++) {
                System.out.println("i = " + i);
                if (i == 2) {
                    System.out.println("continue");
                    continue;
                }
                if (i == 3) {
                    System.out.println("break");
                    i++;
                    break;
                }
                if (i == 7) {
                    System.out.println("continue outer");
                    i++;
                    continue outer;
                }
                if (i == 8) {
                    System.out.println("break outer");
                    break outer;
                }
                for (int k = 0; k < 5; k++) {
                    if (k == 3) {
                        System.out.println("continue inner");
                        continue inner;
                    }
                }
            }
        }
    }
}
/**
 * 打印结果:
 * i = 0
 * continue inner
 * i = 1
 * continue inner
 * i = 2
 * continue
 * i = 3
 * break
 * i = 4
 * continue inner
 * i = 5
 * continue inner
 * i = 6
 * continue inner
 * i = 7
 * continue outer
 * i = 8
 * break outer
 */

如果没有break outer语句,就没有办法从内部循环跳出外部循环,因为break和continue只能中断最内层的循环,所以这也是break outer的用处。

如果想在中断循环的同时退出,那么使用return即可。

标签在while循环中的使用:

package 第4章_控制执行流程.第7节_臭名昭著的goto;

public class LabeledWhile {
    public static void main(String[] args) {
        int i = 0;
        outer:
        // 设置一个外部标签
        while (true) {// 无限循环
            System.out.println("Outer while loop");
            while (true) {// 嵌套的无限循环
                i++;
                System.out.println("i = " + i);
                if (i == 1) {
                    System.out.println("continue");
                    continue;// 当i==1时,跳出本次循环,开始下一次迭代
                }
                if (i == 3) {
                    System.out.println("continue outer");
                    continue outer;// 当i==3时,跳到outer外部循环处,开始下一次迭代
                }
                if (i == 5) {
                    System.out.println("break");
                    break;// 当i==5时,跳出当前循环,不再执行后面的语句,只是跳出了内部循环
                }
                if (i == 7) {
                    System.out.println("break outer");
                    break outer;// 当i==7时,跳到outer处,并不再执行后面语句,中断两层循环
                }
            }
        }
    }
}
/**
 * 打印结果:
 * Outer while loop
 * i = 1
 * continue
 * i = 2
 * i = 3
 * continue outer
 * Outer while loop
 * i = 4
 * i = 5
 * break
 * Outer while loop
 * i = 6
 * i = 7
 * break outer
 */

总结

  • 一般的continue会退回最内层循环的开头(顶部),并继续执行。

  • 带标签的continue会到达标签的位置,并重新进入紧接在那个标签后面的循环。

  • 一般的break会中断并跳出当前循环。

  • 带标签的break会中断并跳出标签所指的循环。

注意:之所以使用标签就是因为有嵌套循环的存在,而普通的break和continue不能跳出多层嵌套。

4.8 switch

switch是根据整数表达式的值,可以从一系列代码中选择一段去执行,基本格式如下:

switch(integral-selector) {
    case integral-value1:
        statement;
        break;
    case integral-value2:
        statement;
        break;
    case integral-value3:
        statement;
        break;
    ...
    default:
        statement;
}

其中integral-selector是一个能够产生整数值的表达式,switch会将这个表达式的结果与每个integral-value进行比较,如果相符则执行对应的语句,如果不相符则执行default语句。

其中break会使执行流程跳转至switch主体的末尾。但实际上break是可选的,如果省略break,那么会继续执行后面的case语句,直到遇到一个break为止。而default语句后面没有break,因为执行流程已经到了break的跳转目的地,当然也可以在default后面放置一个break,没有任何实际意义。

package 第4章_控制执行流程.第8节_switch;

public class SwitchTest {
    public static void main(String[] args) {
        int a = 2;

        // 使用break的情况
        System.out.println("使用break的情况: ");
        switch (a) {
            case 1:
                System.out.println(1);
                break;
            case 2:
                System.out.println(2);
                break;
            case 3:
                System.out.println(3);
                break;
            default:
                System.out.println("匹配失败");
        }

        // 不使用break的情况
        System.out.println("不使用break的情况: ");
        switch (a) {
            case 1:
                System.out.println(1);
            case 2:
                System.out.println(2);
            case 3:
                System.out.println(3);
            default:
                System.out.println("匹配失败");
        }
    }
}
/**
 * 打印结果:
 * 使用break的情况:
 * 2
 * 不使用break的情况:
 * 2
 * 3
 * 匹配失败
 */

switch语句需要使用一个选择因子,可以是int或char那样的整数值,现在也可以是字符串值。

package 第4章_控制执行流程.第8节_switch;

import java.util.Random;

import static net.mindview.util.Print.print;
import static net.mindview.util.Print.printnb;

public class VowelsAndConsonants {
    public static void main(String[] args) {
        Random rand = new Random(47);
        for (int i = 0; i < 100; i++) {
            int c = rand.nextInt(26) + 'a';
            printnb((char) c + ", " + c + ": ");
            switch (c) {
                case 'a':
                case 'e':
                case 'i':
                case 'o':
                case 'u':
                    print("vowel");
                    break;
                case 'y':
                case 'w':
                    print("Sometimes a vowel");
                    break;
                default:
                    print("consonant");
            }
        }
    }
}
/**
 * 打印结果:
 * y, 121: Sometimes a vowel
 * n, 110: consonant
 * z, 122: consonant
 * b, 98: consonant
 * r, 114: consonant
 * n, 110: consonant
 * y, 121: Sometimes a vowel
 * g, 103: consonant
 * c, 99: consonant
 * f, 102: consonant
 * o, 111: vowel
 * w, 119: Sometimes a vowel
 * z, 122: consonant
 */

注意:case语句进行了堆叠,即只要满足多种条件中的一个,就执行那段代码。

以上是关于《Java编程思想》阅读笔记之第4章-控制执行流程的主要内容,如果未能解决你的问题,请参考以下文章

《Java编程思想》读书笔记之第3章-操作符

[读书笔记]Java编程思想

《Java编程思想》读书笔记之第8章-多态

《Java编程思想》读书笔记之第7章-复用类

[读书笔记]Java编程思想第4章之控制执行流程

Java超简明入门学习笔记