一、符号谜题

1. 奇数性
错误答案:
public static boolean isOdd(int i){
return i % 2 == 1;
}
所有负数取余,次函数都会返回false.
正确答案:
public static boolean isOdd(int i){
return i % 2 != 0;
}
更好性能采用:
public static boolean isOdd(int i){
return (i & 1) != 0;
}
原因:
 
2. double位数打印问题
System.out.println(2.00 - 1.10);
将会打印0.8999999999999999 .
解决方案,如果位数较少就不使用double; 如果位数较多,使用BigDecimal.
 
3.
public static void main(String args[]){
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
将会打印5,存在溢出问题。
乘法中都是以int进行计算,在赋值的时候才提升到long,这个时候已经发生溢出。
解决方案:

final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;
4. System.out.println(12345+5432l);
将打印17777,因为5432l的最后一个不是1,而是L。
 
5. System.out.println(Long.toHexString(0x100000000L + 0xcafebabe));
将会打印cafebabe,而不是1cafebabe,混合类型计算导致。

如果十六进制和八进制字
面常量的最高位被置位了,那么它们就是负数。在这个程序中,数字0xcafebabe
是一个int 常量,它的最高位被置位了,所以它是一个负数。它等于十进制数值
-889275714。
 
6. System.out.println((int)(char)(byte) -1);
将打印65535。符号扩展和零扩展的问题。
 
7. 三元操作符
char x = 'X';
int i = 0;
System.out.println(true ? x : 0);
System.out.println(false ? i : x);
将会打印88,
如果一个操作数的类型是T,T 表示byte、short 或char,而另一个操作
数是一个int 类型的常量表达式,它的值是可以用类型T 表示的,那么条
件表达式的类型就是T。
否则,将对操作数类型运用二进制数字提升,而条件表达式的类型就是第
二个和第三个操作数被提升之后的类型。
8. 使用复合赋值时 如+= *= 注意隐式的类型转换。
short x = 0;
int i = 123456;
复合赋值编译将不会产生任何错误:
x += i; // 包含了一个隐藏的转型!

二、字符谜题

 
9.
System.out.print("H"+"a");
System.out.print('H'+'a');
第二个将会打印出169
 
10.
final String pig = "length: 10";
final String dog = "length: " + pig.length();
System.out. println("Animals are equal: "
+ pig == dog);
将会打印false, 执行过程为:((
"Animals are equal: "+ pig) == dog)
 
11.
System.out.println("a\u0022.length()
+\u0022b".length());
编译器在将程序解析成各种符号之前,先将Unicode
转义字符转换成为它们所表示的字符[JLS 3.2]。
 
13.
/**
* 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
*/
注释 unicode编码转义问题。程序代码也存在这个问题
 
14. String(Byte[] )构造器不稳定, 使用时最好指定字符集 String(Byte[] , String charset )
 
15.
replaceAll(String, String) 第一个参数为正则表达式,replaceAll(".", "\\")会替换每一个单字符,解决方案:
.replaceAll("\\." , "/") 或者
replaceAll(Pattern.quote("."),"/")
另外 String.replace(String, String) 的功能和replaceAll的功能一样,不同的是,全部按照字面值处理
 
16.
跨平台文件分隔符:File.separator ,
17. 在java代码中,
System.out.println('H'+'a');
System.out.print("2 + 2 = " + 2+2);
这种代码不会报错,因为”:“的作用。
 
18. 注意StringBuffer的构造器,没有针对char的constructor, 会强转为int,然后作为容量进行初始化的,可以使用针对String的构造函数。
一个char更像int而不是Stirng.
 

三、循环谜题

19. new Random().nextInt(int)等函数存在栅栏柱错误(fencepost error ),即达不到声明的长度,只能无限接近,但是在程序里,就会错1.
如果你要建造一个
100 英尺长的栅栏,其栅栏柱间隔为10 英尺,那么你需要多少根栅栏柱呢?11
根或9 根都是正确答案,这取决于是否要在栅栏的两端树立栅栏柱,但是10 根
却是错误的
 
20.
byte 与 int的比较 注意byte的范围,而且byte和int 的==操作,永远返回的都是false。需要强制转换的。
 
21. j = j++; 不会对j的值做任何更改。相当于
int tmp = j; j = j + 1; j = tmp;
 
22.
以下程序将会无限循环,当等于END时,再加1,END将变为Integer.MIN_VALUE。使用long,或者去掉=
int END = Integer.MAX_VALUE;
int START = END - 100;
int count = 0;
for (int i = START; i <= END; i++)
count++;
System.out.println(count);
 
23.
三个移位操作符:<<、>>和>>>。移位长度总是介于0 到31 之间,如果左操作数是long 类型的,则介于0 到63 之间。这个长度是对32取余的,如果左操作数是long 类型的,则对64 取余。如果试图对一个int 数值移位32 位,或者是对一个long 数值移位64 位,都只能返回这个数值自身的值。
右移操作符总是起到右移的作用,而左移操作符也总是起到左移的
作用。负的移位长度通过只保留低5 位而剔除其他位的方式被转换成了正的移位长度——如果左操作数是long 类型的,则保留低6 位。因此,如果要将一个int数值左移,其移位长度为-1,那么移位的效果是它被左移了31 位。
 
移位长度是对32 取余的,或者如果左操作数是long 类型的,则对64 取
 
2012年4月26日 23:42:58 29,P39
24.
i != i的情况成立,当i为NaN的时候,double和float都有这个值。可以使用Double.NaN获得
 
25. 不使用浮点数 使得while (i != i + 0) {
} 无限循环,——String
 
26.
请提供一个对i 的声明,将下面的循环转变为一个无限循环:
while (i != 0) {
i >>>= 1;
}
i为short byte等长度小于int的类型的任意负值可以实现。先转型为int值,使用符号拓展,在进行强转为窄类型会导致丢失信息。
复合赋值一定要注意类型问题
 
27. 如何使while (i <= j && j <= i && i != j) {
} 无限循环下去:Integer i = new Integer(0); Integer j = new Integer(0);
前两个执行数值比较,第三个执行标识比较,so...
new Integer(0) == new Integer(0))为false。new Integer(0) == 0 为true.
 
28. 使 while (i != 0 && i == -i) {} 无限循环——int i = Integer.MIN_VALUE; 或者long i = Long.MIN_VALUE;
对于每一种有符号的整数类型(int、long、byte 和short),负的数值总是比正的数值多一个,这个多出来的值总是这种类型所能表示的最小数值。使用时小心溢出。
 
2012年4月27日 11:02:42
34 p45
29. 不要使用float作为循环索引,因为当float等浮点型,足够大时,+50不会对值不会产生影响
final int START = 2000000000;
int count = 0;
for (float f = START; f < START + 50; f++)
count++;
System.out.println(count);
将立即结束,因为循环条件不成立。
50 的二进制表示只需要6 位,所以将50 加到2,000,000,000
上不会对右边6 位之外的其他为产生影响。
从右边数过来的第7 位和第8 位仍旧是0。提升这个31 位的int 到具有24 位精度的float 会在第7 位和第8 位之间四舍五入,从而直接丢弃最右边的7 位
 
30. ms % 60*1000 == 0 不等同于 (ms % 60000 == 0) ——%和 *有相同的优先级
 

四。异常谜题

 
31. 下面的程序将返回false, 返回的true会被抛弃
static boolean decision() {
try {
return true;
} finally {
return false;
}
}
32. 第一个不能打印goodbye,第二个方式可以
try {
System.out.println("Hello world");
System.exit(0);
} finally {
System.out.println("Goodbye world");
}
//###############################
Runtime.getRuntime().addShutdownHook(
new Thread() {
public void run() {
System.out.println("Goodbye world");
}
});
System.exit(0);
}
}
33. 下面的程序会因为无限递归而进StackOverFlow,
public class Reluctant {
private Reluctant internalInstance = new Reluctant();
 
public Reluctant() throws Exception {
throw new Exception("I'm not coming out");
}
 
public static void main(String[] args) {
try {
Reluctant b = new Reluctant();
System.out.println("Surprise!");
} catch (Exception ex) {
System.out.println("I told you so");
}
}
}
 
