天天看點

吃人的那些 Java 名詞:對象、引用、堆、棧

作為一個有着 8 年 Java 程式設計經驗的 IT 老兵,說起來很慚愧,我被 Java 當中的四五個名詞一直困擾着:對象、引用、堆、棧、堆棧(棧可同堆棧,是以是四個名詞,也是五個名詞)。每次我看到這幾個名詞,都隐隐約約覺得自己在被一隻無形的大口慢慢地吞噬,隻剩下滿地的衣服碎屑(為什麼不是骨頭,因為骨頭也好吃)。

記得中學的課本上,有一篇名為《狂人日記》課文;那時候根本了解不了魯迅寫這篇文章要表達的中心思想,隻覺得滿篇的“吃人”令人心情壓抑;老師在講台上慷慨激昂的講,大多數的同學同我一樣,在課本面前“癡癡”的發呆。

十幾年後,再讀《狂人日記》,恍然如夢:

魯迅先生以狂人的口吻,再現了動亂時期下中國人的精神狀态,視角新穎,文筆細膩又不乏辛辣之味。

當時的中國,混亂成了主色調。以清廷和孔教為主的封建舊思想還在潛移默化地影響着人們的思想,與此同時以革命和新思潮為主的現代思想已經開始了對大衆靈魂的洗滌和沖擊。

最近,和沉默王二技術交流群(120926808)的群友們交流後,Java 中那四五個會吃人的名詞:對象、引用、堆、棧、堆棧,似乎在腦海中也清晰了起來,盡管疑惑有時候仍然會在陰雲密布時跑出來——正鑒于此,這篇文章恰好做一下歸納。

一、對象和引用

在 Java 中,盡管一切都可以看做是對象,但計算機操作的并非對象本身,而是對象的引用。 這話乍眼一看,似懂非懂。究竟什麼是對象,什麼又是引用呢?

先來看對象的定義:按照通俗的說法,每個對象都是某個類(class)的一個執行個體(instance)。那麼,執行個體化的過程怎麼描述呢?來看代碼(類是 String):

new String("我是對象張三");

new String("我是對象李四");

在 Java 中,執行個體化指的就是通過關鍵字“new”來建立對象的過程。以上代碼在運作時就會建立兩個對象——“我是對象張三"和"我是對象李四”;現在,該怎麼操作他們呢?

去過公園的同學應該會見過幾個大爺,他們很有一番本領——個個都能把風筝飛得老高老高,徒留我們眼饞的份!風筝飛那麼高,沒辦法直接用手拽着飛啊,全要靠一根長長的看不見的結實的繩子來牽引!操作 Java 對象也是這個理,得有一根繩——也就是接下來要介紹的“引用”(我們肉眼也常常看不見它)。

String zhangsan, lisi;

zhangsan = new String("我是對象張三");

lisi = new String("我是對象李四");

這三行代碼該怎麼了解呢?

先來看第一行代碼:String zhangsan, lisi;——聲明了兩個變量 zhangsan 和 lisi,他們的類型為 String。

①、歧義:zhangsan 和 lisi 此時被稱為引用。

你也許聽過這樣一句古文:“神之于形,猶利之于刀;未聞刀沒而利存,豈容形亡而神在?”這是無神論者範缜(zhen)的名言,大緻的意思就是:靈魂對于肉體來說,就像刀刃對于刀身;從沒聽說過刀身都沒了刀刃還存在,那麼怎麼可能允許肉體死亡了而靈魂還在呢?

“引用”之于對象,就好比刀刃之于刀身,對象還沒有建立,又怎麼存在對象的“引用”呢?

如果 zhangsan 和 lisi 此時不能被稱為“引用”,那麼他們是什麼呢?答案很簡單,就是變量啊!(鄙人了解)

②、誤解:zhangsan 和 lisi 此時的預設值為 null。

應該說 zhangsan 和 lisi 此時的值為 undefined——借用 JavaScript 的關鍵字;也就是未定義;或者應該是一個新的關鍵字 uninitialized——未初始化。但不管是 undefined 還是 uninitialized,都與 null 不同。

既然沒有初始化,zhangsan 和 lisi 此時就不能被使用。假如強行使用的話,編譯器就會報錯,提醒 zhangsan 和 lisi 還沒有出生(初始化);見下圖。

吃人的那些 Java 名詞:對象、引用、堆、棧

如果把 zhangsan 和 lisi 初始化為 null,編譯器是認可的(見下圖);由此可見,zhangsan 和 lisi 此時的預設值不為 null。

吃人的那些 Java 名詞:對象、引用、堆、棧

再來看第二行代碼:zhangsan = new String("我是對象張三");——建立“我是對象張三"的 String 類對象,并将其指派給 zhangsan 這個變量。

此時,zhangsan 就是"我是對象張三"的引用;“=”操作符賦予了 zhangsan 這樣神聖的權利。

第三行代碼 lisi = new String("我是對象李四");和第二行代碼 zhangsan = new String("我是對象張三");同理。

現在,我可以下這樣一個結論了——對象是通過 new 關鍵字建立的;引用是依賴于對象的;= 操作符把對象指派給了引用。

我們再來看這樣一段代碼:

String zhangsan, lisi;
zhangsan = new String("我是對象張三");
lisi = new String("我是對象李四");
zhangsan = lisi;      

當 zhangsan = lisi; 執行過後,zhangsan 就不再是"我是對象張三"的引用了;zhangsan 和 lisi 指向了同一個對象(“我是對象李四”);是以,你知道 System.out.println(zhangsan == lisi); 列印的是 false 還是 true 了嗎?