深入理解Java引用類型

在Java中類型可分為兩大類:值類型與引用類型 值類型就是基本數(shù)據(jù)類型(如int ,double 等),而引用類型,是指除了基本的變量類型之外的所有類型(如通過 class 定義的類型)。所有的類型在內(nèi)存中都會分配一定的存儲空間(形參在使用的時候也會分配存儲空間,方法調(diào)用完成之后,這塊存儲空間自動消失), 基本的變量類型只有一塊存儲空間(分配在stack中), 而引用類型有兩塊存儲空間(一塊在stack中,一塊在heap中),在函數(shù)調(diào)用時Java是傳值還是傳引用,這個估計很多人至今都很糊涂,下面用圖形與代碼來解釋:

下載1.41.png

在上圖中引用類型在傳參時不是在heap中再分配一塊內(nèi)存來存變量c 所指向的A(),而是讓a 指向同一個A 的實例,這就與C++ 中的指針一樣,先聲明指針變量a,b,c,d 在傳參的時候讓a 指向c所指向的內(nèi)存,讓 d 指向 b 所指向的內(nèi)存。很明顯Java中的引用與C++中的指針在原理上是相類似的,但記住Java沒有指針,只有引用。下面再通過一些具體的代碼來討論引用:

1. 簡單類型是按值傳遞的

Java 方法的參數(shù)是簡單類型的時候,是按值傳遞的 (pass by value)。這一點我們可以通過一個簡單的例子來說明:

package test;

 

public class Test {

   //交換兩個變量的值

   public static void Swap(int a,int b){

      int c=a;

      a=b;

      b=c;

      System.out.println("a: "+a);

      System.out.println("b: "+b);

   }

  

   public static void main(String[] args){

      int c=10;

      int d=20;

      Swap(c,d);

      System.out.println("After Swap:");

      System.out.println("c: "+d);

      System.out.println("d: "+c);

   }

}

運(yùn)行結(jié)果:

a: 20

b: 10

After Swap:

c: 20

d: 10

不難看出,雖然在 Swap (a,b) 方法中改變了傳進(jìn)來的參數(shù)的值,但對這個參數(shù)源變量本身并沒有影響,即對 main(String[]) 方法里的 a,b 變量沒有影響。那說明,參數(shù)類型是簡單類型的時候,是按值傳遞的。以參數(shù)形式傳遞簡單類型的變量時,實際上是將參數(shù)的值作了一個拷貝傳進(jìn)方法函數(shù)的,那么在方法函數(shù)里再怎么改變其值,其結(jié)果都是只改變了拷貝的值,而不是源值。

  1. 什么是引用

Java 是傳值還是傳引用,問題主要出在對象的傳遞上,因為 Java 中簡單類型沒有引用。既然爭論中提到了引用這個東西,為了搞清楚這個問題,我們必須要知道引用是什么。

簡單的說,*引用其實就像是一個對象的名字或者別名 (alias),一個對象在內(nèi)存中會請求一塊空間來保存數(shù)據(jù),根據(jù)對象的大小,它可能需要占用的空間大小也不等。訪問對象的時候,我們不會直接是訪問對象在內(nèi)存中的數(shù)據(jù),而是通過引用去訪問。引用也是一種數(shù)據(jù)類型,我們可以把它想象為類似 C++ 語言中指針的東西,它指示了對象在內(nèi)存中的地址——只不過我們不能夠觀察到這個地址究竟是什么。

如果我們定義了不止一個引用指向同一個對象,那么這些引用是不相同的,因為引用也是一種數(shù)據(jù)類型,需要一定的內(nèi)存空間(stack,棧空間)來保存。但是它們的值是相同的,都指示同一個對象在內(nèi)存(heap,堆空間)的中位置。比如:
String a="This is a Text!";

String b=a;
1.42.png

通過上面的代碼和圖形示例不難看出,a 和 b 是不同的兩個引用,我們使用了兩個定義語句來定義它們。但它們的值是一樣的,都指向同一個對象 "This is a Text!"。但要注意String 對象的值本身是不可更改的 (像 b = "World"; b = a; 這種情況不是改變了 "World" 這一對象的值,而是改變了它的引用 b 的值使之指向了另一個 String 對象 a)。

1.43.png

如圖,開始b 的值為綠線所指向的“Word Two”,然后 b=a; 使 b 指向了紅線所指向的”Word“.

這里我描述了兩個要點:

(1) 引用是一種數(shù)據(jù)類型(保存在stack中),保存了對象在內(nèi)存(heap,堆空間)中的地址,這種類型即不是我們平時所說的簡單數(shù)據(jù)類型也不是類實例(對象);

(2) 不同的引用可能指向同一個對象,換句話說,一個對象可以有多個引用,即該類類型的變量。

3. 對象是如何傳遞的呢

