如下所示代码:
public class Example023 { public static void main(String[] args) { errorMethod(); rightMethod(); } private static void errorMethod() { StringBuffer word = null; Random rnd = new Random(); switch (rnd.nextInt(2)) { case 1: word = new StringBuffer('P'); case 2: word = new StringBuffer('G'); default: word = new StringBuffer('M'); } word.append('a'); word.append('i'); word.append('n'); System.out.println(word); } private static void rightMethod() { StringBuffer word = null; Random rnd = new Random(); switch (rnd.nextInt(3)) { // error1 case 1: word = new StringBuffer("P");// error2 break;// error3 case 2: word = new StringBuffer("G");// error2 break;// error3 default: word = new StringBuffer("M");// error2 break;// error3 } word.append('a'); word.append('i'); word.append('n'); System.out.println(word); } }
输出结果:
第一行输出恒为ain,第二行输出结果达到预期的效果,输出一个pain,gain,main的随机。
代码解析:
第一行输出对应的函数是errorMethod,第二行对应的函数是rightMethod。在errorMethod中,存在三个bug。
第一个bug是所选取的随机数使得switch语句只能到达其三种情况中的两种。Random.nextInt(int)的规范描述道:“返回一个伪随机的、均等地分布在从 0(包括)到指定的数值(不包括)之间的一个 int 数值”。这意味着表达式 rnd.nextInt(2)可能的取值只有0和1,Switch语句将永远也到不了 case 2 分支,这表示程序将永远不会打印Gain。nextInt的参数应该是3而不是2。
第二个bug是在不同的情况(case)中没有任何break语句。
最后一个,也是最微妙的一个。bug是表达式new StringBuffer('m')可能没有做哪些你希望它做的事情。你可能对StringBuffer(char) 构造器并不熟悉,这很容易解释:它压根就不存在。StringBuffer有一个无参数的构造器,一个接受一个String作为字符串缓冲区初始内容的构造器,以及一个接受一个int作为缓冲区初始容量的构造器。在本例中,编译器会选择接受 int 的构造器,通过拓宽原始类型转换把字符数值'm' 转换为一个 int 数值77。换句话说,new StringBuffer('m')返回的是一个具有初始容量 77 的空的字符串缓冲区。该程序余下的部分将字符 a、i 和 n 添加到了这个空字符串缓冲区中,并打印出该字符串缓冲区那总是 ain 的内容。这里要记住的是,char不是string,它更像是一个int。
(注:本【java解惑】系列,均是博主阅读《java解惑》原书后,将原书上的讲解和例子部分改编,然后写成博文进行发布的。所有例子均亲自测试通过,并共享在github上。通过这些例子,激励自己,惠及他人。同时,本系列所有博文会同步发布在博主个人微信公众号(搜索“爱题猿”或者“ape_it”),方便大家阅读。如果文中有任何侵犯原作者权利的内容,请及时告知博主,以便及时删除;如果读者对文中的内容有异议或者问题,欢迎通过博客留言或者微信公众号留言等方式共同探讨。)
源代码地址:https://github.com/rocwinger/java-disabuse