Java™ Puzzlers:
Traps, Pitfalls, and Corner Cases
(Java 解惑)
Joshua Bloch/ Neal Gafter
June 24, 2005
1
本书特写了 95 个有关 Java 或其类库的陷阱和缺陷的谜题,其中大多数谜题都采用了短程序的
方式,这些程序的行为与其看似的大相径庭。在每个谜题之后都给出了详细的解惑方案,这些
解惑方案超越了对程序行为的简单解释,向读者展示了如何一劳永逸地避免底层的陷阱与缺
陷。
本书趣味十足、寓教于乐,适合于具备 Java 知识的学习者和有编程经验的 Java 程序员。
· 本书评语:
“每一种编程语言都有其怪癖的行为,这本生动的书通过趣味十足和发人深省的编程谜题揭示了
Java 编程语言的奇异之处”。
——Guy Steele,SunJ 院士,《Java 语言规范》的合著者
“我笑了,我叫起来了,我钦佩地举起了双手。”
——Tim Peierls,Prior Artisans 公司总裁,JSR 166 专家组成员
2
目 录
Java谜题 1——表达式谜题 ......................................................................1
谜题 1:奇数性........................................................................................................................1
谜题 2:找零时刻....................................................................................................................2
谜题 3:长整除........................................................................................................................3
谜题 4:初级问题....................................................................................................................5
谜题 5:十六进制的趣事........................................................................................................6
谜题 6:多重转型....................................................................................................................7
谜题 7:互换内容....................................................................................................................9
谜题 8:Dos Equis .................................................................................................................10
谜题 9:半斤..........................................................................................................................11
谜题 10:八两........................................................................................................................13
Java谜题 2——字符谜题 ........................................................................14
谜题 11:最后的笑声............................................................................................................14
谜题 12:ABC .......................................................................................................................15
谜题 13:畜牧场....................................................................................................................17
谜题 14:转义字符的溃败....................................................................................................18
谜题 15:令人晕头转向的Hello...........................................................................................20
谜题 16:行打印程序............................................................................................................21
谜题 17:嗯?........................................................................................................................22
谜题 18:字符串奶酪............................................................................................................23
谜题 19:漂亮的火花............................................................................................................25
谜题 20:我的类是什么?....................................................................................................26
谜题 21:我的类是什么?II .................................................................................................28
谜题 22:URL的愚弄............................................................................................................29
谜题 23:不劳无获................................................................................................................30
Java谜题 3——循环谜题 ........................................................................33
谜题 24:尽情享受每一个字节............................................................................................33
谜题 25:无情的增量操作....................................................................................................34
谜题 26:在循环中................................................................................................................35
谜题 27:变幻莫测的i值.......................................................................................................37
谜题 28:循环者....................................................................................................................38
谜题 29:循环者的新娘........................................................................................................39
谜题 30:循环者的爱子........................................................................................................40
谜题 31:循环者的鬼魂........................................................................................................41
谜题 32:循环者的诅咒........................................................................................................42
3
谜题 33:循环者遇到了狼人................................................................................................44
谜题 34:被计数击倒了........................................................................................................45
谜题 35:一分钟又一分钟....................................................................................................47
Java谜题 4——异常谜题 ........................................................................48
谜题 36:优柔寡断................................................................................................................48
谜题 37:极端不可思议........................................................................................................49
谜题 38:不受欢迎的宾客....................................................................................................51
谜题 39:您好,再见!........................................................................................................53
谜题 40:不情愿的构造器....................................................................................................54
谜题 41:域和流....................................................................................................................56
谜题 42:异常为循环而抛....................................................................................................57
谜题 43:异常地危险............................................................................................................60
谜题 44:切掉类....................................................................................................................62
谜题 45:令人疲惫不堪的测验............................................................................................65
Java谜题 5——类谜题 ............................................................................66
谜题 46:令人混淆的构造器案例........................................................................................66
谜题 47:啊呀!我的猫变成狗了........................................................................................68
谜题 48:我所得到的都是静态的........................................................................................70
谜题 49:比生命更大............................................................................................................71
谜题 50:不是你的类型........................................................................................................73
谜题 51:那个点是什么?....................................................................................................74
谜题 52:合计数的玩笑........................................................................................................78
谜题 53:按你的意愿行事....................................................................................................80
谜题 54:Null与Void .............................................................................................................81
谜题 55:特创论....................................................................................................................82
Java谜题 6——库谜题 ............................................................................85
谜题 56:大问题....................................................................................................................85
谜题 57:名字里有什么?....................................................................................................86
谜题 58:产生它的散列码....................................................................................................88
谜题 59:什么是差?............................................................................................................90
谜题 60:一行的方法............................................................................................................91
谜题 61:日期游戏................................................................................................................93
谜题 62:名字游戏................................................................................................................95
谜题 63:更多同样的问题....................................................................................................96
谜题 64:按余数编组............................................................................................................97
谜题 65:一种疑似排序的惊人传奇....................................................................................99
Java谜题 7——更多的类谜题 ..............................................................102
谜题 66:一件私事..............................................................................................................102
谜题 67:对字符串上瘾......................................................................................................104
谜题 68:灰色的阴影..........................................................................................................106
4
谜题 69:黑色的渐隐..........................................................................................................108
谜题 70:一揽子交易..........................................................................................................109
谜题 71:进口税..................................................................................................................110
谜题 72:终极危难..............................................................................................................112
谜题 73:你的隐私正在公开..............................................................................................113
谜题 74:同一性的危机......................................................................................................115
谜题 75:头还是尾?..........................................................................................................116
名字重用的术语表.......................................................................................................118
Java谜题 8——更多的库谜题 ..............................................................120
谜题 76:乒乓......................................................................................................................120
谜题 77:搞乱锁的妖怪......................................................................................................122
谜题 78:反射的污染..........................................................................................................125
谜题 79:这是狗的生活......................................................................................................127
谜题 80:更深层的反射......................................................................................................130
谜题 81:烧焦到无法识别..................................................................................................131
谜题 82:啤酒爆炸..............................................................................................................133
谜题 83:诵读困难者的一神论..........................................................................................135
谜题 84:被粗暴地中断......................................................................................................136
谜题 85:惰性初始化..........................................................................................................137
Java谜题 9——高级谜题 ......................................................................140
谜题 86:有毒的括号垃圾..................................................................................................140
谜题 87:紧张的关系..........................................................................................................140
谜题 88:原生类型的处理..................................................................................................142
谜题 89:泛型迷药..............................................................................................................145
谜题 90:荒谬痛苦的超类..................................................................................................148
谜题 91:序列杀手..............................................................................................................150
谜题 92:双绞线..................................................................................................................153
谜题 93:类的战争..............................................................................................................154
谜题 94:迷失在混乱中......................................................................................................156
谜题 95:只是些甜点..........................................................................................................159
5
Java 谜题 1——表达式谜题
谜题 1:奇数性
return i % 2 == 1;
下面的方法意图确定它那唯一的参数是否是一个奇数。这个方法能够正确运转
吗?
public static boolean isOdd(int i){
}
奇数可以被定义为被 2 整除余数为 1 的整数。表达式 i % 2 计算的是 i 整除 2
时所产生的余数,因此看起来这个程序应该能够正确运转。遗憾的是,它不能;
它在四分之一的时间里返回的都是错误的答案。
为什么是四分之一?因为在所有的 int 数值中,有一半都是负数,而 isOdd 方
法对于对所有负奇数的判断都会失败。在任何负整数上调用该方法都回返回
false ,不管该整数是偶数还是奇数。
这是 Java 对取余操作符(%)的定义所产生的后果。该操作符被定义为对于所
有的 int 数值 a 和所有的非零 int 数值 b,都满足下面的恒等式:
(a / b) * b + (a % b) == a
换句话说,如果你用 b 整除 a,将商乘以 b,然后加上余数,那么你就得到了最
初的值 a 。该恒等式具有正确的含义,但是当与 Java 的截尾整数整除操作符
相结合时,它就意味着:当取余操作返回一个非零的结果时,它与左操作数具有
相同的正负符号。
当 i 是一个负奇数时,i % 2 等于-1 而不是 1, 因此 isOdd 方法将错误地返
回 false。为了防止这种意外,请测试你的方法在为每一个数值型参数传递负数、
零和正数数值时,其行为是否正确。
这个问题很容易订正。只需将 i % 2 与 0 而不是与 1 比较,并且反转比较的含
义即可:
return i % 2 != 0;
public static boolean isOdd(int i){
}
如果你正在在一个性能临界(performance-critical)环境中使用 isOdd 方法,
那么用位操作符 AND(&)来替代取余操作符会显得更好:
public static boolean isOdd(int i){
return (i & 1) != 0;
1
}
总之,无论你何时使用到了取余操作符,都要考虑到操作数和结果的符号。该操
作符的行为在其操作数非负时是一目了然的,但是当一个或两个操作数都是负数
时,它的行为就不那么显而易见了。
谜题 2:找零时刻
请考虑下面这段话所描述的问题:
Tom 在一家汽车配件商店购买了一个价值$1.10 的火花塞,但是他钱包中都是两
美元一张的钞票。如果他用一张两美元的钞票支付这个火花塞,那么应该找给他
多少零钱呢?
下面是一个试图解决上述问题的程序,它会打印出什么呢?
public static void main(String args[]){
System.out.println(2.00 - 1.10);
}
public class Change{
}
你可能会很天真地期望该程序能够打印出 0.90,但是它如何才能知道你想要打
印小数点后两位小数呢?
如果你对在 Double.toString 文档中所设定的将 double 类型的值转换为字符串
的规则有所了解,你就会知道该程序打印出来的小数,是足以将 double 类型的
值与最靠近它的临近值区分出来的最短的小数,它在小数点之前和之后都至少有
一位。因此,看起来,该程序应该打印 0.9 是合理的。
这么分析可能显得很合理,但是并不正确。如果你运行该程序,你就会发现它打
印的是 0.8999999999999999。
问题在于 1.1 这个数字不能被精确表示成为一个 double,因此它被表示成为最
接近它的 double 值。该程序从 2 中减去的就是这个值。遗憾的是,这个计算的
结果并不是最接近 0.9 的 double 值。表示结果的 double 值的最短表示就是你所
看到的打印出来的那个可恶的数字。
更一般地说,问题在于并不是所有的小数都可以用二进制浮点数来精确表示的。
如果你正在用的是 JDK 5.0 或更新的版本,那么你可能会受其诱惑,通过使用
printf 工具来设置输出精度的方订正该程序:
//拙劣的解决方案——仍旧是使用二进制浮点数
System.out.printf("%.2f%n",2.00 - 1.10);
2
这条语句打印的是正确的结果,但是这并不表示它就是对底层问题的通用解决方
案:它使用的仍旧是二进制浮点数的 double 运算。浮点运算在一个范围很广的
值域上提供了很好的近似,但是它通常不能产生精确的结果。二进制浮点对于货
币计算是非常不适合的,因为它不可能将 0.1——或者 10 的其它任何次负幂——
精确表示为一个长度有限的二进制小数
解决该问题的一种方式是使用某种整数类型,例如 int 或 long,并且以分为单
位来执行计算。如果你采纳了此路线,请确保该整数类型大到足够表示在程序中
你将要用到的所有值。对这里举例的谜题来说,int 就足够了。下面是我们用 int
类型来以分为单位表示货币值后重写的 println 语句。这个版本将打印出正确答
案 90 分:
System.out.println((200 - 110) + "cents");
解决该问题的另一种方式是使用执行精确小数运算的 BigDecimal。它还可以通
过 JDBC 与 SQL DECIMAL 类型进行互操作。这里要告诫你一点: 一定要用
BigDecimal(String)构造器,而千万不要用BigDecimal(double)。后一个构造
器将用它的参数的“精确”值来创建一个实例:new BigDecimal(.1)将返回一个
表示 0.100000000000000055511151231257827021181583404541015625 的
BigDecimal。通过正确使用 BigDecimal,程序就可以打印出我们所期望的结果
0.90:
import java.math.BigDecimal;
public class Change1{
}
这个版本并不是十分地完美,因为 Java 并没有为 BigDecimal 提供任何语言上的
支持。使用 BigDecimal 的计算很有可能比那些使用原始类型的计算要慢一些,
对某些大量使用小数计算的程序来说,这可能会成为问题,而对大多数程序来说,
这显得一点也不重要。
public static void main(String args[]){
}
System.out.println(new BigDecimal("2.00").
subtract(new BigDecimal("1.10")));
总之, 在需要精确答案的地方,要避免使用float和double;对于货币计算,
要使用int、long或BigDecimal。对于语言设计者来说,应该考虑对小数运算
提供语言支持。一种方式是提供对操作符重载的有限支持,以使得运算符可以被
塑造为能够对数值引用类型起作用,例如 BigDecimal。另一种方式是提供原始
的小数类型,就像 COBOL 与 PL/I 所作的一样。
谜题 3:长整除
这个谜题之所以被称为长整除是因为它所涉及的程序是有关两个 long 型数值整
除的。被除数表示的是一天里的微秒数;而除数表示的是一天里的毫秒数。这个
程序会打印出什么呢?
public class LongDivision{
3