Java流程控制的陷阱——if语句和循环体的陷阱

来源:互联网 发布:js input框不可修改 编辑:程序博客网 时间:2024/05/28 04:55

3、if语句的陷阱

3、1 else隐含的条件

       else字面意义是“否则”,隐含的条件是前面条件都不符合,也就是else有一个隐含的条件,else if的条件是if显示条件和else隐式条件的交集。
public class IfErrorTest {public static void main(String[] args) {int age = 45;if ( age > 20 ) {System.out.println("青年人");}else if ( age > 40 ) {System.out.println("中年人");}else if ( age > 60 ) {System.out.println("老年人");}}}
输出结果为:
青年人
       上面的程序是试图根据年龄来判断属于青年人、中年人还是老年人。从表面上来看,程序的运行过程为:年龄大于20岁是青年人,年龄大于40岁的是中年人,年龄大于60岁的是老年人。但是最终输出的却是青年人。造成这个问题的原因就是else后的隐含条件,else的隐含条件就是不满足else之前的条件。换句话来说,上面的代码实质上等于如下代码。
public class IfErrorTest {public static void main(String[] args) {int age = 45;if ( age > 20 ) {System.out.println("青年人");}else if ( age > 40 && !(age > 20)) {System.out.println("中年人");}else if ( age > 60 && !(age > 40) ) {System.out.println("老年人");}}}
       这样就比较容易看出为什么会发生上面的错误。对于判断中年人和老年人的情况是永远不会发生的,因此,程序也永远不可能打印出中年人和老年人。为了能够达到预期的效果,将程序修改为如下所示。
public class IfRightTest {public static void main(String[] args) {int age = 45;if ( age > 60 ) {System.out.println("老年人");}else if ( age > 40 ) {System.out.println("中年人");}else if ( age > 20 ) {System.out.println("青年人");}}}
输出结果为:
中年人
       上面程序的判断逻辑可以转换为如下三种情形。
  1. age大于60,判断为“老年人”。
  2. age大于40岁,且age小于等于60岁,判断为“中年人”。
  3. age大于20岁,且age小于等于40岁,判断为“青年人”。

       如果每次都去计算if条件和else条件的交集也是一件非常繁琐的事情,为了避免出现上面的错误,使用if-else语句有一条基本原则:总是优先把包含范围小的条件放在前面处理。例如age>60比age>20的范围更小,所以应该优先处理age>60的情况。

3、2 空语句

public class BlankStatement {public static void main(String[] args) {int age = 45;if ( age > 60 ); {System.out.println("老年人");}else if ( age > 40 ) {System.out.println("中年人");}else if ( age > 20 ) {System.out.println("青年人");}}}
从表面上来看,这个程序应该一切正常,程序先处理小范围条件,后处理大范围的条件,应该输出“青年人”,但是程序却报错:Syntax error on token "else", delete this token。原因是:if ( age > 60 );,在判断之后有一个分号,这个分号就是一个空语句。也就是说,这个分号就是if语句的条件执行体,if语句到这里就完全结束了。需要指出的是,如果if语句后没有花括号括起来的条件执行体,那么这个if语句仅仅控制到该语句后的第一个分号处,后面部分将不再受该if语句的控制

4、循环体的陷阱

4、1 循环体花括号

       Java对于if语句、while语句、for语句的处理策略完全一样:如果紧跟该语句的是花括号括起来的语句块,那么该if语句、while语句、for语句将控制花括号括起来的语句块;如果if语句、while语句、for语句之后没有紧跟花括号,那么if语句、while语句、for语句的作用范围到该语句之后的第一个分号结束。
public class WhileScopeTest {public static void main(String[] args) {int i = 0 ; while ( i < 10 ) System.out.println(i);i ++;}}
       上面程序编译正常,运行时一直输出0,并且是一个死循环。出现这个输出结果是因为程序省略了while循环的循环体花括号,那么受while循环控制的循环体只有一行代码System.out.println(i);。接下来的i++;并不在循环体之内,将导致循环计数器i一直保持0。
       只有当循环体内只包含一条语句时才可以省略循环体的花括号,此时循环本身不会受到太大的影响。当循环体有多条语句时,不可省略循环体的花括号,否则循环体将变成只有紧跟循环条件的那条语句。

4、2 省略花括号的危险

class Cat {private static long instance = 0;public Cat() {System.out.println("执行无参数的构造器");instance++;}public static long getInstance() {return instance;}}public class CatTest {public static void main(String[] args) {for ( int i = 0 ; i < 10 ; i ++ ) Cat cat = new Cat();System.out.println(Cat.getInstance());}}
       上面程序试图通过for循环来创建10个Cat对象,然后通过getInstance()静态方法获取instance的值,由于循环体只有一条语句,因此省略了循环体的花括号,但是如果尝试编译该程序,编译器会报错:Syntax error, insert ";" to complete Statement。发生这种错误的原因:因为Java语言规定:for、while或do循环中的重复执行语句不能是一条单独的局部变量定义语句;如果程序要使用循环来重复定义局部变量,这条局部变量定义语句必须放在花括号内才有效。因此将程序改为如下所示。
class Cat {private static long instance = 0;public Cat() {System.out.println("执行无参数的构造器");instance++;}public static long getInstance() {return instance;}}public class CatTest {public static void main(String[] args) {for ( int i = 0 ; i < 10 ; i ++ ) {Cat cat = new Cat();}System.out.println(Cat.getInstance());}}






1 0