1. 程式人生 > >Java基礎(四)Java類的成員變數和區域性變數

Java基礎(四)Java類的成員變數和區域性變數

在Java中,成員變數和區域性變數存在較大的差異性。首先,我們來看一下變數的分類圖:


成員變數

成員變數被分為:類屬性和例項屬性。

例項屬性:定義一個屬性時,不使用static修飾的就是例項屬性,

類屬性:定義一個屬性時,使用static修飾的是類屬性。

類屬性從這個類的準備階段起開始存在,直到系統完全銷燬這個類,類屬性的作用域與這個類的生存範圍相同

例項屬性則從這個類的例項被建立開始存在,直到系統完全銷燬整個例項,例項屬性的作用域與對應例項的生存範圍相同。

PS:一個類在使用之前,要經過類載入、類驗證、類準備、類解析、類初始化等幾個階段。以上就是類的生命週期。

注意:成員變數無須顯式初始化

,只要為一個類定義了類屬性或例項屬性,則系統會在這個類的準備階段或建立這個類的例項時,進行預設初始化,成員變數預設初始化時的複製規則與陣列動態初始化時陣列元素的賦值規則完全相同。

例項屬性隨例項的存在而存在,而類屬性則隨類的存在而存在。例項也可以訪問類屬性,同一個類的所有例項訪問類屬性時,實際上訪問的是同一個類屬性,因為它們實際上都是訪問到該類的類屬性。

區域性變數

區域性變數根據定義形式不同,又可以被分為如下三種:

形參在定義方法簽名時定義的變數,形參的作用域在整個方法內有效;

方法區域性變數:在方法體內定義的區域性變數,它的作用域是從定義該變數的地方生肖,到該方法結束時生效;

程式碼塊區域性變數

:在程式碼塊中定義的區域性變數,這個區域性變數的作用域從定義該變數的地方生效,到該程式碼塊結束時失效。

與成員變數不同的是,區域性變數除了形參之外,都必須顯示初始化。也就說,必須先給方法區域性變數和程式碼塊區域性變數指定初始值,否則不可以訪問它們。

成員變數的初始化和記憶體中的執行機制

當系統載入類或建立該類的例項時,系統自動為成員變數分配記憶體空間,並在分配內控空間後,自動為成員變數指定初始值。

public class Person {
	public String name;
	public static int age;
	
	public static void main(String[] args) {
		//建立第一個Person物件
		Person p1 = new Person();
		//建立第二個Person物件
		Person p2 = new Person();
		//分別為兩個Person物件的name屬性賦值
		p1.name="張三";
		p2.name = "李四";
		//分別為兩個Person物件的eyeNum屬性賦值
		p1.age = 20;
		p2.age = 30;
	}
}


當程式執行第一行程式碼Person p1 = new Person();時,如果這行程式碼是第一次使用Person類,則系統通常會在第一次使用Person類時載入這個類,並初始化這個類。

在類的準備階段,系統將會為該類的類屬性分配記憶體空間,並指定預設初始值。當Person類初始化完成後,系統記憶體中的儲存示意圖如下:

                     (初始化Person類後的示意圖)

Person類初始化完成後,系統將在堆記憶體中為Person類分配一塊記憶體區(當Person類初始化完成後,系統會隱含地為Person類建立一個類物件),在這塊記憶體區裡包含了儲存類屬性eyeNum的記憶體,並設定eyeNum的預設初始值:0

接著,系統會建立一個Person物件,並把整個Person物件賦給P1變數,Person變數裡包含了名為name的例項屬性,例項屬性是在建立例項時分配記憶體空間、並指定初始值的。

當建立了第一個Person物件時,系統記憶體中的儲存如下:

                         (建立第一個Person物件後的示意圖)

age類屬性並不屬於Person物件,它是屬於Person類的,所以建立第一個Person物件時並沒有為eyeNum類屬性分配記憶體,系統只是為name例項屬性分配了記憶體空間,並指定預設初始值:null

接著執行 Person p2 =new Person();程式碼建立第二個Person物件,此時因為Person類已經存在於堆記憶體中了,所以不再需要對Person類進行初始化。

建立第二個Person物件與建立第一個Person物件並沒有什麼不同。

當程式執行 p1.name ="張三";程式碼時,將為p1name屬性賦值,也就是讓上圖中堆記憶體中name指向一個"張三"的字串。

執行完成後,兩個Person物件在記憶體中的示意圖如下:


          (為第一個Person物件的name屬性賦值後的儲存示意圖)

從上圖中可以看出:name屬性是Person的例項屬性,因此修改第一Person物件的name屬性時僅僅與該物件有關,與Person類和其他Person物件沒有任何關係。同樣,修改第二個Person物件的name屬性時,也只修改這個Person物件的name屬性,與Person類和其他Person物件無關。

直到執行p1.age=2;程式碼時,此時通過Person物件來修改Person的類屬性,從圖5.12中不難看出,Person物件根本沒有儲存age屬性,通過p1訪問的age屬性,其實還是Person類的age屬性。因此,此時修改的是Person類的age屬性。修改成功後,記憶體中的示意圖如下:

                (設定p1.age的屬性後的儲存示意圖)

通過P1來訪問其類屬性時,實際上訪問的是Person類的age屬性。事實上,所有Person例項訪問age屬性時,都將訪問到person類的age。因此,不管是通過Person類訪問age屬性,還是銅鼓哦任何Person獨享來訪問age屬性,所訪問的是同一塊記憶體,Person類和所有的Person物件的age屬性都會隨之改變。


區域性變數的初始化和記憶體中的執行機制

區域性變數定義後,必須經過顯示初始化才能使用,系統不會為區域性變數執行初始化。這意味著:

定義區域性變數之後,系統並未為這個變數分配記憶體空間,直到等到程式為這個變數賦初始值時,系統才會為區域性變數分配記憶體,並將初始值儲存到這塊記憶體中。

與成員變數不同,區域性變數不屬於任何類或例項,因此它總是儲存在其所在方法的棧記憶體中的。

如果區域性變數是基本型別的變數,則直接把這個變數的值儲存在該變數對應的記憶體中;

如果區域性變數是一個引用型別的變數,則這個變數裡存放的是地址,通過改地址,引用到該變數實際引用的物件或陣列。

棧記憶體中的變數無須系統垃圾回收,棧記憶體中的變數往往是隨方法或程式碼塊的執行結束而結束的

因此,區域性變數的作用域是從初始化該變數開始,到該方法或該程式碼塊執行完成而結束。因此區域性變數只儲存基本型別的值或者物件的引用,因此區域性變數所佔的記憶體區通常非常小。

PS:成員變數時被放置到堆記憶體中的,而區域性變數放置在棧記憶體中。

成員變數將被放置到堆記憶體中,成員變數的作用域擴大到類存在範圍或者物件存在範圍,有兩個害處:

  1. 增大了變數的生存時間,這將導致更大的系統開銷;
  2. 擴大了變數的作用域,這不利於提高程式的內聚性。