實例變量和類變量

Java內存管理分爲兩個方面:內存分配和內存回收。這裏的內存分配特指創建Java對象時JVM爲該對象在內存中所分配的內存空間;內存回收指的是當該Java對象失去引用,變成垃圾時,JVM的垃圾回收機制自動清理該對象,並回收該對象所佔用的內存。由於JVM內置了垃圾回收機制回收失去引用的Java對象所佔用的內存,所以很多Java開發者認爲Java不存在內存泄露、資源泄露的問題。實際上這是一種錯覺,Java程序依然會有內存泄露。
由於JVM的垃圾回收機制由一條後臺線程完成,本身也是非常消耗性能的,因此如果肆無忌憚地創建對象,讓系統分配內存,那麼這些分配的內存都將由垃圾回收機制進行回收。這樣做有兩個壞處:
 不斷分配內存使得系統中可能的內存減少,從而降低程序的運行性能
 大量已分配內存的回收使得垃圾回收的負擔加重,減低程序的運行性能

局部變量分爲三種:
 形參
 方法內的局部變量
 代碼塊內的局部變量
局部變量的作用時間很短暫,它們都被存儲在棧內存中

從Java程序的角度來看,static就是一個標誌,static的作用是將實例成員變爲類成員。static只能修飾在類裏定義的成員部分,包括成員變量、方法、內部類(枚舉與藉口)、初始化塊。如果沒有使用static修飾類裏的這些成員,這些成員屬於該類的實例;如果使用了static修飾,這些成員就屬於類本身。由此可見,static只能修飾類裏的成員,不能修飾外部類,不能修飾局部變量、局部內部類。

如果一個是實例變量,一個是類變量,則實例變量總是可以引用類變量。
這是因爲:static修飾的成員變量屬於類,類變量會隨着類初始化得到初始化。在初始化一個對象之前,肯定得先初始化該對象所屬的類

使用static修飾的成員變量是類變量,屬於該類本身;沒有使用static修飾的成員變量是實例變量。屬於該類的實例。在同一個JVM內,每個類只對應一個Class對象,但每個類可以創建多個Java對象
由於同一個JVM內每個類只能對應一個Class對象,因此同一個JVM內的一個類的類變量只需要一塊內存空間;但對於實例變量而言,該類沒創建一次實例,就需要爲實例變量分配一塊內存空間。也就是說,程序中有幾個實例,實例變量就需要幾塊內存空間

大部分時候會把類和對象嚴格地區分開來,但從另一個角度來看,類也是對象,所有類都是Class的實例。每個類初始化完成後,系統都會爲該類創建一個對應的Class實例,程序可以通過反射來獲取某個類所對應的Class實例。例如,要獲取Person類對應的Class實例,通過Person.class;或Class.forName("Person"),一條代碼即可

對於實例變量而言,它屬於Java對象本身,從程序運行的角度來看,每次程序創建Java對象時都需要爲實例變量分配內存空間,並對實例變量執行初始化。
從語法角度來看,程序可以在三個地方對實例變量執行初始化。
 1、定義實例變量時制定初始值
 2、非靜態初始化塊中對實例變量指定初始值
 3、構造器中對實例變量指定初始值
其中第1、2方式(定義時指定初始值和非靜態初始化塊中指定初始值)比第三種方式(構造器中指定初始值)更早執行,但第1、2種方式執行順序與它們在源程序中的排列順序相同
定義變量時指定的初始值和初始化塊中指定的初始值的執行順序,與它們在源程序中的排列順序相同
定義實例變量時指定初始值、初始化塊中爲實例變量指定初始值的語句的地位是平等的,經編譯器處理後,它們都將被提取到構造器中

定義實例變量時指定初始值、初始化塊中爲實例變量指定初始值、構造器中爲實例變量指定初始值,三者的作用完全類似,都用於對實例變量指定初始值。經過編譯器處理之後,它們對應的賦值語句都被合併到構造器中。在合併過程中,定義變量語句轉換得到的賦值語句、初始化塊裏的語句轉換得到的賦值語句,總是位於構造器的所有語句之前;合併後,兩種賦值語句的順序保持爲它們在源代碼中的順序

類變量屬於Java類本身。從程序運行的角度來看,每個JVM對一個Java類只初始化一次,因此只有每次運行Java程序時,纔會初始化該Java類,纔會爲該類的類變量分配內存空間,並執行初始化。
從語法角度來看,程序可以在兩個地方對類變量執行初始化
 定義類變量時指定初始值
 靜態初始化塊中對類變量指定初始值
這兩種方式的執行書序與它們在原程序中的排序相同

先爲所有類變量分配內存空間,再按源代碼中的排列順序執行靜態初始化塊中所指定的初始值和定義類變量時所指定的初始值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章