小變量大學問 干了這杯全家桶

java零基礎入門-面向對象篇(七)? 各種變量在JVM中的位置和運行方式

本章對前面學的知識進行一個總結,為下面要介紹的封裝繼承和多態打好基礎。

學習就要不斷的復習和總結,才能讓學過的知識不斷的得到沉淀,變成自己的知識,切勿心浮氣躁,囫圇吞棗。

在我的編程生涯中,遇到過很多問題和BUG,最讓我難忘的就是那種既不報錯,又沒有異常的BUG,這種BUG解起來讓人痛苦不堪,比如我當年就碰到過一個由static 靜態變量引發的BUG,最后只能用輸出語句排查,這就是典型的知識點掌握的不牢引發的問題。當然那段代碼不是我寫的。

所以在我們使用變量的時候,一定要掌握一些原則,就是潛規則,不能隨便看心情來定義變量。如果能夠知道變量的內在原理,那對我們寫代碼會有更大的幫助。

變量的分類

根據變量的聲明位置一般分為兩種

類里面方法外面聲明的變量,是成員變量。成員變量又分為類變量實例變量。類變量就是static修飾的變量,它屬于類。實例變量就是沒有static修飾的變量,它屬于對象。

再就是在方法內部聲明的變量,是局部變量。局部變量包括方法的參數,方法內部定義的變量。


變量分類

1.普通成員變量,屬于對象

2.靜態成員變量,屬于類

3.局部變量,方法的參數是局部變量

4.方法內部定義的變量是局部變量

前面說堆棧的時候,說變量在棧里面,其實那僅僅是指局部變量。普通成員變量在堆里面,靜態成員變量在方法區。是不是又有點懵,就一個變量整這么多地方干啥,東放一點西放一點,放一起不就完了。其實分開存放是有原因的,因為他們的生命周期不同,不同的變量有著不同的生命周期,導致他們必須存放在不同的地方。

為什么生命周期不同要放不同的地方?你想想你家有冰箱吧,你試試不把冰淇淋放冰箱,估計你還沒吃,它就變成液體了。因為冰淇淋的生命周期不同,它不在冰箱活不了多久,要根據它的特點指定存放的地方,不然你還沒開始調用(吃),他就被銷毀了(化了)。


棧幀

首先來看局部變量,局部變量是生命周期最短的,為什么呢?因為局部變量一般定義在方法里面,他隨著方法的調用創建,隨著方法的完畢銷毀。

我們前面說的棧是一種數據結構,它是一種設計概念,而我們虛擬機里面的虛擬機棧是用這個設計概念實現的程序,請不要混淆兩者的概念。

現在我們可以完善一下前面講的那個棧的結構了。在JVM中,我們每次入棧其實不是入棧的一個變量,而是一個棧幀,在我們知識體系不完善的時候用入棧變量來解釋可能會好理解一些,但是現在我們儲備了足夠的知識,我們就可以理解的更準確一點。

棧幀是什么?棧幀是虛擬機進行方法調用和方法執行的數據結構。就是說,我們每一個方法,都對應了一個棧幀。

棧幀主要由以下幾個部分組成,局部變量表,操作數棧,動態連接,方法返回地址。這里我們只關注局部變量表,其他幾個部分,知道有這個東西就行了。


棧幀的組成


對我們來說,棧幀里面最需要了解的就是局部變量表。我們方法中定義的局部變量,就是參數和方法內定義的變量都存在這里(第一張圖里面,3,4)。下面我們來看看調用方法的時候,棧幀是怎么進出的。

1.當方法執行到 studyFrameWork 這個方法的時候,棧幀入棧。方法的局部變量 frameWork 和 spring 在棧幀的局部變量表中,一起入棧。

1


2.當studyFrameWork這個方法執行完畢后,方法對應的棧幀出棧。

2


3.當程序繼續運行到下一個方法 studyWebFrameWork 的時候,這個方法對應的棧幀入棧,并且這個方法的局部變量 webFrameWork 和 vue 保存在局部變量表,一同入棧。

