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

来源:https://www.cnblogs.com/qing-gee/archive/2018/11/02/9894098.html
-Advertisement-
Play Games

記得中學的課本上,有一篇名為《狂人日記》課文;那時候根本理解不了魯迅寫這篇文章要表達的中心思想,只覺得滿篇的“吃人”令人心情壓抑;老師在講臺上慷慨激昂的講,大多數的同學同我一樣,在課本面前“痴痴”的發呆。 作為一個有著8年Java編程經驗的IT老兵,說起來很慚愧,我被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還沒有出生(初始化);見下圖。

如果把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;

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;

這兩行代碼在編譯的時候是什麼樣子呢?

編譯器當然是先處理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;
    }
}

 

 

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 想知道immutablejs、mobx和原生的redux對比,到底誰的性能更高一籌嗎,我們該如何對web頁面進行性能測試嗎,敬請關註我的這三篇博客,文中我將會從採集數據、分析數據、得出結論等方面詳細的闡述我的測試歷程。 ...
  • https://blog.csdn.net/oumaharuki/article/details/79268498 別人的記載,寫的很不錯,還有下載的方法 以下是自己使用過的,做出來的例子: 做出來的效果圖: 記住使用之前要npm下載哦 npm install vue-codemirror --sa ...
  • 想知道immutablejs、mobx和原生的redux對比,到底誰的性能更高一籌嗎,我們該如何對web頁面進行性能測試嗎,敬請關註我的這三篇博客,文中我將會從採集數據、分析數據、得出結論等方面詳細的闡述我的測試歷程。 ...
  • *** 全局變數:在script標簽裡面定義一個變數,這個變數在頁面中js部分都可以使用 - 在方法外部使用,在方法內部使用,在另外一個script標簽中使用 *** 局部變數:在方法內部定義一個變數,只能在方法內部調用 - 如果在方法的外部調用這個變數,提示出錯 ...
  • relative:相對定位。 1. 不論其父元素和相鄰元素的position是什麼,均相對於自身原來的位置來偏移。 2. 不會脫離文檔流,其原來的位置依然保留著,不會被文檔中其他的元素占用。 3. 原來是行內元素,設置相對定位後,依然是行內元素。 4. 設置了相對定位的塊級元素,如果沒有設置寬度,其 ...
  • 在最近的項目中,引用了vux,在可拓展性以及復用性,都算是比較優秀的框架了。但是美中不足的是對於vux在對於vue-cli3.0的跟進還沒有同步 需要自己做下修改,同比 有贊的vant 以及 iview 都有了對於vue-cli3.0的相容了 現記錄如下: 一、安裝vue-cli 3 首先官方文檔: ...
  • ** js裡面不區分整數和小數 var j = 123; alert(j/1000*1000); //在Java裡面結果是0 //在js裡面不區分整數和小數 123/1000 = 0.123 *1000= 123; ** 字元串的相加和相減的操作 var str = "456"; alert(str ...
  • JavaScript的簡介 * 是基於對象和事件驅動的語言,應用於客戶端 - 基於對象: ** 提供好了很多對象,可以直接拿過來使用 - 事件驅動: ** HTML做網站靜態效果,JavaScript動態效果 - 客戶端:專門指的是瀏覽器 * JavaScript的特點 (1)交互性 - 信息的動態 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...