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("青年人");
}
}
}
输出结果为:
中年人
上面程序的判断逻辑可以转换为如下三种情形。
- age大于60,判断为“老年人”。
- age大于40岁,且age小于等于60岁,判断为“中年人”。
- age大于20岁,且age小于等于40岁,判断为“青年人”。
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());
}
}