X-Spirit的陋室铭

生活有的时候面临许多选择,这些选择让人困惑。 生活有的时候没有任何选择,这个时候让人压抑。 但无论生活有没有给你选择,你只能做一件事情: 排除困难,勇往直前!

2007年9月30日星期日

3.8 Control Flow 控制流

Control Flow 控制流

Java, like any programming language, supports both conditional statements and loops to determine control flow. We start with the conditional statements and then move on to loops. We end with the somewhat cumbersome switch statement that you can use when you have to test for many values of a single expression.

本节介绍条件语句和循环语句,最后介绍用于检测单一表达式的多个值的开关语句。

C++ NOTE


The Java control flow constructs are identical to those in C and C++, with a few exceptions. There is no goto, but there is a "labeled" version of break that you can use to break out of a nested loop (where you perhaps would have used a goto in C). Finally. JDK 5.0 adds a variant of the for loop that has no analog in C or C++. It is similar to the foreach loop in C#.

Java控制流结构和C以及C++是相同的,有些例外。没有goto语句 ,但是有加标签的break语句,你可以跳出嵌套循环(在C中你可能使用goto语句)。最后,JDK5.0增加了一个for循环的变体,该变体在CC++中没有类似。它和C#中的foreach循环相似。


Block Scope 块作用域

Before we get into the actual control structures, you need to know more about blocks.

A block or compound statement is any number of simple Java statements that are surrounded by a pair of braces. Blocks define the scope of your variables. Blocks can be nested inside another block. Here is a block that is nested inside the block of the main method.

一个块或者复合语句是由一对花括号括起来的许多简单Java语句。块定义了你变量的作用范围。块可以内嵌在另一个块中。这里有一个内嵌在main方法块中的块。

public static void main(String[] args)

{

int n;

. . .

{

int k;

. . .

} // k is only defined up to here

}


However, you may not declare identically named variables in two nested blocks. For example, the following is an error and will not compile:

但是不要再嵌套的两个块中定义同名的变量,这样会导致编译出错。

public static void main(String[] args)

{

int n;

. . .

{

int k;

int n; // error--can't redefine n in inner block

. . .

}

}


C++ NOTE


In C++, it is possible to redefine a variable inside a nested block. The inner definition then shadows the outer one. This can be a source of programming errors; hence, Java does not allow it.

C++中,在嵌套块中定义同名变量是可能的。内部定义可能覆盖掉外部定义。这可能导致程序错误,因此Java不允许该情况。


Conditional Statements 条件语句

The conditional statement in Java has the form

Java中的条件语句有如下形式:if (条件) 语句


if (condition) statement

The condition must be surrounded by parentheses.

条件必须被圆括号括起来。

In Java, as in most programming languages, you will often want to execute multiple statements when a single condition is true. In this case, you use a block statement that takes the form

Java中,和在多数编程语言中一样,你经常需要在一个条件为真时执行多条语句。此时,你需要使用一个块语句来使之清晰明了


{
statement
1
statement
2
. . .
}

For example:例如

if (yourSales >= target)

{

performance = "Satisfactory";

bonus = 100;

}


In this code all the statements surrounded by the braces will be executed when yourSales is greater than or equal to target. (See Figure 3-8.)

在这段代码中,当yourSales大于或等于target时,括号中的语句将被执行。

Figure 3-8. Flowchart for the if statement


NOTE


A block (sometimes called a compound statement) allows you to have more than one (simple) statement in any Java programming structure that might otherwise have a single (simple) statement.

一个块有时候也可允许你在其中放置一条语句,或者也可放置一条以上的语句。


The more general conditional in Java looks like this (see Figure 3-9):
Java
中更常见的条件判断如下
if (condition) statement1 else statement2

Figure 3-9. Flowchart for the if/else statement


For example:例如

if (yourSales >= target)

{

performance = "Satisfactory";

bonus = 100 + 0.01 * (yourSales - target);

}

else

{

performance = "Unsatisfactory";

bonus = 0;

}


The else part is always optional. An else groups with the closest if. Thus, in the statement
else
部分总是可选的。一个else部分总是紧跟最近的if。因此下面的语句中

if (x <= 0) if (x == 0) sign = 0; else sign = -1;


the else belongs to the second if.
else
部分属于第二个if

