沒事的時候,我並不喜歡逛 P 站,而喜歡逛 programcreek 這些技術型網站,因而那天晚上,在夜深人靜的時候,我就發現了一個專一基礎但不容忽視的主題。好比說:Java 中的 null 究竟是什麼鬼?像這類靈魂拷問的主題,很是值得深刻地研究一下。java
null 在 Java 中是一個特殊的存在,由於它和大名鼎鼎的 NullPointerException
(NPE)如影隨形。NPE 的發明人 Tony Hoare 曾在 2009 年認可:「Null References 是一個荒唐的設計,就好像我賭輸掉了十億美圓」。程序員
那爲何 Java 會一直保留着 null,而沒有把它消滅掉呢?我想是由於 null 的存在的確爲 Java 帶來了更多好處(我在下文中指出了一些,看你們能不能發現哦)。web
就讓咱們從下面這個語句開始。app
String s = null;
複製代碼
首先,咱們來回顧一下:什麼是變量,什麼是值。舉個恰當的例子,變量就好像一個窯洞,裏面能夠住人,也能夠不住人,人就至關於值。在 Java 中,若是一個變量要存儲某個值,就須要先聲明是什麼類型。網站
s 爲一個 String 類型的變量,這一點是毫無疑問的,對吧?那確定啊,二哥,你別廢話了,怎麼可能有人懷疑這一點。人工智能
Java 有兩種類型,一種是基本類型,一種是引用類型。聲明爲基本類型的變量存儲的是值,聲明爲引用類型的變量存儲的是對象的引用,這一點想必你們也不懷疑吧。spa
就以前那行語句來講,String 是一個引用類型,值爲 null,也就是說 s 這個變量什麼也沒存儲,就好像一個窯洞裏面什麼人也沒住,同樣。設計
假如 String s = "沉默王二"
,就可使用下面這幅圖來表示。code
而 String s = null
的表現形式則是不一樣的,它沒有可引用的對象。orm
那內存中的 null 究竟是什麼玩意呢?Java 中的 null 究竟是什麼樣的一個值?
無論怎麼樣,null 不是一個有效的對象,因此內存中並無爲它分配空間,沒它的位置。null 僅僅是一種表現符號,代表引用此時沒有指向任何一個對象。Java虛擬機的規範中也沒有規定 null 的具體值。
這不只讓我聯想到了佛經中的一句經典臺詞,想必你們也猜到了,大聲的念出來吧!「色便是空,空便是色。」咱們漢語中的這個「空」字恰到好處地匹配了這個 null
,換句話說就是「null 便是空,空便是 null。」經典啊,經典。
一個類的成員變量若是是引用類型的話,它的默認值就爲 null,這和基本類型有所不一樣。
public class Null {
private String name;
private int age;
public static void main(String[] args) {
Null n = new Null();
System.out.println(n.name);
System.out.println(n.age);
}
}
複製代碼
上面這段代碼的輸出結果是:
null
0
複製代碼
但若是是一個引用類型的局部變量的話,編譯器會提醒咱們對其初始化。
若是一個變量當前沒有肯定要初始化的值,那麼 null 就是最佳選擇,即所謂的延遲初始化,直到實際使用的時候再賦值爲「它實際」的值(null 的第 1 個好處)。更神奇的是,null 居然能夠被強制轉化。
String s = (String) null;
Integer i = (Integer) null;
System.out.println(s);
System.out.println(i);
複製代碼
程序不只能夠編譯經過,運行時也沒有拋出可怕的 NPE。可是呢,Java 仍是有原則的,當把 null 賦值給基本類型變量的時候就會編譯不經過。
編譯器是否是很智能,很人性化,畢竟基本類型和引用類型是不一樣的,null 只能做爲引用類型的初始化值,卻不能做爲基本類型的初始化值,由於基本類型有本身的初始化值,好比說 int 的爲 0。不過,不要過輕信人工智能化,我保證能把編譯器繞暈。
Integer j = null;
int k = j;
System.out.println(k);
複製代碼
先給基本類型的包裝類型變量賦值爲 null,再把該變量賦值給基本類型變量,編譯器就無能爲力了。不過,NPE 會在運行時被揪出來鞭屍了。
關於 null,還有另一個有趣的事實:若是使用了帶有 null 值的引用類型變量,instanceof 將會返回 false。
String s1 = null;
System.out.println(s1 instanceof String); // false
複製代碼
也就是說,若是一個引用類型變量的值不爲 null,而且在使用 instanceof 操做符判斷類型的時候沒有拋出 ClassCastException
,那麼結果就爲 true。不然,即使是爲一個變量明確地聲明瞭類型,好比說 String s1 = null
,instanceof 仍然沒法知道 s1 是 String 類型。
好了,咱們再來看一看 null 的其餘用途,好比說表示對象不存在、終止條件。
下圖是 System.console()
方法的 Javadoc,該方法會返回與當前 Java 虛擬機相關聯的惟一對象(若是有的話);若是沒有的話,返回 null。
想一想看,若是 Java 沒有保留 null 的話,要返回什麼呢?至少得再定義一個和 null 差很少意義的關鍵字。
再來看看另一個例子,來自 java.io.BufferedReader
類的 readLine()
方法
public String readLine() throws IOException
Returns: A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached.
複製代碼
該方法會一行一行地返回讀取的字符串,直到流的結尾。怎麼判斷到了流的結尾呢,返回 null。這樣的話,咱們就能夠把判 null 做爲讀取字符串的條件。
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
複製代碼
固然了,也能夠返回其餘的關鍵字,好比說 -1,來表示 readLine()
到了流的末尾,但這樣的作法和返回 null 沒有多大的分別。
最後,奉上一句羅素同窗的名言:「存在即合理。」null 既然已經存在了,天然有它存在的道理,無論它的功過是非。
好了,各位讀者朋友們,以上就是本文的所有內容了。能看到這裏的都是最優秀的程序員,升職加薪就是你了👍。原創不易,若是以爲有點用的話,請不要吝嗇你手中點讚的權力。
PS:固然還有更多好看的 Java 原創文章,比較多,這裏不便一一列舉,感興趣的能夠到公衆號【沉默王二】回覆【null】獲取。