34. 在关闭流的时候,会抛出异常,关闭操作一般在finally子句中执行,所以正确并且优雅的方案是:
} finally {
closeIgnoringException(in);
closeIgnoringEcception(out);
}
private static void closeIgnoringException(Closeable c) {
if (c != null) {
try {
c.close();
} catch (IOException ex) {
// There is nothing we can do if close fails
}
}
35. 下面的函数接近无限循环(虽然最终会抛出stackoverflow异常),执行方式类似于先序遍历完全二叉树。
public static void main(String[] args) {
workHard();
System.out.println("It's nap time.");
}
private static void workHard() {
try {
workHard();
} finally {
workHard();
}
}

五、类谜题

六、库谜题

 
36. 千万不要在一个整型字面常量的前面加上一个0;这会使它变成一个八进制字面常量
 
37. 不要因为偶然地添加了一个返回类型,而将一个构造器声明变成了一个方法声明
 
38. Math.abs() ——如果其参数等于Integer.MIN_VALUE等MIN_VALUE,那么产生的结果与该参数相同 ,也是负值
 
2012年4月29日 19:09:34 p100
 
39. 下面是一个经常使用到的比较器,但是该比较器存在一个很大的问题,溢出问题。
public int compare(Integer i1, Integer i2) {
return i2 - i1;
}
下面的比较器可以正常工作。
public int compare(Integer i1, Integer i2) {
return (i2 < i1 ? -1 : (i2 == i1 ? 0 :1));
}
 

Java谜题7——更多的类谜题

 
40. 避免重用java包内自带的类名,会带来意想不到的问题,比如:
public class StrungOut {
public static void main(String[] args) {
String s = new String("Hello world");
System.out.println(s);
}
}
class String {
private final java.lang.String s;
public String(java.lang.String s) {
this.s = s;
}
public java.lang.String toString() {
return s;
}
}
上面的程序,会因为main方法的参数不对而报找不到main方法的错误。
 
41. 静态导入import static 。。。对可读性有很大的影响,而且遮蔽可能会导致问题,尽量少用。
不定参数也存在同样问题
 
42. 重用变量名不是一个好主意,尽量选择方法覆盖的方式实现。另外,final 修饰符不影响重用该变量名。
重用名字是危险的;应该避免隐藏、遮蔽和遮掩
 

Java谜题7——更多的库谜题

2012年5月2日 23:09:22 P129
43. 反射相关的东西就不是很清楚了,Method.invoke()感觉功能很强大....
 
44. Process类拥有一个waitFor方法,返回值为执行的状态值,执行过程是阻塞式的。
但是有一点需要注意:
由于某些本地平
台只提供有限大小的缓冲,所以如果未能迅速地读取子进程(subprocess)的输出
流,就有可能会导致子进程的阻塞,甚至是死锁
故在调用之前需要排空输出流:
// Master
Process process = Runtime.getRuntime().exec(COMMAND);
int exitValue = process.waitFor();
System.out.println("exit value = " + exitValue);
改为:
Process process = Runtime.getRuntime().exec(COMMAND);
drainInBackground(process.getInputStream());
int exitValue = process.waitFor();
System.out.println("exit value = " + exitValue);
调用的排空输出流的方法为:
static void drainInBackground(final InputStream is) {
new Thread(new Runnable(){
public void run(){
try{
while( is.read() >= 0 );
} catch(IOException e){
// return on IOException
}
}
}).start();
}
2012年5月2日 23:42:31 p138

 

Java谜题9——高级谜题

 
45.
常量变量将会被编译进那些引用它们的类中。一个常量变量就是任何被常
量表达式初始化的原始类型或字符串变量。令人惊讶的是,null 不是一个常量
表达式。
 
46.