控制流程语句

控制流程语句用于控制程序按照一定流程来执行。

if-then

它告诉你要只有 if 后面是 true 时才执行特定的代码。

  1. void applyBrakes() {
  2. // the "if" clause: bicycle must be moving
  3. if (isMoving){
  4. // the "then" clause: decrease current speed
  5. currentSpeed--;
  6. }
  7. }

如果 if 后面是 false, 则跳到 if-then 语句后面。语句可以省略中括号,但在编码规范里面不推荐使用,如:

  1. void applyBrakes() {
  2. // same as above, but without braces
  3. if (isMoving)
  4. currentSpeed--;
  5. }

if-then-else

该语句是在 if 后面是 false 时,提供了第二个执行路径。

  1. void applyBrakes() {
  2. if (isMoving) {
  3. currentSpeed--;
  4. } else {
  5. System.err.println("The bicycle has already stopped!");
  6. }
  7. }

下面是一个完整的例子:

  1. class IfElseDemo {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. int testscore = 76;
  7. char grade;
  8. if (testscore >= 90) {
  9. grade = 'A';
  10. } else if (testscore >= 80) {
  11. grade = 'B';
  12. } else if (testscore >= 70) {
  13. grade = 'C';
  14. } else if (testscore >= 60) {
  15. grade = 'D';
  16. } else {
  17. grade = 'F';
  18. }
  19. System.out.println("Grade = " + grade);
  20. }
  21. }

输出为:Grade = C

switch

switch 语句可以有许多可能的执行路径。可以使用 byte, short, char, 和 int 基本数据类型,也可以是枚举类型(enumerated types)、String 以及少量的原始类型的包装类 Character, Byte, Short, 和 Integer。

下面是一个 SwitchDemo 例子:

  1. class SwitchDemo {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. int month = 8;
  7. String monthString;
  8. switch (month) {
  9. case 1:
  10. monthString = "January";
  11. break;
  12. case 2:
  13. monthString = "February";
  14. break;
  15. case 3:
  16. monthString = "March";
  17. break;
  18. case 4:
  19. monthString = "April";
  20. break;
  21. case 5:
  22. monthString = "May";
  23. break;
  24. case 6:
  25. monthString = "June";
  26. break;
  27. case 7:
  28. monthString = "July";
  29. break;
  30. case 8:
  31. monthString = "August";
  32. break;
  33. case 9:
  34. monthString = "September";
  35. break;
  36. case 10:
  37. monthString = "October";
  38. break;
  39. case 11:
  40. monthString = "November";
  41. break;
  42. case 12:
  43. monthString = "December";
  44. break;
  45. default:
  46. monthString = "Invalid month";
  47. break;
  48. }
  49. System.out.println(monthString);
  50. }
  51. }

break 语句是为了防止 fall through。

  1. class SwitchDemoFallThrough {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. java.util.ArrayList<String> futureMonths = new java.util.ArrayList<String>();
  7. int month = 8;
  8. switch (month) {
  9. case 1:
  10. futureMonths.add("January");
  11. case 2:
  12. futureMonths.add("February");
  13. case 3:
  14. futureMonths.add("March");
  15. case 4:
  16. futureMonths.add("April");
  17. case 5:
  18. futureMonths.add("May");
  19. case 6:
  20. futureMonths.add("June");
  21. case 7:
  22. futureMonths.add("July");
  23. case 8:
  24. futureMonths.add("August");
  25. case 9:
  26. futureMonths.add("September");
  27. case 10:
  28. futureMonths.add("October");
  29. case 11:
  30. futureMonths.add("November");
  31. case 12:
  32. futureMonths.add("December");
  33. break;
  34. default:
  35. break;
  36. }
  37. if (futureMonths.isEmpty()) {
  38. System.out.println("Invalid month number");
  39. } else {
  40. for (String monthName : futureMonths) {
  41. System.out.println(monthName);
  42. }
  43. }
  44. }
  45. }

