一、符号谜题
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.print("2 + 2 = " + 2+2);
System.out.println('H'+'a');
这种代码不会报错,因为”:“的作用。
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)的输出 流,就有可能会导致子进程的阻塞,甚至是死锁
故在调用之前需要排空输出流:
// MasterProcess 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.