3


以上這個類是將兩個方法分開執行的情況,我們再看看下面這種情況

1.調用方法 studyAllFrameWork 的時候,局部變量 frameWork 和 spring 在局部變量表中入棧

1


2.這個時候,studyAllFrameWork 這個方法沒有執行完畢,在這個方法內部又再次調用了 studyWebFrameWork 這個方法,局部變量vue,webFrameWork 在局部變量表中入棧。這個時候棧中有兩個棧幀,先入棧的在棧底,后入棧的在棧頂。

2


3.后入棧的方法執行完畢,返回值返回給先入棧的方法,這個時候后入棧的方法出棧,程序繼續執行。

3


4.最后當第一個入棧的方法也執行完畢后,第一個方法對應的棧幀也出棧了。

4


看到這里,很多同學心里的問題應該有答案了。

為什么只有棧頂可以操作?我直接用棧底的數據不行嗎?

因為當我們調用方法的時候,方法中的數據按照順序入棧,如果沒有運行完畢或者出現異常,這個方法對應的棧幀是不會出棧的,如果往下執行,又調用了另外一個方法,那么另外一個方法對應的棧幀會繼續入棧,在棧頂,然后程序開始執行后入棧的這個棧幀對應的方法,所以只有棧頂的這個棧幀對應的方法才可以操作,當他運行完畢出棧以后,會繼續運行第一個沒有運行完畢的方法,這時候,第一個方法對應的棧幀就是棧頂(因為只有一個棧幀了)。所以只有棧頂可以操作。

當然,如果我們第二個方法中又調用了第三個方法,就會有第三個方法對應的棧幀入棧。然后依次運行,出棧。

入棧的時候,局部變量開始生效,出棧的時候,局部變量就銷毀掉了,所以局部變量的生命周期只在方法內部有效,出了方法就不能使用了。現在大家應該對這個有更深刻的理解了吧。


成員變量的位置

成員變量

成員變量有兩種,普通成員變量屬于對象,因為當我們使用 new 創建一個對象的時候,這個對象會在堆里面開辟一個空間,而成員變量會隨著這個對象的創建,也在堆里面。而當程序里面沒有任何變量指向這個對象的時候,這個對象就會被清潔阿姨回收掉,這個時候成員變量也會一起被回收掉。

所以普通成員變量的生命周期和對象一樣,隨著對象的創建而創建,隨著對象的回收而回收。

再看另外一種,靜態成員變量。靜態成員變量在方法區,它屬于類,所以它的生命周期跟類一樣。也就是說當對象生老病死以后,這個靜態成員變量依然活的好好的。

本文開頭說過一個靜態變量引發的BUG就是,某位不知名程序員為了滿足一個 “全局” 的需求,結果使用了靜態變量,然后他每次 new出來的對象都會對這個全局變量進行修改, 由于方法區是線程共享的,結果出現了意料之外的結果。所以理解每種變量所在區域和該區域的特點很有必要。

對于各種變量,我們知道它們的生命周期和作用范圍,對我們后面的學習很有必要。比如我們要控制變量的作用范圍,可以使用局部變量的時候就使用局部變量,過多的使用成員變量會增加堆內存的開銷。再就是我們講到封裝等概念的時候,也需要控制變量的作用范圍,使我們的程序更加符合程序設計的規范。

最后,本章解釋的jvm棧幀結構其實也不是很完全,但是作為初學者,學這些已經遠遠超出了我們需要掌握的知識,能理解最好,實在不能理解也沒有關系。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 說到夢想,我感覺在我的世界里它是不斷變化的,小時候老師都會問長大的夢想是什么,老師啊醫生啊這樣的詞匯都出來了,再到...
    憶昔rlm閱讀 26,631評論 0 0
  • 你的沉默像一尊雕像 打碎它,無數眼淚飛了出來 葉片沉郁,陽光欠身刺入 晚風掀開帷幕 燈火自虛門飛出 飛出瞳孔、腦袋...
    王圣欽閱讀 161評論 0 0