偶然想起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惟一的參數傳遞方式——按值傳遞
看下面一段代碼:
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編程思想》中的一段話:
假若「將一個對象賦值給另外一個對象」,實際是將「引用」從一個地方複製到另外一個地方.