最近在工作中,经常发现java中的一些小细节,自己以前都没有注意,发现之后还是有点新鲜的,不过都是很基础的东西(也许是自己java基础太过薄弱,嘿嘿),就记录在这里,以予自乐。
1、 方法重载的问题
private Test(Object o) ...{
System.out.println("Object");
}
private Test(double[] o) ...{
System.out.println("double array");
}
public static void main(String[] args) ...{
new Test(null);
}
}
上述代码的输出结果会是什么呢?"Object"?"double array"?还是其它?
答案是"double array",由于null的特殊性,在这里看似既符合Object有符合double[],但是java的设计者似乎懂得近水楼台先得月这个俗语,于是java对象的匹配顺序是从低层次到高层次,数组对象继承于Object,也就是层次比Object低,因此这里输出的是"double array"而不是"Object"。
2、奇数的问题
是否能用下面的方法确定其唯一参数是奇数?
System.out.println(i % 2);
return i % 2 == 1;
}
看到这个你就会想到小学老师教的“能被2整除的是偶数,不能被2整除的是奇数”,于是乎很自然的就认为这是正确的。那么我告诉你正确答案:错!大错特错!翻翻教科书,奇数跟偶数的概念只适用于自然数,而这里的参数i是int型的,它包含了负数,因此也就错了!(TX们,看书要仔细啊!)
3、无符号右移的问题
i >>>= 1;
}
上面的代码中,要声明怎样的一个i,才能使循环变成无限循环?
很难想吧?那就提供几个选项缩小范围吧:
A short i = -1
B int i = -1
C long i = -1L
D byte i = -1
E char i = 1
还是不知道答案吧?那就告诉你吧,答案应该是A跟D。
“>>>”在java中被称为:“无符号”右移位运算符,它使用了“零扩展”:无论正负,都在高位插入0。而对char,byte或者short进行移位处理,那么在移位进行之前,它们会自动转换成一个int,因此“零扩展”不再发生,于是A跟D无论怎么移始终都等于-1,也就变成了无限循环。
4、try-catch的问题
public static void main(String[] args) ...{
try ...{
System.out.print("1");
Runtime.getRuntime().addShutdownHook(new Thread() ...{
public void run() ...{
System.out.print("2");
}
});
System.exit(0);
} finally ...{
System.out.print("3");
}
}
}
上面的代码有错吗?错在哪?如果没有错,那么其输出结果又是什么?
答案:代码没有错,其输出结果是“12”。
很出乎意料是不是?如果不是真的运行了程序,相信很多朋友都说不出正确结果,这么短短的一段代码却藏着多处陷阱。“try后必须紧跟至少一个catch…”,“finally的代码无论如何都要执行…”,相信很多朋友都可以这么背出来。但是真实的情况如你所见,try可以跳过catch直接跟finally的,而且finally的代码也有不被执行的情况。不明白?那你再仔细看看那句System.exit(0)吧,它意味着什么呢?执行它就代表你的程序(线程)结束,既然程序都结束了,finally当然就执行不到了,这跟return是有区别的,要特别注意!
既然提到try的问题,那就顺便把在网上看到的另外一篇介绍try之古怪的文章转过来吧,相信你看了后会大跌眼镜!
原帖:http://ajaxgo.javaeye.com/blog/118979
==========================以下内容为转载===============================
今天无意中,同事发现了一个有趣的现象,首先来看一下以下代码:
java 代码会发现,18行加载了一下变量b中的值,所以,其实,在执行代码的第8行时,并不是执行return,而只是往内存中存储变量b的值。
private static int todo() ...{
int b=1;
try...{
int a=2/0;
} catch(Exception e) ...{
System.out.println("in catch");
return 1;
} finally ...{
System.out.println("in final");
}
return 2;
}
/** *//**
* @param args
*/
public static void main(String[] args) ...{
System.out.println(todo());
}
}
此时,运行结果为
in catch
in final
1
看到这里,少部分人已经开始不明白了,我们继续看另一则代码(请注意细小的差别):
java 代码
private static int todo() ...{
int b=1;
try...{
int a=2/0;
} catch(Exception e) ...{
System.out.println("in catch");
return b;
} finally ...{
System.out.println("in final");
}
return 2;
}
/** *//**
* @param args
*/
public static void main(String[] args) ...{
System.out.println(todo());
}
}
此时,运行结果依旧是
in catch
in final
1
但是通过单步执行,会发现,两种代码的执行顺序有少许差别
第一种代码的try/catch单步执行顺序为
7->10->8
第二种代码的try/catch单步执行顺序为
7->8->10->8
这里我们对其进行了研究,从中间码入手,首先来分别看看两者的中间码:
上述第一块代码的中间码
11 ldc "in catch"> [22]
13 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
16 getstatic java.lang.System.out : java.io.PrintStream [16]
19 ldc "in final"> [30]
21 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
24 iconst_1
25 ireturn
可以发现,明显的执行顺序,1行把装入字符串转入内存,2行打印,4行在把字符串装入内存,5行再打印,7行返回
上述第二块代码的中间码
13 ldc "in catch"> [22]
15 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
18 iload_0 [b]
19 istore_3
20 getstatic java.lang.System.out : java.io.PrintStream [16]
23 ldc "in final"> [30]
25 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
28 iload_3
29 ireturn
以上的过程,分析下中间码,还是可以理解的,但是非常非常让人奇怪的是,如下的代码(请还是注意不同的细节)
java 代码
private static int todo() ...{
int b=1;
try...{
int a=2/0;
} catch(Exception e) ...{
System.out.println("in catch");
return b;
} finally ...{
b=3;
System.out.println("in final");
}
return 2;
}
/** *//**
* @param args
*/
public static void main(String[] args) ...{
System.out.println(todo());
}
}
此时运行结果照样是
in catch
in final
1
单步执行结果是 7->8->10->11->8
想不通的是,根据单步执行,在finally里对b进行了修改,但是,返回的仍旧是1
再来看看这个代码的中间码:
第三个代码的中间码
13 ldc "in catch"> [22]
15 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
18 iload_0 [b]
19 istore_3
20 iconst_3
21 istore_0 [b]
22 getstatic java.lang.System.out : java.io.PrintStream [16]
25 ldc "in final"> [30]
27 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
30 iload_3
31 ireturn
因为,其实我没怎么学过汇编,只是初步的分析了
3行加载b的地址,4行在地址3储存b的值(此时地址3的值为1),5行加载一个常量,6行把常量存入b的地址(此时b的值为3),10行加载地址3的值,最后返回值。
不知道这样的解释是否合理,也请,希望能够得到一个对于try./catch块执行问题的通俗答案,谢谢!
==========================以上内容为转载===============================
好了,由于时间关系,本次拾遗就到此结束了,以后会陆续补充些其它经典问题上来,一来供自己备忘,二来给java初学者一些警示,希望通过此文能减少大家编码时出现的错误!哪怕是有一位朋友觉得有用,我也会非常高兴!