输出为:

  1. August
  2. September
  3. October
  4. November
  5. December

技术上来说,最后一个 break 并不是必须,因为流程跳出 switch 语句。但仍然推荐使用 break ,主要修改代码就会更加简单和防止出错。default 处理了所有不明确值的情况。

下面例子展示了一个局域多个 case 的情况,

  1. class SwitchDemo2 {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. int month = 2;
  7. int year = 2000;
  8. int numDays = 0;
  9. switch (month) {
  10. case 1:
  11. case 3:
  12. case 5:
  13. case 7:
  14. case 8:
  15. case 10:
  16. case 12:
  17. numDays = 31;
  18. break;
  19. case 4:
  20. case 6:
  21. case 9:
  22. case 11:
  23. numDays = 30;
  24. break;
  25. case 2:
  26. if (((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0))
  27. numDays = 29;
  28. else
  29. numDays = 28;
  30. break;
  31. default:
  32. System.out.println("Invalid month.");
  33. break;
  34. }
  35. System.out.println("Number of Days = " + numDays);
  36. }
  37. }

输出为:Number of Days = 29

使用 String

Java SE 7 开始,可以在 switch 语句里面使用 String,下面是一个例子

  1. class StringSwitchDemo {
  2. public static int getMonthNumber(String month) {
  3. int monthNumber = 0;
  4. if (month == null) {
  5. return monthNumber;
  6. }
  7. switch (month.toLowerCase()) {
  8. case "january":
  9. monthNumber = 1;
  10. break;
  11. case "february":
  12. monthNumber = 2;
  13. break;
  14. case "march":
  15. monthNumber = 3;
  16. break;
  17. case "april":
  18. monthNumber = 4;
  19. break;
  20. case "may":
  21. monthNumber = 5;
  22. break;
  23. case "june":
  24. monthNumber = 6;
  25. break;
  26. case "july":
  27. monthNumber = 7;
  28. break;
  29. case "august":
  30. monthNumber = 8;
  31. break;
  32. case "september":
  33. monthNumber = 9;
  34. break;
  35. case "october":
  36. monthNumber = 10;
  37. break;
  38. case "november":
  39. monthNumber = 11;
  40. break;
  41. case "december":
  42. monthNumber = 12;
  43. break;
  44. default:
  45. monthNumber = 0;
  46. break;
  47. }
  48. return monthNumber;
  49. }
  50. public static void main(String[] args) {
  51. String month = "August";
  52. int returnedMonthNumber = StringSwitchDemo.getMonthNumber(month);
  53. if (returnedMonthNumber == 0) {
  54. System.out.println("Invalid month");
  55. } else {
  56. System.out.println(returnedMonthNumber);
  57. }
  58. }
  59. }

输出为:8

注:switch 语句表达式中不能有 null。

while

while 语句在判断条件是 true 时执行语句块。语法如下:

  1. while (expression) {
  2. statement(s)
  3. }

while 语句计算的表达式,必须返回 boolean 值。如果表达式计算为 true,while 语句执行 while 块的所有语句。while 语句继续测试表达式,然后执行它的块,直到表达式计算为 false。完整的例子:

  1. class WhileDemo {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. int count = 1;
  7. while (count < 11) {
  8. System.out.println("Count is: " + count);
  9. count++;
  10. }
  11. }
  12. }

用 while 语句实现一个无限循环:

  1. while (true){
  2. // your code goes here
  3. }

do-while

语法如下:

  1. do {
  2. statement(s)
  3. } while (expression);

do-while 语句和 while 语句的区别是,do-while 计算它的表达式是在循环的底部,而不是顶部。所以,do 块的语句,至少会执行一次,如 DoWhileDemo 程序所示:

  1. class DoWhileDemo {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. int count = 1;
  7. do {
  8. System.out.println("Count is: " + count);
  9. count++;
  10. } while (count < 11);
  11. }
  12. }

