1 public static String valueOf(Object obj) { 2 return (obj == null) ? "null" : obj.toString(); 3 }
以上會報空指針異常;而下面這樣就可以打印 null。
1 Object obj = null; 2 System.out.println(String.valueOf(obj));
這種方式能正常運行,原因如下:debug 代碼會發現,兩種方式執行了不同的重載方法,第一種執行了以下方法
1 public static String valueOf(char data[]) { 2 return new String(data); 3 }
第二種執行了以下方法,
1 public static String valueOf(Object obj) { 2 return (obj == null) ? "null" : obj.toString(); 3 }
如果對重載不熟悉,很難解釋其中原因;當然null
是另一個讓人頭疼的問題,
重載
Java 的重載解析過程是以兩階段運行的:
- 第一階段:選取所有可獲得並且可應用的方法或構造器。
- 第二階段:在第一階段選取的方法或構造器中選取最精確的一個,如果一個方法或構造器可以接受傳遞給另一個方法或構造器的任何參數,那么我們就說第一個方法比第二個方法缺乏精確性。
java.lang.String 中 valueOf() 所有重載方法
1 static String valueOf(boolean b) 2 static String valueOf(char c) 3 static String valueOf(char[] data) 4 static String valueOf(char[] data, int offset, int count) 5 static String valueOf(double d) 6 static String valueOf(float f) 7 static String valueOf(int i) 8 static String valueOf(long l) 9 static String valueOf(Object obj)
因為基本類型不能賦值 null,String.valueOf(null) 只能匹配 valueOf(char[] data)
和 valueOf(Object obj)
;而 valueOf(char data[])
更精確,所以選擇執行 valueOf(char data[])
。
再看一個例子:
1 public static void hah(Integer i) { 2 System.out.println(i); 3 } 4
5 public static void hah(Long l) { 6 System.out.println(l); 7 } 8
9 private static void hah(Object o){ 10 System.out.println(o); 11 }
null
null 有類型嗎?null 是一個值還是一個對象?
我們知道若instanceof
左邊為null
,那么不論右邊是什么類,直接返回 false;至少可以知道 null 不是對象;其實執行valueOf(char[] data)
方法也證明了這點。
null 有類型嗎?null 是一個值還是一個對象?
我們知道若instanceof
左邊為null
,那么不論右邊是什么類,直接返回 false;至少可以知道 null 不是對象;其實執行valueOf(char[] data)
方法也證明了這點。
1 System.out.println(String.valueOf((Integer)null));
null 造成的NullPointerException
大概是最常見的異常,不論是 JDK 還是第三方類庫都做了很多工作盡可能的避免空指針異常;比如 Apache Commons 的 collections、lang 判空,Guava 的 Optional 等;甚至 Optional 類已經成為 Java 8 類庫的一部分。
或許,大家認為這兩段代碼都會拋出空指針異常;其實,第二段代碼會正常執行。
hah() 方法是上面例子中定義的方法,它用 static 修飾是靜態方法;對於靜態方法和靜態變量,使用了靜態綁定,並不會拋出空指針異常;但是像這種對象訪問類成員的寫法最好不要使用,很容易造成誤解。
1 String s = null; 2 System.out.println(s.length());
1 Demo demo = null; 2 demo.hah("hello");
靜態綁定就是在程序執行前方法已經被綁定(在編譯期中已經確定);比如 demo.hah("hello") 這個代碼,反編譯 class 文件得到:
1 Demo demo = null; 2 hah((Object)"hello");
你會發現編譯后方法調用已經和 demo 沒有關系,必然不會拋出空指針異常。