如下所示代码:

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