输出为:

  1. Count is: 1
  2. Count is: 2
  3. Count is: 3
  4. Count is: 4
  5. Count is: 5
  6. Count is: 6
  7. Count is: 7
  8. Count is: 8
  9. Count is: 9
  10. Count is: 10

for

for 语句提供了一个紧凑的方式来遍历一个范围值。程序经常引用为”for 循环”,因为它反复循环,直到满足特定的条件。for 语句的通常形式,表述如下:

  1. for (initialization; termination;
  2. increment) {
  3. statement(s)
  4. }

使 for 语句时要注意:

  • initialization 初始化循环;它执行一次作为循环的开始。
  • 当 termination 计算为 false,循环结束。
  • increment 会在循环的每次迭代执行;该表达式可以接受递增或者递减的值
  1. class ForDemo {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. for(int i=1; i<11; i++){
  7. System.out.println("Count is: " + i);
  8. }
  9. }
  10. }

输出为:

  1. Count is: 1
  2. Count is: 2
  3. Count is: 3
  4. Count is: 4
  5. Count is: 5
  6. Count is: 6
  7. Count is: 7
  8. Count is: 8
  9. Count is: 9
  10. Count is: 10

注意:代码在 initialization 声明变量。该变量的存活范围,从它的声明到 for 语句的块的结束。所以,它可以用在 termination 和 increment。如果控制 for 语句的变量,不需要在循环外部使用,最好是在 initialization 声明。经常使用 i,j,k 经常用来控制 for 循环。在 initialization 声明他们,可以限制他们的生命周期,减少错误。

for 循环的三个表达式都是可选的,一个无限循环,可以这么写:

  1. // infinite loop
  2. for ( ; ; ) {
  3. // your code goes here
  4. }

for 语句还可以用来迭代 集合(Collections) 和 数组(arrays),这个形式有时被称为增强的 for 语句( enhanced for ),可以用来让你的循环更加紧凑,易于阅读。为了说明这一点,考虑下面的数组:

  1. int[] numbers = {1,2,3,4,5,6,7,8,9,10};

使用 增强的 for 语句来循环数组

  1. class EnhancedForDemo {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. int[] numbers =
  7. {1,2,3,4,5,6,7,8,9,10};
  8. for (int item : numbers) {
  9. System.out.println("Count is: " + item);
  10. }
  11. }
  12. }

输出:

  1. Count is: 1
  2. Count is: 2
  3. Count is: 3
  4. Count is: 4
  5. Count is: 5
  6. Count is: 6
  7. Count is: 7
  8. Count is: 8
  9. Count is: 9
  10. Count is: 10

尽可能使用这种形式的 for 替代传统的 for 形式。

break

break 语句有两种形式:标签和非标签。在前面的 switch 语句,看到的 break 语句就是非标签形式。可以使用非标签 break 用来结束 for,while,do-while 循环,如下面的 BreakDemo 程序:

  1. class BreakDemo {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. int[] arrayOfInts = { 32, 87, 3, 589, 12, 1076, 2000, 8, 622, 127 };
  7. int searchfor = 12;
  8. int i;
  9. boolean foundIt = false;
  10. for (i = 0; i < arrayOfInts.length; i++) {
  11. if (arrayOfInts[i] == searchfor) {
  12. foundIt = true;
  13. break;
  14. }
  15. }
  16. if (foundIt) {
  17. System.out.println("Found " + searchfor + " at index " + i);
  18. } else {
  19. System.out.println(searchfor + " not in the array");
  20. }
  21. }
  22. }

这个程序在数组终查找数字12。break 语句,当找到值时,结束 for 循环。控制流就跳转到 for 循环后面的语句。程序输出是:

  1. Found 12 at index 4