隨著學(xué)習(xí)的深入,你也許會對對象的傳遞方式產(chǎn)生疑問,即對象究竟是“按值傳遞”還是“按引用傳遞”?

(1)認(rèn)為是“按值傳遞”的:

package test;

 

public class Test {

      public static void Sample(int a){

         a+=20;

         System.out.println("a: "+a);

      }

     

      public static void main(String[] args){

         int b=10;

         Sample(b);

         System.out.println("b: "+b);

   }

}

運(yùn)行結(jié)果:

a: 30

b: 10

在這段代碼里,修改變量 a 的值,不改變變量 b 的值,所以它是“值傳遞”。

(2)認(rèn)為是“按引用傳遞”的:

package test;

 

public class Test {

      public static void Sample(StringBuffer a){

         a.append(" Changed ");

         System.out.println("a: "+a);

      }

     

      public static void main(String[] args){

         StringBuffer b=new StringBuffer("This is a test!");

         Sample(b);

         System.out.println("b: "+b);

   }

}./*歡迎加入java交流Q君樣:909038429一起吹水聊天

運(yùn)行結(jié)果:

a: This is a test! Changed

b: This is a test! Changed

在Sample(StringBuffer)這個函數(shù)中,修改了引用 a 的值,同時 b 的值也變化了,所以它是“按引用傳遞”的!

那么對象(記住在Java中一切皆對象,無論是int a;還是String a;,這兩個變量a都是對象)在傳遞的時候究竟是按什么方式傳遞的呢?其答案就只能是:即是按值傳遞也是按引用傳遞,但通?;緮?shù)據(jù)類型(如int,double等)我們認(rèn)為其是“值傳遞”,而自定義數(shù)據(jù)類型(class)我們認(rèn)為其是“引用傳遞”。

4. 正確看待傳值還是傳引用的問題

要正確的看待這個問題必須要搞清楚為什么會有這樣一個問題。

實際上,問題來源于 C,而不是 Java。

C 語言中有一種數(shù)據(jù)類型叫做指針,于是將一個數(shù)據(jù)作為參數(shù)傳遞給某個函數(shù)的時候,就有兩種方式:傳值,或是傳指針。 在值傳遞時,修改函數(shù)中的變量值不會改變原有變量的值,但是通過指針卻會改變。

    void Swap(int a,int b){ int c=a;a=b;b=c;}

    void Swap(int *a,int *b){ int c=*a;*a=*b;*b=c; }

    int c=10;

    int d=20;

    Swap(c,d);    //不改變 c , d 的值

    Swap(&c,&d);  //改變 c , d 的值

許多的 C 程序員開始轉(zhuǎn)向?qū)W習(xí) Java,他們發(fā)現(xiàn),使用類似 SwapValue(T,T)(當(dāng)T 為值類型時) 的方法仍然不能改變通過參數(shù)傳遞進(jìn)來的簡單數(shù)據(jù)類型的值,但是如果T時一個引用類型時,則可能將其成員隨意更改。于是他們覺得這很像是 C 語言中傳值/傳指針的問題。但是 Java 中沒有指針,那么這個問題就演變成了傳值/傳引用的問題。可惜將這個問題放在 Java 中進(jìn)行討論并不恰當(dāng)。

討論這樣一個問題的最終目的只是為了搞清楚何種情況才能在方法函數(shù)中方便的更改參數(shù)的值并使之長期有效。

5. 如何實現(xiàn)類似 swap 的方法

傳值還是傳引用的問題,到此已經(jīng)算是解決了,但是我們?nèi)匀徊荒芙鉀Q這樣一個問題:如果我有兩個 int型的變量 a 和 b,我想寫一個方法來交換它們的值,應(yīng)該怎么辦?有很多方法,這里介紹一種簡單的方法:

package test;

 

public class Test {

      public static void Swap(int[] a){

         int c=a[0];

         a[0]=a[1];

         a[1]=c;

      }

     

      public static void main(String[] args){

         int[] a=new int[2];

         a[0]=10;

         a[1]=20;

         Swap(a);

         System.out.println(a[0]);

         System.out.println(a[1]);

   }

}

通過數(shù)組可以方便的實現(xiàn)值類型的數(shù)據(jù)源的交換,不過還有一種方法是將所有變量封裝到一個類里面去,通過引用類型來實現(xiàn)。

image

最新2020整理收集的一些高頻面試題(都整理成文檔),有很多干貨,包含mysql,netty,spring,線程,spring cloud、jvm、源碼、算法等詳細(xì)講解,也有詳細(xì)的學(xué)習(xí)規(guī)劃圖,面試題整理等,需要獲取這些內(nèi)容的朋友請加Q君樣:909038429
/./*歡迎加入java交流Q君樣:909038429一起吹水聊天

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容