第3章 选择

 

3.1 引言

 程序可以基于条件决定执行哪些语句。
 选择语句要用到的条件采用布尔表达式计算。布尔表达式是计算结果为布尔值true或者false的表达式。

3.2 Boolean数据类型

 Java提供六种关系操作符(relational operator)也称为比较操作符(comparison operator),用于两个值的比较。
 保存布尔值的变量称为布尔变量(boolean variable),boolean数据类型用于声明布尔型变量。

3.3 if语句

 if语句是一个构造,允许程序确定执行的可选路径。  Java有几种类型的选择语句:单分支if语句。双分支if-else语句、嵌套if语句、多分支if-else语句、switch语句和条件操作符。
 单分支if语句是指当且仅当条件为true时执行一个动作。单分支if语句的语法如下:

{
    if(布尔表达式){
        语句(组);
    }
}

3.4 双分支if-else语句

 if-else语句根据条件是真或者是加,决定执行的路径。根据条件为true或false,双分支if语句可以指定不同的操作。
 下面是双分支if-else语句的语法:

{
    if(布尔表达式){
        布尔表达式为真时执行的语句(组);
    }else{
        布尔表达式为假时执行的语句(组);
    }
}

3.5 嵌套的if语句和多分枝if-else语句

 if语句可以在另外一个if语句中,形成嵌套的if语句。
 下面时一个嵌套的if语句:

{
    if(i>k){
        if(j>k){
            System.out.println("i and j are greater than k");
        }
    }else{
        System.out.println("i is less than or equal to k");
    }
}

3.6 常见错误和陷阱

 常见错误1:忘记必要的括号
 常见错误2:错误地在if行出现分号
 常见错误3:对布尔值的冗余测试
 常见错误4:悬空else出现的歧义
下面代码块内有两个if子句和一个else子句。那么,哪个if子句和这个else匹配?这里的缩进表明else子句匹配第一个if子句。
但是,else实际匹配的是第二个if子句。这种现象就称为悬空else歧义(dangling-else ambiguity)。在同一个块中,else总是和离它最近的if子句匹配。

{
    int i = 1, j = 2, k = 3;
    if(i > j)
        if(i > k)
            System.out.println("A");
    else
            System.out.println("B");
}
{
    int i = 1, j = 2, k = 3;
    if(i > j)
        if(i > k)
            System.out.println("A");
        else
            System.out.println("B");
}

 常见错误5:两个浮点数值的相等测试
浮点数具有有限的计算精度;涉及浮点数的计算可能引入舍入错误。因此两个浮点数值的相等测试并不可靠。
虽然不能依赖于两个浮点数值的相等测试,但是可以通过测试两个数的差距小于某个阈值,来比较它们是否已经足够接近。
 常见陷阱1:简化布尔变量赋值

{
    if(number % 2 == 0)
        even = true;
    else 
        even = false;
}
{
    boolean even = number % 2 == 0;
}

 常见陷阱2:避免不同情形中的重复代码。

3.7 产生随机数

 Math类中的random()方法,调用该方法会返回一个双精度的随机值d且满足0.0<=d<1.0。
 System.exit(status)是在System类中定义的,调用这个方法可以种植程序。参数status为0表明程序正常结束。一个非0的状态代码表示非正常结束。
 对所有的程序都应该先编写少量代码然后进行测试,之后再继续添加更多的代码。这个过程称为递进式开发和测试(incremental development and testing)。这种方法使得调试变得更加容易,因为错误很可能就在你刚刚添加进去的新代码中。

3.8 逻辑操作符

 逻辑操作符!、&&、||和^可以用于产生复合布尔表达式
 有时,是否执行一条语句是由几个条件的组合来决定的。可以使用逻辑操作符组合这些条件。逻辑操作符(logical operator)也称布尔操作符(boolean operator),是对布尔值进行的运算,它会创建新的布尔值。
 !是非操作符,对true取反为false;
 &&是与操作符,当且仅当两个操作数都为true时,这两个布尔型操作数的与(&&)为true;
 ||是或操作符,当至少一个操作数为true时,两个布尔型操作数的或(||)为ture;
 ^是异或操作符,当且仅当两个操作数具有不同的布尔值时,两个布尔型操作数的异或(^)才为true。注意:p1^p1等同于p1!=p2。
 德摩根定律是以印度出生的英国数学家和逻辑学家奥古斯都·德·摩根来命名的,这个定律可以用来简化表达式。定义表述如下:
  !(condition1 && condition2)is the same as !condition1 || !condition2
  !(condition1 || condition2)is the same as !condition1 && !condition2

