Java基礎11 對象引用

來源:互聯網
上載者:User

標籤:

 

我們之前一直在使用“對象”這個概念,但沒有探討對象在記憶體中的具體儲存方式。這方面的討論將引出“對象引用”(object reference)這一重要概念。

 

對象引用

我們沿用之前定義的Human類,並有一個Test類:

 
public class Test{    public static void main(String[] args)    {        Human aPerson = new Human(160);                     }}class Human{       /**     * constructor     */    public Human(int h)    {        this.height = h;    }    /**     * accessor     */    public int getHeight()    {       return this.height;    }    /**     * mutator     */    public void growHeight(int h)    {        this.height = this.height + h;    }

private int height;}
 

 

外部可以調用類來建立對象,比如上面在Test類中:

Human aPerson = new Human(160);

建立了一個Human類的對象aPerson。

上面是一個非常簡單的表述,但我們有許多細節需要深入:

  1. 首先看等號的右側。new是在記憶體中為對象開闢空間。具體來說,new是在記憶體的堆(heap)上為對象開闢空間。這一空間中,儲存有對象的資料和方法。
  2. 再看等號的左側。aPerson指代一個Human對象,被稱為對象引用(reference)。實際上,aPerson並不是對象本身,而是類似於一個指向對象的指標。aPerson存在於記憶體的棧(stack)中。
  3. 當我們用等號賦值時,是將右側new在堆中建立對象的地址賦予給對象引用。

這裡的記憶體,指的是JVM (Java Virtual Machine)虛擬出來的Java進程記憶體空間。記憶體的堆和棧概念可參考Linux從程式到進程。

 

對象引用

 

 

棧的讀取速度比堆快,但棧上儲存的資料受到有效範圍的限制。在C語言中,當一次函數調用結束時,相應的棧幀(stack frame)要刪除,棧幀上儲存的參量和自動變數就消失了。Java的棧也受到同樣的限制,當一次方法調用結束,該方法儲存在棧上的資料將清空。在 Java中,所有的(普通)對象都儲存在堆上。因此,new關鍵字的完整含義是,在堆上建立對象。

 

基本類型(primitive type)的對象,比如int, double,儲存在棧上。當我們聲明基本類型時,不需要new。一旦聲明,Java將在棧上直接儲存基本類型的資料。所以,基本類型的變數名表示的是資料本身,不是引用。

 

 

引用和對象的關係就像風箏和人。我們看天空時(程式裡寫的),看到的是風箏(引用),但風箏下面對應的,是人(對象):

 

引用和對象分離;引用指向對象

 

儘管引用和對象是分離的,但我們所有通往對象的訪問必須經過引用這個“大門”,比如以 引用.方法() 的方式訪問對象的方法。在Java中,我們不能跳過引用去直接接觸對象。再比如,對象a的資料成員如果是一個普通對象b,a的資料成員儲存的是指向對象b的引用 (如果是基本類型變數,那麼a的資料成員儲存的是基本類型變數本身了)。

在Java中,引用起到了指標的作用,但我們不能直接修改指標的值,比如像C語言那樣將指標值加1。我們只能通過引用執行對對象的操作。這樣的設計避免了許多指標可能引起的錯誤。

 

引用的賦值

當我們將一個引用賦值給另一個引用時,我們實際上複製的是對象的地址。兩個引用將指向同一對象。比如 dummyPerson=aPerson;,將導致:

一個對象可以有多個引用 (一個人可以放多個風箏)。當程式通過某個引用修改對象時,通過其他引用也可以看到該修改。我們可以用以下Test類來測試實際效果:

 
public class Test{    public static void main(String[] args)        {             Human aPerson = new Human(160);             Human dummyPerson = aPerson;             System.out.println(dummyPerson.getHeight());             aPerson.growHeight(20);             System.out.println(dummyPerson.getHeight());        }}
 

我們對aPerson的修改將影響到dummyPerson。這兩個引用實際上指向同一對象。

 

所以,將一個引用賦值給另一個引用,並不能複製對象本身。我們必須尋求其他的機制來複製對象。

 

記憶體回收

隨著方法調用的結束,引用和基本類型變數會被清空。由於對象存活於堆,所以對象所佔據的記憶體不會隨著方法調用的結束而清空。進程空間可能很快被不斷建立的對象佔滿。Java內建有記憶體回收(garbage collection)機制,用於清空不再使用的對象,以回收記憶體空間。

記憶體回收的基本原則是,當存在引用指向某個對象時,那麼該對象不會被回收; 當沒有任何引用指向某個對象時,該對象被清空。它所佔據的空間被回收。

假設了某個時刻JVM中的記憶體狀態。Human Object有三個引用: 來自棧的aPerson和dummyPerson,以及另一個對象的資料成員president。而Club Object沒有引用。如果這個時候記憶體回收啟動,那麼Club Object將被清空,而Human Object來自Club Object的引用(president)也隨之被刪除。

 

記憶體回收是Java中重要的機制,它直接影響了Java的運行效率。我將在以後深入其細節。

 

參數傳遞

當我們分離了引用和對象的概念後,Java方法的參數傳遞機制實際上非常清晰: Java的參數傳遞為值傳遞。也就是說,當我們傳遞一個參數時,方法將獲得該參數的一個拷貝。

實際上,我們傳遞的參數,一個是基本類型的變數,另一個為對象的引用。

基本類型變數的值傳遞,意味著變數本身被複製,並傳遞給Java方法。Java方法對變數的修改不會影響到原變數。

引用的值傳遞,意味著對象的地址被複製,並傳遞給Java方法。Java方法根據該引用的訪問將會影響對象。

 

在這裡有另一個值得一提的情況: 我們在方法內部使用new建立對象,並將該對象的引用返回。如果該返回被一個引用接收,由於對象的引用不為0,對象依然存在,不會被記憶體回收。

 

總結

new

引用,對象

被記憶體回收的條件

參數: 值傳遞 引用的值傳遞

Java基礎11 對象引用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.