首页 > 代码库 > (翻译)《二》语言基础(4)--控制流语句

(翻译)《二》语言基础(4)--控制流语句

控制流语句

代码中的语句通常都是自上而下顺序执行。但是控制流语句打断执行的流程,利用决策、循环、分支,让你的程序有条件的执行代码的某一部分。本节描述java支持的决策语句(if-then,if-then-else,switch),循环语句(for,while,do-while),分支语句(break,continue,return)。

if-then和if-then-else语句

if-then是控制流语句中最基础的。它告诉你的程序执行某段代码,当某个特定的条件为真时。比如,Bicycle类会允许刹车降低自行车的速度,只有当自行车还在运动中。applyBrakes方法可能的实现如下:

void applyBrakes(){

  if(isMoving){

    currentSpeed--;

  }

}

如果条件为false(自行车没有移动),跳过if-then语句。

此外,如果只有一条语句,大括号是可选的。

void applyBrakes(){

  if(isMoving)

    currentSpeed--;

}

是否要省略大括号根据个人的风格。不过省略大括号使代码更脆弱。如果后面会在then分支中增加语句,一个常见的错误是忘记增加对应的大括号。编译器无法发现这类错误,你因此将会得到一个错误的结果。

if-then-else语句提供了另外的路径来执行,当if从句的结果是false。你可以在applyBrakes方法中使用if-then-else语句来执行一些动作,如果自行车在没有运动时使用刹车。本例中,将会打印一些错误信息说明自行车已经停下。

void applyBrakes(){

  if(isMoving){

    currentSpeed--;

  }else{

    System.err.println("bicycle has already stopped.");

  }

}

下面程序,根据得分分配等级,大于90是A,大于80是B,大于70是C,大于60是D,其他是F。

class IfElsedemo{

  public static void main(String[] args){

    int testScore = 79;

    char grade;

    if(testScore >= 90){

      grade = ‘A‘;

    }else if(testScore >= 80){

      grade = ‘B‘;

    }else if(testScore >= 70){

      grade = ‘C‘;

    }else if(testScore >= 60){

      grade = ‘D‘;

    }else {

      grade = ‘F‘;

    }

    System.out.println("grade is " + grade);

  }

}

输出为C。

你也许注意到testScore的值满足混合语句中不止一条表达式:79>70;79>60。但是当一个条件满足,那么对应的语句就会执行(grade = ‘C‘),剩下的条件语句就不会执行了。

switch语句

不像if-then和if-then-else语句,switch语句可以有很多可以执行的路径。switch和byte、short、int、char基础数据类型工作。它也可以和枚举类型、String类、一些封装了基础数据类型的专门的类:Character、Byte、Short、Integer一起工作。

下面代码,声明一个int型变量month,其值表示月份;然后根据swich语句,打印月份。

public class SwitchDemo{

  public static void main(String[] args){

    int month = 8;

    String monthStr;

    switch(month){

      case 1:monthStr = "January";

          break;

      case 2:monthStr = "February";

          break;

      case 3:monthStr = "March";

          break;

      case 4:monthStr = "April";

          break;

      case 5:monthStr = "May";

          break;

      case 6:monthStr = "June";

          break;

      case 7:monthStr = "July";

          break;

      case 8:monthStr = "August";

          break;

      case 9:monthStr = "September";

          break;

      case 10:monthStr = "October";

          break;

      case 11:monthStr = "November";

          break;

      case 12:monthStr = "December";

          break;

      default:monthStr = "Invalid month";

          break;

    }

    System.out.println(monthStr);

  }

}

输出是August。

switch语句体被称为switch块。switch块中可以有多个case标记和一个default标记的语句。switch语句计算表达式,执行跟在对应case标记后的所有语句。

你也可以使用if-then-else完成上面代码。

int month = 8;

if(month == 1){

  System.out.println("January");

}else if(month == 2){

  System.out.println("February");

}...

根据代码可读性和要测试的语句来决定使用switch语句还是if-then-else语句。if-then-else语句可以测试值的范围或条件,而switch语句只能根据一个整数、一个枚举值或一个String对象测试表达式。

另外一个有趣的地方是break语句。每一个break语句会终止一个switch块。控制流就继续执行跟在switch块后的第一条语句。break语句是必须的,如果没有它们,switch块中的语句会整个执行下去:匹配case标记后面的所有语句都会顺序执行,不管后面case的值是否匹配,直到碰到break语句。下面代码展示switch块从上而下整个执行,计算month对应月份和一年中其后的所有月份。

class SwitchFallThroughDemo{

  public static void main(String[] args){

    java.util.ArrayList<String> futureMonths = new java.util.ArrayList<String>();

    int month = 8;

    switch(month){

      case 1:futureMonths.add("January");

      case 2:futureMonths.add("February");

      case 3:futureMonths.add("March");

      case 4:futureMonths.add("April");

      case 5:futureMonths.add("May");

      case 6:futureMonths.add("June");

      case 7:futureMonths.add("July");

      case 8:futureMonths.add("August");

      case 9:futureMonths.add("September");

      case 10:futureMonths.add("October");

      case 11:futureMonths.add("November");

      case 12:futureMonths.add("December");

          break;

      default:break;

    }

    if(futureMonths.isEmpty()){

      System.out.println("Invalid month number");

    }else{

      for(String month : futureMonths){

        System.out.print(month+" ");

      }

    }

  }

}

输出:August September October November December

技术上来说,最后一个break语句并不需要,因为流程已经流出switch语句。推荐使用break这样易于修改代码、减少错误可能性。default代码段处理所有没被case代码段处理的值。

下面例子展示一条语句有多个case标签。这个例子计算某月的天数。

class SwitchDemo2{

  public static void main(String[] args){

    int month = 2;

    int year = 2000;

    int numDays = 0;

    switch(month){

      case 1:caes 3:case 5:case 7: case 8:case 10:case 12:

        numDays = 31;

        break;

      case 4:case 6:case 9:case 11:

        numDays = 30;

        break;

      case 2:

        if (((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0))

          numDays = 29;

        else

          numDays = 28;

        break;

      default:

        System.out.println("invalid month");

        break

    }

    System.out.println("Number of Days = " + numDays);

  }

}

输出:Number of Days = 29

switch语句中使用字符串

java7及其以后,你可以在switch语句表达式中使用String对象。下面例子,通过字符串展示月数。

public class StringSwitchDemo{

  public static int getMonthNumber(String month){

    int num = 0;

    if(month == null)

      return num;

    switch(month.toLowCase()){

      case "january":

        num = 1;

        break;

      case "february":

        num = 2;

        break; 

      case "march":

        num = 3;

        break;

      case "april":

        num = 4;

        break;

      case "may":

        num = 5;

        break;

      case "june":

        num = 6;

        break;

      case "july":

        num = 7;

        break;

      case "august":

        num = 8;

        break;

      case "september":

        num = 9;

        break;

      case "october":

        num = 10;

        break;

      case "november":

        num = 11;

        break;

      case "december":

        num = 12;

        break;

      default:

        num = 0;

        break;

    }

    return num;

  }

  public static void main(String[] args){

    String month = "august";

    int num = StringSwitchDemo.getMonthNumber(month);

    if(num == 0){

      Sysem.out.println("invalid month");

    }else{

      System.out.println(num);

    }

  }

}

输出:8

switch表达式中的字符串与每个case标签表达式的比较就好像使用String.equals方法。为了忽略大小写,month参数转成小写(使用toLowCase方法),所有case标签的字符串都是小写。

注意,本例中检查switch表达式是否为null。确保switch表达式不为null,防止空指针异常。

while和do-while语句

 while语句会一直执行块中的语句当条件为真。如下:

while(expression){

  statement(s)

}

while语句计算表达式的值,表达式必须返回一个布尔值。如果表达式值为true,执行while块中的语句。while语句一直测试表达式和执行while块中的语句直到表达式的值为false。下面代码使用while语句打印1到10。

class WhileDemo{

  public static void main(Sring[] args){

    int count = 1;

    while(count < 11){

      System.out.println(count);

      count++;

    }

  }

}

你可以使用while语句实现一个无限循环:

while(true){

}

java同时提供了do-while语句,如下:

do{

  statement(s);

}while(expression);

while语句和do-while语句的区别在于do-while在循环的底部计算它的表达式,而while在顶部计算表达式。因此,do-while语句至少将其块执行一次。如下代码:

class DoWhileDemo{

  public static void main(String[] args){

    int count = 1;

    do{

      System.out.println(count);

      count++;

    }while(count<11);

  }

}

for语句

for语句提供了一个简洁的方法遍历一个范围之中的值。程序中一般用它来循环,因为在for语句中,它不停的重复直到满足某个条件。通常for语句如下:

for(initialization;termination;increment){

  statement(s);

}

使用这个版本的for语句时注意:

初始化表达式初始化这个循环,在循环开始时,执行一次;

当终止表达式计算得到false,循环终止;

循环的每次遍历之后,增加表达式都被执行,这里特别适用于增加或减少一个值;

下面程序使用通用的for语句来打印1到10:

class ForDemo{

  public static void main(String[] args){

    for(int i = 0; i < 11; i++){

      System.out.print(i+" ");

    }

  }

}

输出为:1 2 3 4 5 6 7 8 9 10

注意在初始化表达式中如何声明一个变量。这个变量的作用域从它的声明延续到块的结束,由for语句管理,所以变量可以被终止表达式和增加表达式使用。如果控制for语句的变量并不需要在循环外部使用,那么这个变量最好在初始化表达式中声明。变量名i、j、k通常用于for循环;在初始化表达式中声明它们,可以限制它们的生命周期和减少错误。

for语句中的三个表达式都是可选的,如下就定义了一个无限循环语句:

for(;;;){

}

for语句还有其他的形式,针对集合和数组的遍历。这些形式当作for语句的增强版,可以让你的循环更加紧凑和易读。如下,考虑下面这个数组,拥有数字1到10:

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

下面代码,使用增强版for语句遍历这个数组:

class EnhancedForDemo{

  public static void main(String[] args){

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

    for(int item : numbers){

      System.out.print(item+" ");

    }

  }

}

输出:1 2 3 4 5 6 7 8 9 10

我们推荐尽量使用增强版的for语句。

分支语句

 break语句有两种形式:有标记和无标记的。你在switch语句中已经见过无标记的。你也可以使用无标记的break来终止一个for、while、do-while循环,如下代码:

class BreakDemo{

  public static void main(String[] args){

    int[] arrayOfInts = {32,87,3,589,

              12,1076,2000,

              8,622,127};

    int searchfor = 12;

    int i;

    boolean foundIt = false;

    for(i = 0; i < arrayOfInts.length; i++){

      if(arrayOfInts[i] == searchfor){

        foundIt = true;

        break;

      }

    }

    if(foundId){

      System.out.println("Found " + searchfor + " at index " + i);

    }else{

      System.out.println(searchfor + " not in the array");

    }

  }

}

输出:Found 12 at index 4

程序在数组中寻找数字12。break语句,在数字找到时终止for循环,控制流跳到for循环后面的语句。

一个无标记的break语句终止最内层的switch、for、while、do-while语句,而一个有标记的break语句可以终止外层的循环语句。下面程序和上面程序类似,但是使用了嵌套的for循环在一个二维数组中找一个值。当值找到后,有标记的break终止外层的循环(标记"search")。

class BreakWithLabelDemo{

  public static void main(String[] args){

    int[][] arrayOfInts = {{32,87,3,589},{12,1076,2000,8},{622,127,77,955}};

    int searchfor = 12;

    int i;

    int j = 0;

    boolean foundIt = false;

  search:

    for(i = 0; i < arrayOfInts.length; i++){

      for(j = 0; j < arrayOfInts[i].length; j++){

        if(arrayOfInts[i][j] == searchfor){

          foundIt = true;

          break search;

        }

      }

    }

    if(fountId){

      System.out.println("Found " + searchfor + " at index "+ i + " ,  " + j);

    }else{

      System.out.println(searchfor + "  not in the array");

    }

  }

}

输出:Found 12 at index 1, 0

break语句终止了标记语句;它不会把控制流交回给标记语句。控制流跳到标记语句的后面一条语句。

在for、while、do-while循环中使用continue语句跳过当前的遍历。无标签的形式跳到最内层循环的结尾,并计算控制循环的布尔表达式。下面程序对一个字符串,计算出现的字母p。当果当前字母不是p,continue语句跳过循环的剩余部分,并处理下一个字母。如果是p,计数加1。

class ContinueDemo{

  public static void main(String[] args){

    String searchMe = "peter piper picked a " + "peck of pickled peppers";

    int max = searchMe.length();

    int numPs = 0;

    for(int i = 0; i < max; i++){

      if(searchMe.charAt(i) != ‘p‘)

        continue;

      numPs++;

    }

    System.out.println("found " + numPs + " p in the string");

  }

}

输出:found 9 p in the string

为了使效果更加清晰,试试去掉continue,重新执行。结果变成了35个p。

一个有标记的continue语句跳过当前的遍历,从标记的外层循环中。下面的例子使用嵌套循环在一个字符串中寻找子字符串。两个嵌套循环是需要的:一个循环遍历被寻找的字符串,一个遍历子字符串。下面代码用有标记的continue跳过外层循环的遍历。

class ContinueWithLabelDemo{

  public static void main(String[] args){

    String searchMe = "look  for a substring in me";

    String substring = "sub";

    boolean foundIt = false;

    int max = searchMe.length() - substring.length();

  test:

    for(int i = 0; i < max; i++){

      int n = substring.length();

      int j = 0; 

      int k = i;

      while(n-- !=  0){

        if(searcchMe.charAt(k++) != substring.charAt(j++)){

          continue test;

        }

      }

      foundIt = true;

      break test;

    }

    System.out.println(foundIt ? "found it" : "not found it");

  }

}

输出:found it

最后一条分支语句是return语句。return语句从当前方法中退出,控制流返回到该方法调用的位置。return语句有两种形式:一种返回一个值,一种不返回值。返回值(或者是一个表达式计算出一个值)只需要把值跟在return的后面。

return ++count;

返回的值的数据类型必须匹配方法声明的返回值的类型。当方法声明void,使用不返回值的return。

return

后面会讲到方法。

控制流语句总结

if-then语句是所有控制流语句中最常用的。它告诉你的程序去执行某段代码当你的条件计算为true时。if-then-else语句提供了第二条执行路径当if分句计算为false。不像if-then和if-then-else,switch语句允许任意数量的执行路径。当某个条件为真时,while和do-while语句一直执行一块语句。while和do-while的区别是do-while在循环的底部计算它的表达式,而不是顶部。因此,do-while中的块语句至少执行一遍。for语句提供简洁的方法遍历一个范围内的值。它有两个形式,其中一个是针对集合和数组的。

 

(翻译)《二》语言基础(4)--控制流语句