Repeated if . . . else if . . . alternatives are common (see Figure 3-10). For example:
重复的if…else if …交替是很常见的(参见图3-10)。例如:

if (yourSales >= 2 * target)

{

performance = "Excellent";

bonus = 1000;

}

else if (yourSales >= 1.5 * target)

{

performance = "Fine";

bonus = 500;

}

else if (yourSales >= target)

{

performance = "Satisfactory";

bonus = 100;

}

else

{

System.out.println("You're fired");

}


Figure 3-10. Flowchart for the if/else if (multiple branches)


Loops 循环

The while loop executes a statement (which may be a block statement) while a condition is TRue. The general form is
当条件判断为真时,while循环执行循环体内的语句。通式如下:
while (condition) statement

The while loop will never execute if the condition is false at the outset (see Figure 3-11).
如果条件判断在最开始时就为false,则while循环永远不会执行。(图3-11

Figure 3-11. Flowchart for the while statement


The program in Example 3-3 determines how long it will take to save a specific amount of money for your well-earned retirement, assuming that you deposit the same amount of money per year and that the money earns a specified interest rate.
程序Example3-3将求出为你正式退休存定量的钱要花多长时间,假设你每年定量存款,并且这些钱有一个指定的利率。

In the example, we are incrementing a counter and updating the amount currently accumulated in the body of the loop until the total exceeds the targeted amount.

在这个例子中,我们使一个计数器自增并更新当前循环体内的积累的总和,直到总量超过目标量。

while (balance < goal)

{

balance += payment;

double interest = balance * interestRate / 100;

balance += interest;

years++;

}

System.out.println(years + " years.");


(Don't rely on this program to plan for your retirement. We left out a few niceties such as inflation and your life expectancy.)
(不要依靠这个程序来为你的退休生活做预算。我们忽略了一些细节,例如通货膨胀和你的预期寿命。)

A while loop tests at the top. Therefore, the code in the block may never be executed. If you want to make sure a block is executed at least once, you will need to move the test to the bottom. You do that with the do/while loop. Its syntax looks like this:
while
循环在开始时做判断。因此,循环体内的代码可能永远不会执行。如果你希望保证块内的代码至少执行一次,你需要将检测移动到底部。你可以用do/while循环来做,语句如下:


do statement while (condition);

This loop executes the statement (which is typically a block) and only then tests the condition. It then repeats the statement and retests the condition, and so on. The code in Example 3-4 computes the new balance in your retirement account and then asks if you are ready to retire:
这个循环首先执行语句(这些语句是一个典型的块结构),仅当执行完后才进行条件判断。之后再重复语句和执行判断,如此循环往复。例子3-4中的代码计算了你退休帐户中的新余额,并问你是否准备退休。

do

{

balance += payment;

double interest = balance * interestRate / 100;

balance += interest;

year++;

// print current balance

. . .

// ask if ready to retire and get input

. . .

}

while (input.equals("N"));


As long as the user answers "N", the loop is repeated (see Figure 3-12). This program is a good example of a loop that needs to be entered at least once, because the user needs to see the balance before deciding whether it is sufficient for retirement.
只要用户回答”N”,循环就重复(看图3-12)。这个程序是至少需要进入一次的循环的一个好例子,因为用户需要在判断帐户余额是否足够退休前就看到余额情况。

Example 3-3. Retirement.java

1. import java.util.*;

2.

3. public class Retirement

4. {

5. public static void main(String[] args)

6. {

7. // read inputs

8. Scanner in = new Scanner(System.in);

9.

10. System.out.print("How much money do you need to retire? ");

11. double goal = in.nextDouble();

12.

13. System.out.print("How much money will you contribute every year? ");

14. double payment = in.nextDouble();

15.

16. System.out.print("Interest rate in %: ");

17. double interestRate = in.nextDouble();

18.

19. double balance = 0;

20. int years = 0;

21.

22. // update account balance while goal isn't reached

23. while (balance < goal)

24. {

25. // add this year's payment and interest

26. balance += payment;

27. double interest = balance * interestRate / 100;

28. balance += interest;

29. years++;

30. }

31.

32. System.out.println("You can retire in " + years + " years.");

33. }

34. }


Example 3-4. Retirement2.java

1. import java.util.*;

2.

3. public class Retirement2

4. {

5. public static void main(String[] args)

6. {

7. Scanner in = new Scanner(System.in);

8.

9. System.out.print("How much money will you contribute every year? ");

10. double payment = in.nextDouble();

11.

12. System.out.print("Interest rate in %: ");

13. double interestRate = in.nextDouble();

14.

15. double balance = 0;

16. int year = 0;

17.

18. String input;

19.

20. // update account balance while user isn't ready to retire

21. do

22. {

23. // add this year's payment and interest

24. balance += payment;

25. double interest = balance * interestRate / 100;

26. balance += interest;

27.

28. year++;

29.

30. // print current balance

31. System.out.printf("After year %d, your balance is %,.2f%n", year, balance);

32.

33. // ask if ready to retire and get input

34. System.out.print("Ready to retire? (Y/N) ");

35. input = in.next();

36. }

37. while (input.equals("N"));

38. }

39.}


Figure 3-12. Flowchart for the do/while statement


Determinate Loops 确定性循环

The for loop is a general construct to support iteration that is controlled by a counter or similar variable that is updated after every iteration. As Figure 3-13 shows, the following loop prints the numbers from 1 to 10 on the screen.
for
循环是一个常见的结构,该结构支持由计数器或者简单变量控制的迭代,这些计数器和变量在每次迭代完成后都会更新。如图3-13所示,下面的循环在屏幕上输出110

for (int i = 1; i <= 10; i++)

System.out.println(i);


Figure 3-13. Flowchart for the for statement


The first slot of the for statement usually holds the counter initialization. The second slot gives the condition that will be tested before each new pass through the loop, and the third slot explains how to update the counter.
for
语句的第一个空隙通常写入计数器初始化。第二个空隙给出每轮循环之前都要进行的条件判断,第三个空隙说明了如何更新计数器的值。

Although Java, like C++, allows almost any expression in the various slots of a for loop, it is an unwritten rule of good taste that the three slots of a for statement should only initialize, test, and update the same counter variable. One can write very obscure loops by disregarding this rule.
虽然JavaC++一样,允许for循环中的各个空隙中的任何表达式,但是for循环的三个空隙应当是初始化、判断、更新相同变量,这已经成了一条不成文的规定。忽视这条规则将导致循环语句晦涩难懂。

Even within the bounds of good taste, much is possible. For example, you can have loops that count down:
可以写出倒数的循环


for (int i = 10; i > 0; i--)

System.out.println("Counting down . . . " + i);

System.out.println("Blastoff!");


CAUTION


Be careful about testing for equality of floating-point numbers in loops. A for loop that looks like this

for (double x = 0; x != 10; x += 0.1) . . .


may never end. Because of roundoff errors, the final value may not be reached exactly. For example, in the loop above, x jumps from 9.99999999999998 to 10.09999999999998 because there is no exact binary representation for 0.1.

由于没有精确的0.1的值,使用浮点数字作为循环变量可能导致死循环。


When you declare a variable in the first slot of the for statement, the scope of that variable extends until the end of the body of the for loop.
循环变量的作用范围是整个循环体

for (int i = 1; i <= 10; i++)

{

. . .

}

// i no longer defined here


In particular, if you define a variable inside a for statement, you cannot use the value of that variable outside the loop. Therefore, if you wish to use the final value of a loop counter outside the for loop, be sure to declare it outside the loop header!
如果你希望循环变量的值能够在循环体外继续使用,请在for循环之前定义该变量:

int i;

for (i = 1; i <= 10; i++)

{

. . .

}

// i still defined here


On the other hand, you can define variables with the same name in separate for loops:
在两个独立的for循环中可以定义同名循环变量:

for (int i = 1; i <= 10; i++)

{

. . .

}

. . .

for (int i = 11; i <= 20; i++) // ok to define another variable named i

{

. . .

}


A for loop is merely a convenient shortcut for a while loop. For example,
for
循环不过仅是while循环的简便形式,例如

for (int i = 10; i > 0; i--)

System.out.println("Counting down . . . " + i);


can be rewritten as可以写成

int i = 10;

while (i > 0)

{

System.out.println("Counting down . . . " + i);

i--;

}


Example 3-5 shows a typical example of a for loop.
例子3-5展示了一个for循环的典型例子

The program computes the odds on winning a lottery. For example, if you must pick 6 numbers from the numbers 1 to 50 to win, then there are (50 x 49 x 48 x 47 x 46 x 45)/(1 x 2 x 3 x 4 x 5 x 6) possible outcomes, so your chance is 1 in 15,890,700. Good luck!
这个程序计算了赢得彩票的几率。例如,你从150中选出6个数字,就有(50 x 49 x 48 x 47 x 46 x 45)/(1 x 2 x 3 x 4 x 5 x 6)中可能的选法,所以你的机会是15,890,700分之1。祝你好运。。。

In general, if you pick k numbers out of n, there are
通常,如果你从n中选出k个数字,就有


possible outcomes. The following for loop computes this value:
种可能的选法。下面的for循环计算了如下的值:

int lotteryOdds = 1;

for (int i = 1; i <= k; i++)

lotteryOdds = lotteryOdds * (n - i + 1) / i;


NOTE


See page 82 for a description of the "generalized for loop" (also called "for each" loop) that was added to the Java language in JDK 5.0.
在数组一节中还将介绍JDK 5中新增的for each循环


Example 3-5. LotteryOdds.java

1. import java.util.*;

2.

3. public class LotteryOdds

4. {

5. public static void main(String[] args)

6. {

7. Scanner in = new Scanner(System.in);

8.

9. System.out.print("How many numbers do you need to draw? ");

10. int k = in.nextInt();

11.

12. System.out.print("What is the highest number you can draw? ");

13. int n = in.nextInt();

14.

15. /*

16. compute binomial coefficient

17. n * (n - 1) * (n - 2) * . . . * (n - k + 1)

18. -------------------------------------------

19. 1 * 2 * 3 * . . . * k

20. */

21.

22. int lotteryOdds = 1;

23. for (int i = 1; i <= k; i++)

24. lotteryOdds = lotteryOdds * (n - i + 1) / i;

25.

26. System.out.println("Your odds are 1 in " + lotteryOdds + ". Good luck!");

27. }

28. }


Multiple Selections—The switch Statement

The if/else construct can be cumbersome when you have to deal with multiple selections with many alternatives. Java has a switch statement that is exactly like the switch statement in C and C++, warts and all.
事实上,Java也有CC++中的switch语句 。

For example, if you set up a menuing system with four alternatives like that in Figure 3-14, you could use code that looks like this:

Scanner in = new Scanner(System.in);

System.out.print("Select an option (1, 2, 3, 4) ");

int choice = in.nextInt();

switch (choice)

{

case 1:

. . .

break;

case 2:

. . .

break;

case 3:

. . .

break;

case 4:

. . .

break;

default:

// bad input

. . .

break;

}


Figure 3-14. Flowchart for the switch statement


Execution starts at the case label that matches the value on which the selection is performed and continues until the next break or the end of the switch. If none of the case labels match, then the default clause is executed, if it is present.

如果不加入break语句,则会跳到下一个分支。但是default语句块是都不匹配时执行的。

Note that the case labels must be integers or enumerated constants. You cannot test strings. For example, the following is an error:
case
后面必须是整数或者枚举类型。不可以是字符串

String input = . . .;

switch (input) // ERROR

{

case "A": // ERROR

. . .

break;

. . .

}


PITFALL缺陷


It is possible for multiple alternatives to be triggered. If you forget to add a break at the end of an alternative, then execution falls through to the next alternative! This behavior is plainly dangerous and a common cause for errors. For that reason, we never use the switch statement in our programs.
由于忘记加入break语句可能导致执行下一个分支,所以不建议在程序中使用switch语句。


Statements That Break Control Flow

Although the designers of Java kept the goto as a reserved word, they decided not to include it in the language. In general, goto statements are considered poor style. Some programmers feel the anti-goto forces have gone too far (see, for example, the famous article of Donald Knuth called "Structured Programming with goto statements"). They argue that unrestricted use of goto is error prone but that an occasional jump out of a loop is beneficial. The Java designers agreed and even added a new statement, the labeled break, to support this programming style.

尽管goto语句可能破坏程序结构,但是我们还是偶尔需要跳出循环,因此Java的设计师们加入了标签式Break

Let us first look at the unlabeled break statement. The same break statement that you use to exit a switch can also be used to break out of a loop. For example,以下是不加标签的break语句。用于退出switchbreak语句也可以退出一个循环。

while (years <= 100)

{

balance += payment;

double interest = balance * interestRate / 100;

balance += interest;

if (balance >= goal) break;

years++;

}


Now the loop is exited if either years > 100 occurs at the top of the loop or balance >= goal occurs in the middle of the loop. Of course, you could have computed the same value for years without a break, like this:
以上程序段也可写成如下这个样子

while (years <= 100 && balance < goal)

{

balance += payment;

double interest = balance * interestRate / 100;

balance += interest;

if (balance < goal)

years++;

}


But note that the test balance < goal is repeated twice in this version. To avoid this repeated test, some programmers prefer the break statement.
但是请注意balance<goal这个判断在这个版本中重复了两次。要避免重复判断,很多程序员更倾向于使用break语句。

Unlike C++, Java also offers a labeled break statement that lets you break out of multiple nested loops. Occasionally something weird happens inside a deeply nested loop. In that case, you may want to break completely out of all the nested loops. It is inconvenient to program that simply by adding extra conditions to the various loop tests.
C++不同,Java也包含标签式break语句可以使你跳出多层嵌套循环。

Here's an example that shows the break statement at work. Notice that the label must precede the outermost loop out of which you want to break. It also must be followed by a colon.
下面是一个使用break语句的例子。注意到标签必须放在你想要跳出的循环的前面。

Scanner in = new Scanner(System.in);

int n;

read_data:

while (. . .) // this loop statement is tagged with the label

{

. . .

for (. . .) // this inner loop is not labeled

{

System.out.print("Enter a number >= 0: ");

n = in.nextInt();

if (n < 0) // should never happen—can't go on

break read_data;

// break out of read_data loop

. . .

}

}

// this statement is executed immediately after the labeled break

if (n < 0) // check for bad situation

{

// deal with bad situation

}

else

{

// carry out normal processing

}


If there was a bad input, the labeled break moves past the end of the labeled block. As with any use of the break statement, you then need to test whether the loop exited normally or as a result of a break.
如果输入有误,标签式的break语句跳转到标签的块之后执行。由于使用了break语句,你需要检测循环是否正常退出或者是否达到break的目的。

NOTE


Curiously, you can apply a label to any statement, even an if statement or a block statement, like this:
有趣的是,你可以对任何语句应用标签,即使是if语句或是语句体,如下:


label
:
{
. . .
if (condition) break label; // exits block
. . .
}
// jumps here when the break statement executes

Thus, if you are lusting after a goto and if you can place a block that ends just before the place to which you want to jump, you can use a break statement! Naturally, we don't recommend this approach. Note, however, that you can only jump out of a block, never into a block.
因此,如果你希望使用goto跳转,或者你希望在一个将一个语句体放置在你想跳转的点之前,你可以使用break语句。当然,我们不推荐此方法。注意,你只能跳出一个块,而不能跳出一个块。


Finally, there is a continue statement that, like the break statement, breaks the regular flow of control. The continue statement transfers control to the header of the innermost enclosing loop. Here is an example:
最后,有一种continue语句,和break语句一样,可以打破规则的控制流。continue语句将控制转向最内层的封闭循环的起始处。下面是个例子:

Scanner in = new Scanner(System.in);

while (sum < goal)

{

System.out.print("Enter a number: ");

n = in.nextInt();

if (n < 0) continue;

sum += n; // not executed if n < 0

}


If n < 0, then the continue statement jumps immediately to the loop header, skipping the remainder of the current iteration.
如果n<0,则continue语句立刻跳过当前迭代的剩余部分,转向循环的顶部。

If the continue statement is used in a for loop, it jumps to the "update" part of the for loop. For example, consider this loop.
如果continue语句用于for循环,则其跳转到for循环的“更新”部分,例如:

for (count = 1; count <= 100; count++)

{

System.out.print("Enter a number, -1 to quit: ");

n = in.nextInt();

if (n < 0) continue;

sum += n; // not executed if n < 0

}


If n < 0, then the continue statement jumps to the count++ statement.
如果n<0,则continue语句跳转到count++语句。

There is also a labeled form of the continue statement that jumps to the header of the loop with the matching label.
也有加标签的continue语句可以跳转到指定标签所指向的循环的头部。


TIP


Many programmers find the break and continue statements confusing. These statements are entirely optional—you can always express the same logic without them. In this book, we never use break or continue.
如果你对breakcontinue语句感到困惑,你可以不使用他们。本书中,我们不会使用它们的。


没有评论: