前言:突然有一個疑問,爲什麼容器可以添加null,比如List、Set、Map。
HashMap<String, String> map = new HashMap<>();
map.put(null, "1");
map.put("123", null);
System.out.println("null:" + map.get(null));
System.out.println("123:" + map.get("123"));
Set<Integer> set = new HashSet<>();
set.add(null);
set.add(null);
set.add(null);
System.out.println(Arrays.toString(set.toArray()));
運行結果表示,Map可以將null作爲key或者value,Set只能存一次null。
我目前的理解是,null可以看成一個特殊的實例化對象,但我們無法使用它的任何屬性或者方法(空指針異常),因此在很多情況下都要進行非空判斷。
關鍵字null
在Java中,null是一種特殊的引用數據類型,表示絕對意義上的空,即不存在。
String string;
System.out.println(string);
比如上面的代碼,由於string沒有初始化,string沒有指向任何地址,相當於不存在,編譯會報錯。
下面的代碼編譯可以通過。
String string = null;
System.out.println(string);
但此時string仍然是“不存在”的,使用string的任何屬性或者方法,比如說isEmpty(),會拋出一個運行時異常NullPointerException。
1. null和""
""是一個已經實例化的對象,和null不相等,比如直接==返回false,equals也返回false。
String string1 = null;
String string2 = "";
System.out.println("null == \"\":" + (string1 == string2));
System.out.println("\"\" equal null:" + string2.equals(string1));
2. null到底屬於哪一種類型?
null可以強制轉化爲任意一種引用類型,比如8種包裝類型,示例代碼如下。
Integer integer = (Integer) null;
System.out.println(null instanceof Object); //false
System.out.println(integer instanceof Object); //false
System.out.println(integer); //null
System.out.println(integer.valueOf("123")); //123
System.out.println(((Integer)null).valueOf("123")); //123
System.out.println(integer instanceof Integer); //false
System.out.println(null == null); //true
System.out.println(((Integer)null) == ((Integer)null)); //true
System.out.println(((Short)null) == ((Integer)null)); //編譯不通過,不可比較的類型
運行結果表明,null不是超類Object的一個(子)類實例,null不屬於任何一種類。
null經過了強制類型轉換後,類的靜態屬性(比如Integer.valueOf)可以正常使用,但非靜態屬性仍然會導致異常。但最後的instanceof返回爲false,表明經過轉換後的null仍然並不屬於任何一種類型。
此外,同種類型的null可以比較,不同類型比較編譯報錯。
3. jvm相關命令
我找到了一個NullType接口,此外並沒有找到其它的資料了。
/**
* Represents the null type.
* This is the type of the expression {@code null},
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ahé
* @since 1.6
*/
public interface NullType extends ReferenceType {
}
嘗試去看看jvm是怎麼支持null的,嘗試了下面的示例代碼
public class NullDemo {
public static void main(String[] args) {
Integer integer = (Integer) null;
System.out.println(null == null);
System.out.println(((Integer) null) == ((Integer) null));
}
}
反彙編結果如下
似乎得到了兩條相關命令,java12文檔的介紹如下,aconst_null命令的功能是將null對象引用壓入棧。ifnonnull命令則是彈出棧頂元素,當不爲null時跳轉,而對應的ifnull則是爲null時跳轉。在文檔對於aconst_null的描述中指出,jvm並沒有爲null指定一個具體的值(可能是我翻譯地不對= =)。
目前理解,null本身代表不確定的對象,可以理解爲一個比較特殊的實例對象,但並不屬於任何一種類型。唉,花了一個晚上,似乎還是沒有弄懂null到底怎麼實現的,先挖坑日後再填吧= =