没有合适的资源?快使用搜索试试~ 我知道了~
首页Java Puzzlers中文版:揭秘隐藏陷阱与奇数性问题
"《Java Puzzlers中文版带完整目录》是一本专门针对Java编程语言及其核心库中隐藏的难题和谜题的书籍。书中包含了一系列挑战性的题目,旨在通过解决这些看似简单但实则需要深入理解Java特性的谜题,帮助读者提升技能并发现潜在陷阱。作者希望读者在解决这些谜题的过程中,不仅能够增强对Java语言的理解,还能学会识别和避免在实际编程中可能遇到的逻辑误区。 书中的一个重点章节是关于Java表达式谜题,其中提到一个名为`isOdd`的方法,该方法试图判断一个整数是否为奇数。尽管根据基本的数学逻辑,使用`i % 2 == 1`看起来合理,但由于Java中取余运算符 `%` 对负数的处理,当输入为负奇数时,方法会误判为偶数。这是因为Java `%` 操作符在处理负数时,如果余数不为零,结果会保持与左操作数相同的符号,而非预期的正一。 为解决这个问题,作者建议将条件更改为`i % 2 != 0`,这样不论输入是正负,都能正确识别奇数。在对性能有较高要求的场景,使用按位与操作符`&`(`i & 1`)替换取余操作符也是一个优化选择,因为它能更直接地检测奇数。 此外,书中的其他谜题也涵盖了Java语言中的各种陷阱,如数组越界、空指针异常、多线程同步等问题,这些都是在实际编程中容易忽视但可能导致严重错误的地方。通过解决这些谜题,读者不仅能锻炼逻辑思维和调试能力,还能加深对Java语言深层次特性的认识,从而提高编程水平和代码质量。 阅读这本书,不仅可以作为技术提升的工具,也能作为日常编码中的警示灯,提醒程序员们时刻注意潜在的编程陷阱,确保程序的正确性和健壮性。"
资源详情
资源推荐
![](https://csdnimg.cn/release/download_crawler_static/10942736/bg10.jpg)
尽管 char 是一个整数类型,但是许多类库都对其进行了特殊处理,因为 char
数值通常表示的是字符而不是整数。例如,将一个 char 数值传递给 println 方
法会打印出一个 Unicode 字符而不是它的数字代码。字符数组受到了相同的特殊
处理:println 的 char[]重载版本会打印出数组所包含的所有字符,而
String.valueOf 和 StringBuffer.append 的 char[]重载版本的行为也是类似的。
然而,字符串连接操作符在这些方法中没有被定义。该操作符被定义为先对它的
两个操作数执行字符串转换,然后将产生的两个字符串连接到一起。对包括数组
在内的对象引用的字符串转换定义如下[JLS 15.18.1.1]:
如果引用为 null,它将被转换成字符串"null"。否则,该转换的执行就像是不
用任何参数调用该引用对象的 toString 方法一样;但是如果调用 toString 方法
的结果是 null,那么就用字符串"null"来代替。
那么,在一个非空 char 数组上面调用 toString 方法会产生什么样的行为呢?数
组是从 Object 那里继承的 toString 方法[JLS 10.7],规范中描述到:“返回一
个字符串,它包含了该对象所属类的名字,'@'符号,以及表示对象散列码的一
个无符号十六进制整数”[Java-API]。有关 Class.getName 的规范描述到:在
char[]类型的类对象上调用该方法的结果为字符串"[C"。将它们连接到一起就形
成了在我们的程序中打印出来的那个丑陋的字符串。
有两种方法可以订正这个程序。你可以在调用字符串连接操作之前,显式地将一
个数组转换成一个字符串:
System.out.println(letters + " easy as " +
String.valueOf(numbers));
或者,你可以将 System.out.println 调用分解为两个调用,以利用 println 的
char[]重载版本:
System.out.print(letters + " easy as ");
System.out.println(numbers);
请注意,这些订正只有在你调用了 valueOf 和 println 方法正确的重载版本的情
况下,才能正常运行。换句话说,它们严格依赖于数组引用的编译期类型。
下面的程序说明了这种依赖性。看起来它像是所描述的第二种订正方式的具体实
现,但是它产生的输出却与最初的程序所产生的输出一样丑陋,因为它调用的是
println 的 Object 重载版本,而不是 char[]重载版本。
class ABC2{
public static void main(String[] args){
String letters = "ABC";
Object numbers = new char[] { '1', '2', '3' };
System.out.print(letters + " easy as ");
System.out.println(numbers);
}
}
![](https://csdnimg.cn/release/download_crawler_static/10942736/bg11.jpg)
总之,char 数组不是字符串。要想将一个 char 数组转换成一个字符串,就要调
用 String.valueOf(char[])方法。某些类库中的方法提供了对 char 数组的类似
字符串的支持,通常是提供一个 Object 版本的重载方法和一个 char[]版本的重
载方法,而之后后者才能产生我们想要的行为。
对语言设计者的教训是:char[]类型可能应该覆写 toString 方法,使其返回数
组中包含的字符。更一般地讲,数组类型可能都应该覆写 toString 方法,使其
返回数组内容的一个字符串表示。
谜题
谜题谜题
谜题 13
1313
13:
::
:畜牧场
畜牧场畜牧场
畜牧场
George Orwell 的《畜牧场(Animal Farm)》一书的读者可能还记得老上校的
宣言:“所有的动物都是平等的。”下面的 Java 程序试图要测试这项宣言。那
么,它将打印出什么呢?
public class AnimalFarm{
public static void main(String[] args){
final String pig = "length: 10";
final String dog = "length: " + pig.length();
System.out. println("Animals are equal: "
+ pig == dog);
}
}
对该程序的表面分析可能会认为它应该打印出 Animal are equal: true。毕竟,
pig 和 dog 都是 final 的 string 类型变量,它们都被初始化为字符序列“length:
10”。换句话说,被 pig 和 dog 引用的字符串是且永远是彼此相等的。然而,==
操作符测试的是这两个对象引用是否正好引用到了相同的对象上。在本例中,它
们并非引用到了相同的对象上。
你可能知道 String 类型的编译期常量是内存限定的。换句话说,任何两个 String
类型的常量表达式,如果标明的是相同的字符序列,那么它们就用相同的对象引
用来表示。如果用常量表达式来初始化 pig 和 dog,那么它们确实会指向相同的
对象,但是 dog 并不是用常量表达式初始化的。既然语言已经对在常量表达式中
允许出现的操作作出了限制,而方法调用又不在其中,那么,这个程序就应该打
印 Animal are equal: false,对吗?
嗯,实际上不对。如果你运行该程序,你就会发现它打印的只是 false,并没有
其它的任何东西。它没有打印 Animal are equal: 。它怎么会不打印这个字符
串字面常量呢?毕竟打印它才是正确的呀!谜题 11 的解谜方案包含了一条暗示:
+ 操作符,不论是用作加法还是字符串连接操作,它都比 == 操作符的优先级高。
因此,println 方法的参数是按照下面的方式计算的:
System.out.println(("Animals are equal: " + pig) == dog);
这个布尔表达式的值当然是 false,它正是该程序的所打印的输出。
![](https://csdnimg.cn/release/download_crawler_static/10942736/bg12.jpg)
有一个肯定能够避免此类窘境的方法:在使用字符串连接操作符时,总是将非平
凡的操作数用括号括起来。更一般地讲,当你不能确定你是否需要括号时,应该
选择稳妥地做法,将它们括起来。如果你在 println 语句中像下面这样把比较部
分括起来,它将产生所期望的输出 Animals are equal: false :
System.out.println("Animals are equal: " + (pig == dog));
可以论证,该程序仍然有问题。
如果可以的话,你的代码不应该依赖于字符串常量的内存限定机制。内存限定机
制只是设计用来减少虚拟机内存占有量的,它并不是作为程序员可以使用的一种
工具而设计的。就像这个谜题所展示的,哪一个表达式会产生字符串常量并非总
是很显而易见。
更糟的是,如果你的代码依赖于内存限定机制实现操作的正确性,那么你就必须
仔细地了解哪些域和参数必定是内存限定的。编译器不会帮你去检查这些不变
量,因为内存限定的和不限定的字符串使用相同的类型(String)来表示的。这
些因在内存中限定字符串失败而导致的 bug 是非常难以探测到的。
在比较对象引用时,你应该优先使用 equals 方法而不是 == 操作符,除非你需
要比较的是对象的标识而不是对象的值。通过把这个教训应用到我们的程序中,
我们给出了下面的 println 语句,这才是它应该具有的模样。很明显,在用这种
方式订正了该程序之后,它将打印出 true:
System.out.println("Animals are equal: " + pig.equals(dog));
这个谜题对语言设计者来说有两个教训。
•
字符串连接的优先级不应该和加法一样。这意味着重载 + 操作符来执行
字符串连接是有问题的,就像在谜题 11 中提到的一样。
•
还有就是,对于不可修改的类型,例如 String,其引用的等价性比值的
等价性更加让人感到迷惑。也许 == 操作符在被应用于不可修改的类型时
应该执行值比较。要实现这一点,一种方法是将 == 操作符作为 equals
方法的简便写法,并提供一个单独的类似于 System.identityHashCode
的方法来执行引用标识的比较。
谜题
谜题谜题
谜题 14
1414
14:
::
:转义字符的溃败
转义字符的溃败转义字符的溃败
转义字符的溃败
下面的程序使用了两个 Unicode 的转义字符,它们是用其十六进制代码来表示
Unicode 字符。那么,这个程序会打印什么呢?
public class EscapeRout{
public static void main(String[] args){
// \u0022 是双引号的 Unicode 转义字符
System.out.println("a\u0022.length()
+\u0022b".length());
}
![](https://csdnimg.cn/release/download_crawler_static/10942736/bg13.jpg)
}
对该程序的一种很肤浅的分析会认为它应该打印出 26,因为在由两个双引号
"a\u0022.length()+\u0022b"标识的字符串之间总共有 26 个字符。
稍微深入一点的分析会认为该程序应该打印 16,因为两个 Unicode 转义字符每
一个在源文件中都需要用 6 个字符来表示,但是它们只表示字符串中的一个字
符。因此这个字符串应该比它的外表看其来要短 10 个字符。 如果你运行这个程
序,就会发现事情远不是这么回事。它打印的既不是 26 也不是 16,而是 2。
理解这个谜题的关键是要知道:Java 对在字符串字面常量中的 Unicode 转义字
符没有提供任何特殊处理。编译器在将程序解析成各种符号之前,先将 Unicode
转义字符转换成为它们所表示的字符[JLS 3.2]。因此,程序中的第一个 Unicode
转义字符将作为一个单字符字符串字面常量("a")的结束引号,而第二个
Unicode 转义字符将作为另一个单字符字符串字面常量("b")的开始引号。程
序打印的是表达式"a".length()+"b".length(),即 2。
如果该程序的作者确实希望得到这种行为,那么下面的语句将要清楚得多:
System.out.println("a".length()+"b".length());
更有可能的情况是该作者希望将两个双引号字符置于字符串字面常量的内部。使
用 Unicode 转义字符你是不能实现这一点的,但是你可以使用转义字符序列来实
现[JLS 3.10.6]。表示一个双引号的转义字符序列是一个反斜杠后面紧跟着一个
双引号(\”)。如果将最初的程序中的 Unicode 转义字符用转义字符序列来替
换,那么它将打印出所期望的 16:
System.out.println("a\".length()+\"b".length());
许多字符都有相应的转义字符序列,包括单引号(\')、换行(\n)、制表符(\t)
和反斜线(\\)。你可以在字符字面常量和字符串字面常量中使用转义字符序列。
实际上,你可以通过使用被称为八进制转义字符的特殊类型的转义字符序列,将
任何 ASCII 字符置于一个字符串字面常量或一个字符字面常量中,但是最好是尽
可能地使用普通的转义字符序列。
普通的转义字符序列和八进制转义字符都比 Unicode 转义字符要好得多,因为与
Unicode 转义字符不同,转义字符序列是在程序被解析为各种符号之后被处理
的。
ASCII 是字符集的最小公共特性集,它只有 128 个字符,但是 Unicode 有超过
65,000 个字符。一个 Unicode 转义字符可以被用来在只使用 ASCII 字符的程序
中插入一个 Unicode 字符。一个 Unicode 转义字符精确地等价于它所表示的字符。
Unicode 转义字符被设计为用于在程序员需要插入一个不能用源文件字符集表
示的字符的情况。它们主要用于将非 ASCII 字符置于标识符、字符串字面常量、
字符字面常量以及注释中。偶尔地,Unicode 转义字符也被用来在看起来颇为相
似的数个字符中明确地标识其中的某一个,从而增加程序的清晰度。
![](https://csdnimg.cn/release/download_crawler_static/10942736/bg14.jpg)
总之,在字符串和字符字面常量中要优先选择的是转义字符序列,而不是
Unicode 转义字符。Unicode 转义字符可能会因为它们在编译序列中被处理得过
早而引起混乱。不要使用 Unicode 转义字符来表示 ASCII 字符。在字符串和字符
字面常量中,应该使用转义字符序列;对于除这些字面常量之外的情况,应该直
接将 ASCII 字符插入到源文件中。
谜题
谜题谜题
谜题 15
1515
15:
::
:令人晕头转向的
令人晕头转向的令人晕头转向的
令人晕头转向的 Hello
HelloHello
Hello
下面的程序是对一个老生常谈的例子做出了稍许的变化之后的版本。那么,它会
打印出什么呢?
/**
* Generated by the IBM IDL-to-Java compiler, version 1.0
* from F:\TestRoot\apps\a1\units\include\PolicyHome.idl
* Wednesday, June 17, 1998 6:44:40 o’clock AM GMT+00:00
*/
public class Test{
public static void main(String[] args){
System.out.print("Hell");
System.out.println("o world");
}
}
这个谜题看起来相当简单。该程序包含了两条语句,第一条打印 Hell,而第二
条在同一行打印 o world,从而将两个字符串有效地连接在了一起。因此,你可
能期望该程序打印出 Hello world。但是很可惜,你犯了错,实际上,它根本就
通不过编译。
问题在于注释的第三行,它包含了字符\units。这些字符以反斜杠(\)以及紧
跟着的字母 u 开头的,而它(\u)表示的是一个 Unicode 转义字符的开始。遗憾
的是,这些字符后面没有紧跟四个十六进制的数字,因此,这个 Unicode 转义字
符是病构的,而编译器则被要求拒绝该程序。Unicode 转义字符必须是良构的,
即使是出现在注释中也是如此。
在注释中插入一个良构的 Unicode 转义字符是合法的,但是我们几乎没有什么理
由去这么做。程序员有时会在 JavaDoc 注释中使用 Unicode 转义字符来在文档中
生成特殊的字符。
// Unicode 转义字符在 JavaDoc 注释中有问题的用法
/**
* This method calls itself recursively, causing a
* StackOverflowError to be thrown.
* The algorithm is due to Peter von der Ah\u00E9.
*/
这项技术表示了 Unicode 转义字符的一种没什么用处的用法。在 Javadoc 注释中,
应该使用 HTML 实体转义字符来代替 Unicode 转义字符:
剩余161页未读,继续阅读
![rar](https://img-home.csdnimg.cn/images/20210720083606.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![chm](https://img-home.csdnimg.cn/images/20210720083646.png)
![application/pdf](https://img-home.csdnimg.cn/images/20210720083512.png)
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![](https://profile-avatar.csdnimg.cn/default.jpg!1)
采采卷耳
- 粉丝: 0
- 资源: 3
上传资源 快速赚钱
我的内容管理 展开
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助
![](https://csdnimg.cn/release/wenkucmsfe/public/img/voice.245cc511.png)
最新资源
- 贵州煤矿矿井水分类与处理策略:悬浮物、酸性与非酸性
- 醛固酮增多症肾上腺静脉采样对比:ACTH后LR-CAV的最优评估
- 开源云连接传感器监控平台:农业土壤湿度远程监测
- 母婴用品企业年度生产计划线性规划优化模型:实证与应用
- 井下智能变电站:Rogowski线圈电流检测系统的研发与性能验证
- 霍州矿区煤巷稳定性分析及支护策略
- ARM嵌入式系统远程软件更新方案:基于TFTP协议
- 煤炭选煤中汞分布规律与洗选脱汞效果
- 提升码垛机器人性能:拉格朗日动力学模型与滑模模糊控制的应用
- 增强现实技术提升学前手写教学:设计与开发案例
- 不规则工作面沉陷三角剖分算法提升与应用
- 卡尔曼滤波在瞬变电磁干扰压制中的应用研究
- 煤矿安全能力研究:理论与系统构建
- LonWorks总线技术在斜巷运输车辆定位与跑车防护中的应用
- 神东煤炭集团高效煤粉锅炉系统:节能环保新实践
- Ti/SnO2+Sb2Ox/PbO2电极分形维数与电催化性能研究
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
![](https://img-home.csdnimg.cn/images/20220527035711.png)
![](https://img-home.csdnimg.cn/images/20220527035711.png)
![](https://img-home.csdnimg.cn/images/20220527035111.png)
安全验证
文档复制为VIP权益,开通VIP直接复制
![](https://csdnimg.cn/release/wenkucmsfe/public/img/green-success.6a4acb44.png)