无标签 break 语句结束最里面的 switch,for,while,do-while 语句。而标签break 结束最外面的语句。接下来的程序,BreakWithLabelDemo,类似前面的程序,但使用嵌套循环在二维数组里寻找一个值。但值找到后,标签 break 语句结束最外面的 for 循环(标签为”search”):

  1. class BreakWithLabelDemo {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. int[][] arrayOfInts = { { 32, 87, 3, 589 }, { 12, 1076, 2000, 8 }, { 622, 127, 77, 955 } };
  7. int searchfor = 12;
  8. int i;
  9. int j = 0;
  10. boolean foundIt = false;
  11. search: for (i = 0; i < arrayOfInts.length; i++) {
  12. for (j = 0; j < arrayOfInts[i].length; j++) {
  13. if (arrayOfInts[i][j] == searchfor) {
  14. foundIt = true;
  15. break search;
  16. }
  17. }
  18. }
  19. if (foundIt) {
  20. System.out.println("Found " + searchfor + " at " + i + ", " + j);
  21. } else {
  22. System.out.println(searchfor + " not in the array");
  23. }
  24. }
  25. }

程序输出是:

  1. Found 12 at 1, 0

break 语句结束标签语句,它不是传送控制流到标签处。控制流传送到紧随标记(终止)声明。

注: Java 没有类似于 C 语言的 goto 语句,但带标签的 break 语句,实现了类似的效果。

continue

continue 语句忽略 for,while,do-while 的当前迭代。非标签模式,忽略最里面的循环体,然后计算循环控制的 boolean 表达式。接下来的程序,ContinueDemo,通过一个字符串的步骤,计算字母“p”出现的次数。如果当前字符不是 p,continue 语句跳过循环的其他代码,然后处理下一个字符。如果当前字符是 p,程序自增字符数。

  1. class ContinueDemo {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. String searchMe = "peter piper picked a " + "peck of pickled peppers";
  7. int max = searchMe.length();
  8. int numPs = 0;
  9. for (int i = 0; i < max; i++) {
  10. // interested only in p's
  11. if (searchMe.charAt(i) != 'p')
  12. continue;
  13. // process p's
  14. numPs++;
  15. }
  16. System.out.println("Found " + numPs + " p's in the string.");
  17. }
  18. }

程序输出:

  1. Found 9 p's in the string

为了更清晰看效果,尝试去掉 continue 语句,重新编译。再跑程序,count 将是错误的,输出是 35,而不是 9.

带标签的 continue 语句忽略标签标记的外层循环的当前迭代。下面的程序例子,ContinueWithLabelDemo,使用嵌套循环在字符传的字串中搜索字串。需要两个嵌套循环:一个迭代字串,一个迭代正在被搜索的字串。下面的程序ContinueWithLabelDemo,使用 continue 的标签形式,忽略最外层的循环。

  1. class ContinueWithLabelDemo {
  2. /**
  3. * @param args
  4. */
  5. public static void main(String[] args) {
  6. String searchMe = "Look for a substring in me";
  7. String substring = "sub";
  8. boolean foundIt = false;
  9. int max = searchMe.length() - substring.length();
  10. test: for (int i = 0; i <= max; i++) {
  11. int n = substring.length();
  12. int j = i;
  13. int k = 0;
  14. while (n-- != 0) {
  15. if (searchMe.charAt(j++) != substring.charAt(k++)) {
  16. continue test;
  17. }
  18. }
  19. foundIt = true;
  20. break test;
  21. }
  22. System.out.println(foundIt ? "Found it" : "Didn't find it");
  23. }
  24. }

这里是程序输出:

  1. Found it

return

最后的分支语句是 return 语句。return 语句从当前方法退出,控制流返回到方法调用处。return 语句有两种形式:一个是返回值,一个是不返回值。为了返回一个值,简单在 return 关键字后面把值放进去(或者放一个表达式计算)

  1. return ++count;

return 的值的数据类型,必须和方法声明的返回值的类型符合。当方法声明为 void,使用下面形式的 return 不需要返回值。

  1. return;

源码

本章例子的源码,可以在 com.waylau.essentialjava.flow 包下找到。