1. 程式人生 > >Java之路:物件的宣告和使用

Java之路:物件的宣告和使用

一、物件的宣告

下面定義了由類產生物件的基本形式:

類名 物件名 = new 類名();

建立屬於某類的物件,需要通過下面兩個步驟實現:

⑴ 宣告指向“由類所建立的物件”的變數。
⑵ 利用new建立新的物件,並指派給先前所建立的變數。

class Person {	定義一個Person類
	private String name;
	private int age;
	public void talk(); 
}
Person p = new Person();	
// 也可以用下面這個方法
Person p1;		// 先宣告Person類物件p1
p1 = new
Person(); // 再用new關鍵字例項化Person的物件p1

物件只有在例項化之後才能被使用,而例項化物件的關鍵字就是new。

物件例項化過程如下:
在這裡插入圖片描述
從圖中可以看出,當語句執行到Person p1的時候,只是在“棧記憶體”中聲明瞭一個Person物件p1的引用,但是這個時候p1並沒有在“堆記憶體”中開闢空間。

物件的“引用”本質上就是一個物件在堆記憶體的地址,所不同的是,在Java中,使用者無法向C/C++那樣直接操作這個地址。

本質上,“new Person()”就是使用new關鍵字,來呼叫構造方法Person(),建立一個真實的物件,並把這個物件在“堆記憶體”中的佔據的記憶體首地址賦予p1,這時p1才能稱為一個例項化的物件。

二、物件的使用

如果要訪問物件裡的某個成員變數或方法,可以通過下面的語法來實現:

物件名稱.屬性名     //訪問屬性
物件名稱.方法名()     //訪問方法

例如,想訪問Person類中的name和age屬性,可用如下方法來訪問:

p1.name;   //訪問Person類中的name屬性
p1.age;    //訪問Person類中的age屬性

因此若想將Person類的物件p中的屬性name賦值為“張三”,年齡賦值為25,則可採用下面的寫法:

p1.name = "張三" ;
p1.age = 25 ;

如果想呼叫Person中的talk()方法,可以採用下面的寫法:

p1.talk();   //呼叫Person類中的talk()方法

三、匿名物件

匿名物件是指就是沒有名字的物件。
實際上,根據前面的分析,對於一個物件例項化的操作來講,物件真正有用的部分是在堆記憶體裡面,而棧記憶體只是儲存了一個物件的引用名稱(嚴格來講是物件在堆記憶體的地址)。
所以,所謂的匿名物件就是指,只開闢了堆記憶體空間,而沒有棧記憶體指向的物件。

public class NoNameObject {
	public void say() {
      System.out.println("面朝大海,春暖花開!");
    }
    public static void main(String[]args) {
      new NoNameObject().say();	// 這是匿名物件,沒有被其他物件所引用
    }
}

【結果】
在這裡插入圖片描述
由於“new NoNameObject()”建立的是匿名物件,所以就用“NoNameObject()”整體來作為新構造匿名物件的引用,它訪問類中的方法,就如同普通物件一下,使用點操作符(.):

NoNameObject().say();

匿名物件有如下兩個特點:

⑴ 匿名物件是沒有被其他物件所引用,即沒有棧記憶體指向
⑵ 由於匿名物件沒有棧記憶體指向,所以其只能使用一次,之後就變成無法找尋的垃圾物件,故此會被垃圾回收器收回。


當建立一個物件後,Java虛擬機器(JVM)就會給這個物件分配一個自身的引用——this。由於this是和物件本身相關聯的,所以this只能在類中的非靜態方法中使用。
靜態屬性及靜態方法屬於類,它們與具體的物件無關,所以靜態屬性及靜態方法是沒有this的。

public class StaticThis {
	static int i;
	public static void init() {
		this.i = 10;	// 錯誤,this只能在非靜態方法中使用
	}
	public static int f() {
		init();
		i++;
		return i;
	}
	public void print() {
		System.out.println(this.f());	// 雖然這樣訪問靜態方法不會報錯,但是不推薦使用
		System.out.println(StaticThis.f());	// 因為靜態方法與靜態屬性是屬於類的,所以直接用類名訪問比較好
	}
	public static void main(String[] args) {
		StaticThis st = new StaticThis();
		st.print();
	}
}

同一個類定義下的不同物件,每個物件都有自己的this,雖然都叫this,但指向的物件不同。

四、棧記憶體和堆記憶體的區別

1、棧

在Java中,棧(stack)是由編譯器自動分配和釋放的一塊記憶體區域,主要用於存放一些基本型別(如int、 float等)的變數、指令程式碼、常量及物件控制代碼(也就是物件的引用地址)。

棧記憶體的操作方式類似於資料結構中的棧(僅在表尾進行插入或刪除操作的線性表)。

棧的優勢在於,它的存取速度比較快,僅次於暫存器,棧中的資料還可以共享。
缺點表現在,存在棧中的資料大小與生存期必須是確定的,缺乏靈活性。

2、堆

堆(heap)是一個程式執行動態分配的記憶體區域

在Java中,構建物件時所需要的記憶體從堆中分配。 這些物件通過new指令“顯式”建立,放棄分配方式類似於資料結構中的連結串列。

堆記憶體在使用完畢後,是由垃圾回收(Garbage Collection,簡稱GC)器“隱式”回收的。 在這一點上,是和C/C++是有顯著不同的,在C/C++中,堆記憶體的分配和回收都是顯式的,均由使用者負責,如果使用者申請了堆記憶體,而在使用後忘記釋放,則會產生“記憶體溢位”問題——可用記憶體存在,而其他使用者卻無法使用。

堆的優勢是在於動態地分配記憶體大小,可以“按需分配”,其生存期也不必事先告訴編譯器,在使用完畢後,Java的垃圾收集器會自動收走這些不再使用的記憶體塊。
缺點為,由於要在執行時才動態分配記憶體,相比於棧記憶體,它的存取速度較慢。