{
    !(number % 2 == 0 && number % 3 == 0)
    可简化为:
    number % 2 != 0 || number % 3 != 0
}

 如果操作符&&的操作数之一为false,那么表达式是false;如果操作符||的操作数之一为true,那么表达式是true。Java利用这些特性来提高这些操作符的效率。在编程术语中,&&和||被称为短路或条件操作符;Java也提供了无条件&和|操作符。

3.9 switch语句

 switch语句基于变量或者表达式的值来执行语句。
 switch语句的完整语法:

switch(switch表达式){
    case 值1:语句(组)1;
        break;
    case 值2:语句(组)2;
        break; 
    default:默认情况下执行的语句(组)
}

 switch语句遵从以下规则:
1.switch表达式必须能计算出一个char、byte、short、int或者String型值,且用括号括住。
2.value1…..必须与switch表达式的值具有相同的数据类型。注意它们都是常量表达式,也就是说这里的表达式是不能包含变量的。
3.当switch表达式的值与case语句的值相匹配时,执行从该case开始的语句,直到遇到一个break语句或到达该switch语句的结束。
4.默认情况(default)是可选的,当没有一个给出的case与switch表达式匹配时,则执行该操作。
5.关键字break是可选的。break语句会立即终止switch语句。
 不要忘记在需要的时候使用break语句。一旦匹配其中一个case,就从匹配的case处开始执行,直到遇到break语句或到达switch语句的结束。这种现象被称为落空行为(fall-through behavior)。
 为避免程序设计错误,提高代码的可维护性,如果刻意省略break,在case子句后添加注释是一个好的做法。

3.10 条件操作

 条件操作符,也称三元操作符,因为使用了三个操作数。这是Java中唯一的一个三元操作符。该语法如下所示:
  boolean-expression ? expression1 : expression2
 如果boolean-expression的值为true,则结果为表达式expression1;否则,结果为表达式expression2。

3.11 操作符的优先级和结合规则

 操作符的优先级和结合规则确定了操作符计算的顺序。
 优先级规则定义了操作符的先后次序;如果优先级相同的操作符相邻,则结合规则(associativity)决定它们的次序。
 除了赋值操作符之外,所有的二元操作符都是左结合的(left-asspciative)。赋值操作符是右结合的。

3.12 调试

 调试是在程序中找到和修改错误的过程。
 因为编译器可以明确指出错误的位置以及出错的原因,所以语法错误是容易发生和纠正的。运行时错误也不难找,因为在程序异常中止时,错误的原因都会显示在控制台上。然而,查找逻辑错误就很富有挑战性。
 逻辑错误即Bug。查找和改正错误的过程称为调试(debugging)。调试的一般途径时采用各种方法逐步缩小程序中bug所在的范围。可以手工跟踪(hand trace)程序(即通过读程序找错误),也可以插入打印语句,显示变量的值或程序的执行流程。这种适用于短小、简单的程序。对于庞大、复杂的程序,最有效的调试方法还是使用调试工具。
 JDK包含了一个命令行调试器jdb,结合一个类名来调用该命令。Jdb本身也是一个Java程序,运行自身的一个Java解释器拷贝。所有的Java IDE工具都半酣集成的调试器。调试器应用让你可以跟踪一个程序的执行。它们因系统而不同,但都支持以下大部分有用的特征。
  1.一次执行一条语句:调试器允许你一次执行一条语句,从而可以看到每条语句的效果。
  2.跟踪进入或者一步运行一个方法:如果一个方法正在被执行,可以让调试器跟踪进入方法内部,并且一次执行方法里面的一条语句,或者也可以让调试器一步运行整个方法。如果你知道方法是正确工作的,应该一次运行整个方法。   3.设置断点:你也可以在一条特定的语句上面设置断点。当遇到一个断点时,程序将暂停。可以设置任意多的断点。当你知道程序错误从什么地方可能开始的时候,断点特别有用。你可以将断点设置在那条语句上,让程序先执行到断点处。
  4.显示变量:调试器让你选择多个变量并且显示它们的值。当你跟踪一个程序的时候,变量的内容持续更新。
  5.显示调用堆栈:调试器让你跟踪所有的方法调用。当你需要看到程序执行流程的整体过程时,这个特征非常有用。
  6.修改变量:一些调试器允许你在调试的过程中修改变量的值。当你希望用不同的示例来测试程序,而又不希望离开调试器的时候,这是非常方便的。