記得中學的課本上,有一篇名為《狂人日記》課文;那時候根本理解不了魯迅寫這篇文章要表達的中心思想,只覺得滿篇的“吃人”令人心情壓抑;老師在講臺上慷慨激昂的講,大多數的同學同我一樣,在課本面前...
記得中學的課本上,有一篇名為《狂人日記》課文;那時候根本理解不了魯迅寫這篇文章要表達的中心思想,只覺得滿篇的“吃人”令人心情壓抑;老師在講臺上慷慨激昂的講,大多數的同學同我一樣,在課本面前“癡癡”的發呆。
作為一個有著8年Java編程經驗的IT老兵,說起來很慚愧,我被Java當中的四五個名詞一直困擾著:對象、引用、堆、棧、堆棧(棧可同堆棧,因此是四個名詞,也是五個名詞)。每次我看到這幾個名詞,都隱隱約約覺得自己在被一只無形的大口慢慢地吞噬,只剩下滿地的衣服碎屑(為什么不是骨頭,因為骨頭也好吃)。
十幾年后,再讀《狂人日記》,恍然如夢:
魯迅先生以狂人的口吻,再現了動亂時期下中國人的精神狀態,視角新穎,文筆細膩又不乏辛辣之味。
當時的中國,混亂成了主色調。以清廷和孔教為主的封建舊思想還在潛移默化地影響著人們的思想,與此同時以革命和新思潮為主的現代思想已經開始了對大眾靈魂的洗滌和沖擊。
最近,和沉默王二技術交流群(120926808)的群友們交流后,Java中那四五個會吃人的名詞:對象、引用、堆、棧、堆棧,似乎在腦海中也清晰了起來,盡管疑惑有時候仍然會在陰云密布時跑出來——正鑒于此,這篇文章恰好做一下歸納。
一、對象和引用
在Java中,盡管一切都可以看做是對象,但計算機操作的并非對象本身,而是對象的引用。 這話乍眼一看,似懂非懂。究竟什么是對象,什么又是引用呢?
先來看對象的定義:按照通俗的說法,每個對象都是某個類(class)的一個實例(instance)。那么,實例化的過程怎么描述呢?來看代碼(類是String):
new String("我是對象張三");
new String("我是對象李四");
12
在Java中,實例化指的就是通過關鍵字“new”來創建對象的過程。以上代碼在運行時就會創建兩個對象——“我是對象張三"和"我是對象李四”;現在,該怎么操作他們呢?
我們都去過公園,見過幾個大爺,他們很有一番本領——個個都能把風箏飛得老高老高,徒留我們眼饞的份!風箏飛那么高,沒辦法直接用手拽著飛啊,全要靠一根長長的看不見的結實的繩子來牽引!操作Java對象也是這個理,得有一根繩——也就是接下來要介紹的“引用”(我們肉眼也常常看不見它)。
String zhangsan, lisi;
zhangsan = new String("我是對象張三");
lisi = new String("我是對象李四");
123
這三行代碼該怎么理解呢?
先來看第一行代碼: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還沒有出生(初始化);見下圖。
如果把zhangsan和lisi初始化為null,編譯器是認可的(見下圖);由此可見,zhangsan和lisi此時的默認值不為null。
再來看第二行代碼: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;
1234
當zhangsan = lisi;執行過后,zhangsan就不再是"我是對象張三"的引用了;zhangsan和lisi指向了同一個對象(“我是對象李四”);因此,你知道System.out.println(zhangsan == lisi);打印的是false還是true了嗎?
二、堆、棧、堆棧
誰來告訴我,為什么有很多地方(書、博客等等)把棧叫做堆棧,把堆棧叫做棧?搞得我都頭暈目眩了——繞著門柱估計轉了80圈,不暈才怪!
我查了一下金山詞霸,結果如下:
我的天吶,更暈了,有沒有!怎么才能不暈呢?我這里有幾招武功秘籍,你們盡管拿去一睹為快:
1)以后再看到堆、棧、堆棧三個在一起打牌的時候,直接把“堆棧”踢出去;這仨人不適合在一起玩,因為堆和棧才是老相好;你“堆棧”來這插一腳算怎么回事;這世界上只存在“堆、棧”或者“堆棧”(標點符號很重要哦)。
2)堆是在程序運行時在內存中申請的空間(可理解為動態的過程);切記,不是在編譯時;因此,Java中的對象就放在這里,這樣做的好處就是:
當需要一個對象時,只需要通過new關鍵字寫一行代碼即可,當執行這行代碼時,會自動在內存的“堆”區分配空間——這樣就很靈活。
另外,需要記住,堆遵循“先進后出”的規則。就好像,一個和尚去挑了一擔水,然后把一擔水裝缸里面,等到他口渴的時候他再用瓢舀出來喝。請放肆地打開你的腦洞腦補一下這個流程:缸底的水是先進去的,但后出來的。所以,我建議這位和尚在缸上貼個標簽——保質期90天,過期飲用,后果自負!
還是記不住,看下圖:
(不好意思,這是鼎,不是缸,將就一下哈)
3)棧,又名堆棧(簡直了,完全不符合程序員的思維啊,我們陳許愿習慣說一就是一,說二就是二嘛),能夠和處理器(CPU,也就是腦子)直接關聯,因此訪問速度更快;舉個十分不恰當的例子哈——眼睛相對嘴巴是離腦子近的一方,因此,你可以一目十行,但絕對做不到一開口就讀十行字,哪怕十個字也做不到。
既然訪問速度快,要好好利用啊!Java就把對象的引用放在棧里。為什么呢?因為引用的使用頻率高嗎?
不是的,因為Java在編譯程序時,必須明確的知道存儲在棧里的東西的生命周期,否則就沒法釋放舊的內存來開辟新的內存空間存放引用——空間就那么大,前浪要把后浪拍死在沙灘上啊。
現在清楚堆、棧和堆棧了吧?
三、特殊的“對象”
先來看《Java編程思想》中的一段話:
在程序設計中經常用到一系列類型,他們需要特殊對待。之所以特殊對待,是因為new將對象存儲于“堆”中,故用new創建一個對象──特別小、簡單的變量,往往不是很有效。因此,不用new來創建這類變量,而是創建一個并非是引用的變量,這個變量直接存儲值,并置于棧中,因此更加高效。
在Java中,這些基本類型有:boolean、char、byte、short、int、long、float、double和void;還有與之對應的包裝器:Boolean、Character、Byte、Short、Integer、Long、Float、Double和Void;他們之間涉及到裝箱和拆箱,我們有機會再聊。
看兩行簡單的代碼:
int a = 3;
int b = 3;
12
這兩行代碼在編譯的時候是什么樣子呢?
編譯器當然是先處理int a = 3;,不然還能跳過嗎?編譯器在處理int a = 3;時在棧中創建了一個變量為a的內存空間,然后查找有沒有字面值為3的地址,沒找到,就開辟一個存放3這個字面值的地址,然后將a指向3的地址。
編譯器忙完了int a = 3;,就來接著處理int b = 3;;在創建完b的變量后,由于棧中已經有3這個字面值,就將b直接指向3的地址;就不需要再開辟新的空間了。
依據上面的概述,我們假設在定義完a與b的值后,再令a=4,此時b是等于3呢,還是4呢?
思考一下,再看答案哈。
答案揭曉:當編譯器遇到a = 4;時,它會重新搜索棧中是否有4的字面值,如果沒有,重新開辟地址存放4的值;如果已經有了,則直接將a指向4這個地址;因此a值的改變不會影響到b的值哦。
最后,留個作業吧,下面這段代碼在運行時會輸出什么呢?
public class Test1 {
public static void main(String args[]) {
int a = 1;
int b = 1;
a = 2;
System.out.println(a);
System.out.println(b);
TT t = new TT("T");
TT t1 = t;
t.setName("TT");
System.out.println(t.getName());
System.out.println(t1.getName());
}
}
class TT{
private String name;
public TT (String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name1) {
this.name = name1;
}
}
來源:本文內容搜集或轉自各大網絡平臺,并已注明來源、出處,如果轉載侵犯您的版權或非授權發布,請聯系小編,我們會及時審核處理。
聲明:江蘇教育黃頁對文中觀點保持中立,對所包含內容的準確性、可靠性或者完整性不提供任何明示或暗示的保證,不對文章觀點負責,僅作分享之用,文章版權及插圖屬于原作者。
Copyright?2013-2024 JSedu114 All Rights Reserved. 江蘇教育信息綜合發布查詢平臺保留所有權利
蘇公網安備32010402000125
蘇ICP備14051488號-3技術支持:南京博盛藍睿網絡科技有限公司
南京思必達教育科技有限公司版權所有 百度統計