Java中對象和引用的理解

偶然想起Java中對象和引用的基本概念,爲了加深下對此的理解和認識,特意整理一下相關的知識點,經過具體實例從二者的概念和區別兩方面去更形象的認識理解,再去記憶。

 

1、對象和引用的概念: 
在Java中萬物皆對象,好比咱們定義一個簡單的動物類:javascript

class Animal {
    String count;
    String weight;
    ....
}

 

有了這個Animal類以後,咱們能夠來建立一個Animal對象:php

Animal an = new Animal();

 

咱們把編寫這個語句的動做就稱做建立一個對象,細化這個動做爲: 
1. 右面的」new Animal」,是以Animal類爲模板的,在堆空間裏建立一個Animal對象; 
2. 末尾的」( )」表明着:在對象建立以後,當即調用Animal類的構造函數,對新生成的對象進行初始化。(若是沒構造函數,Java會有一個默認的構造函數的); 
3. 左面的」Animal an」 建立了一個Animal類引用變量。即之後能夠用來指向Animal對象的對象引用; 
4. 「=」 操做符使對象引用指向剛纔建立的那個Animal對象。 
拆分開也就是:等同於java

Animal an;
an = new Animal();

 

 

有兩個實體:一個是對象引用變量;一個是對象自己。 
在java中,都是經過引用來操縱對象的,這也是二者的區別。python

2、對象和引用的區別: 
一、關聯性: 
1). 當對象的引用變量指向對象時,二者就互相聯繫起來,改變引用的屬性,就會改變對象的屬性; 
2). 若是同一個對象被多個引用變量引用的話,則這些引用變量將共同影響這個對象自己; 
3). 在java中,都是經過引用來操縱對象的。nginx

二、差別性: 
1). 一個對象能夠被不一樣的引用變量來操縱,同時一個引用變量也能夠指向不一樣的對象,可是同一時刻下只能指向一個對象。 
2). 從存儲空間上來看,對象和引用也是相互獨立的,對象通常存儲在堆中,而引用存儲在堆棧中(存儲速度而更快)。編程

對於引用變量的深層含義,未必在初學的時候就能深入理解, 
因此理解好下面這兩句話的真正含義很是重要數組

Case cc=new Case();

Case cc;
cc=new Case();

 

1.先搞清楚什麼是堆,什麼是棧。 
Java開闢了兩類存儲區域,對比兩者的特色app

存儲區域 存儲內容 優勢 缺點 回收
基本類型的變量和對象的引用變量 存取速度比堆要快,僅次於寄存器,棧數據能夠共享 存在棧中的數據大小與生存期必須是肯定的,缺少靈活性。棧中主要存放一些基本類型的變量 當超過變量的做用域後,Java會自動釋放掉該變量,內存空間能夠當即被另做他用
由new等指令建立的對象和數組 能夠動態地分配內存大小,生存期也沒必要事先告訴編譯器 因爲要在運行時動態分配內存,存取速度較慢 由Java虛擬機的自動垃圾回收器來回收再也不使用的數據

堆棧的存儲特色決定了其中存儲的數據類型。函數

注意,棧內存儲的除了基本類型的變量(String, int 這種類型的變量)還會存儲對象的引用變量。java中,引用變量其實是一個指針,它指向的是堆內存中對象實例。ui

引用變量就至關因而爲數組或對象起的一個名稱,之後就能夠在程序中使用棧中的引用變量來訪問堆中的數組或對象。

2.給引用變量賦值

回過頭再來看代碼

實際上裏面分解成了四個步驟。

Case cc; '''在棧內存裏面開闢了空間給引用變量cc,這時cc=null'''
cc=new Case();
''' 1. new Case()在堆內存裏面開闢了空間給Case類的對象,這個對象沒有名字 2. Case()隨即調用了Case類的構造函數 3. 把對象的地址在堆內存的地址給引用變量cc '''

 

 

這樣咱們就明確了:

  • Java中,這裏的「=」並非賦值的意思,而是把對象的地址傳遞給變量;
  • 對象建立出來,其實連名字都沒有,所以必須經過引用變量來對其進行操做。

爲了形象地說明對象、引用及它們之間的關係,能夠作一個或許不很穩當的比喻。對象比如是一隻很大的氣球,大到咱們抓不住它。引用變量是一根繩, 能夠用來系汽球

緊接着就會問,引用變量是怎麼傳遞的呢? 
這就涉及到Java惟一的參數傳遞方式——按值傳遞

看下面一段代碼:

public class ObjectRef {

    '''基本類型的參數傳遞'''
    public static void testBasicType(int m) {
        System.out.println("m=" + m);//m=50
        m = 100;
        System.out.println("m=" + m);//m=100
    }
   '''參數爲對象,不改變引用的值'''
   '''s即sMain指向的對象執行了append方法,在原來的字符串上加了段「_add」'''

    public static void add(StringBuffer s) {
        s.append("_add");
    }

    '''參數爲對象,改變引用的值 '''
    '''引用變量指向了一個新的對象,已經不是sMain指向的對象了'''
    public static void changeRef(StringBuffer s) {
        s = new StringBuffer("Java");
    }

    public static void main(String[] args) {
        int i = 50;
        testBasicType(i);
        System.out.println(i);'''i=50'''
        StringBuffer sMain = new StringBuffer("init");
        System.out.println("sMain=" + sMain.toString());'''sMain=init'''
        add(sMain);
        System.out.println("sMain=" + sMain.toString());'''sMain=init_add'''
        changeRef(sMain);
        System.out.println("sMain=" + sMain.toString());'''sMain=Java'''
    }
}

看這裏,給人的感受是傳遞過來的明明是對象的引用,爲何就是值得傳遞呢? 
由於傳遞以前,被傳的就是個引用啊,咱們所謂的「傳地址」,在傳以前,那但是一個實例,傳過來的是實例的地址。這裏傳遞的值,從始至終就是個地址,sMain就是個地址,傳給s仍是個地址。大家感覺下:

'''參數爲對象,不改變引用的值'''
'''s即sMain,指向的對象執行了append方法,在原來的字符串上加了段「_add」'''
    public static void add(StringBuffer s) {
        s.append("_add");
    }

以上輸出的結果會是「init_add」

而這裏,s引用了一個新的對象,根本沒有進行參數的傳遞,它和以前的sMain沒有關係了。

'''參數爲對象,改變引用的值 '''
'''引用變量指向了一個新的對象,已經不是sMain指向的對象了'''
    public static void changeRef(StringBuffer s) {
        s = new StringBuffer("Java");
    }

以上輸出的結果會是「Java」 

引用《Java編程思想》中的一段話:

假若「將一個對象賦值給另外一個對象」,實際是將「引用」從一個地方複製到另外一個地方.

相關文章
相關標籤/搜索