首页 » 编写高质量代码:改善Java程序的151个建议 » 编写高质量代码:改善Java程序的151个建议全文在线阅读

《编写高质量代码:改善Java程序的151个建议》建议85:小心switch带来的空值异常

关灯直达底部

使用枚举定义常量时,会伴有大量的switch语句判断,目的是为每个枚举项解释其行为,例如这样一个方法:


public static void doSports(Season season){

switch(season){

case Spring:

System.out.println("春天放风筝");

break;

case Summer:

System.out.println("夏天游泳");

break;

case Autumn:

System.out.println("秋天捉知了");

break;

case Winter:

System.out.println("冬天滑冰");

break;

default:

System.out.println("输入错误!");

break;

}

}


上面的代码中输入了一个Season类型的枚举,然后使用switch进行匹配,目的是输出每个季节能进行的活动。现在的问题是:这段代码有没有问题?

我们先来看它是如何被调用的,因为要传递进来的是Season类型,也就是一个实例对象,那当然应该允许为空了,我们就传递一个null值进去看看有没有问题,代码如下:


public static void main(Stringargs){

doSports(null);

}


似乎会打印出“输出错误!”,因为在switch中没有匹配到指定值,所以会打印出default的代码块,是这样的吗?不是,运行后的输出如下所示:


Exception in thread"main"java.lang.NullPointerException

at Client.doSports(Client.java:9)

at Client.main(Client.java:5)


竟然是空指针异常,第9行也就是switch那一行,怎么会有空指针呢?这就与枚举和switch的特性有关了,此问题也是在开发中经常发生的。我们知道,目前Java中的switch语句只能判断byte、short、char、int类型(JDK 7已经允许使用String类型),这是Java编译器的限制。问题是为什么枚举类型也可以跟在switch后面呢?

很简单,因为编译时,编译器判断出switch语句后的参数是枚举类型,然后就会根据枚举的排序值继续匹配,也就是说上面的代码与以下代码相同:


public static void doSports(Season season){

switch(season.ordinal()){

case Season.Spring.ordinal():

……

case Season.Summer.ordinal():

……

}

}


看明白了吧,switch语句是先计算season变量的排序值,然后与枚举常量的每个排序值进行对比的。在我们的例子中season变量是null值,无法执行ordinal方法,于是报空指针异常了。

问题清楚了,解决方法也很简单,在doSports方法中判断输入参数是否是null即可。