1. 程式人生 > >java是傳值還是傳引用

java是傳值還是傳引用

計算機 reference 成了 了解 傳遞參數 指針傳遞 數組 bubuko 自己的

1,C/C++中的指針、引用、句柄

C++primer中對 對象的定義:對象是指一塊能存儲數據並具有某種類型的內存空間,一個對象a,它有值和地址&a。

指針:p也是對象,它同樣有地址&p和存儲的值p,只不過,p存儲的數據類型是數據的內存地址。

對象有常量(const)和變量之分,既然指針本身是對象,那麽指針所存儲的地址也有常量和變量之分,常量指針是指,指針這個對象所存儲的地址是不可以改變的,而指向常量的指針的意思是,不能通過該指針來改變這個指針所指向的對象。

引用:引用是對象的別名,可以看作是一個常量指針,相比於普通指針功能受限但是安全性更高(引用的一個優點是它一定不為空)。定義一個引用的時候,程序把該引用和它的初始值綁定在一起,而不是拷貝它。計算機必須在聲明r的同時就要對它初始化,並且,r一經聲明,就不可以再和其它對象綁定在一起了。

句柄:是指針的指針,句柄實際上是一個數據,是一個Long (整長型)的數據。句柄是一個標識符,是拿來標識對象或者項目的,它就象我們的姓名一樣。

Windows是一個以虛擬內存為基礎的操作系統。在這種系統環境下,Windows內存管理器經常在內存中來回移動對象,依此來滿足各種應用程序的內存需要。對象被移動意味著它的地址變化了。如果地址總是如此變化,我們該到哪裏去找該對象呢?為了解決這個問題,Windows操作系統為各應用程序騰出一些內存儲地址,用來專門登記各應用對象在內存中的地址變化,而這個地址(存儲單元的位置)本身是不變的。Windows內存管理器在移動對象在內存中的位置後,把對象新的地址告知這個句柄地址

來保存。

2,java中的引用和句柄

1)java中的3種數據類型:
基本數據類型(數值型(整數類型(int,short,byte,long),浮點類型(float,double)),字符型(char),布爾型(boolean));

引用數據類型(類(class),接口(interface),數組([])));

null類型;

2)java中的引用和句柄

句柄:為了區別引用類型的變量標識符和基本數據類型變量標識符,我們特別的(特意的)使用Handle來稱呼引用類型的變量標識符。

句柄其實就是變量,不同於基本變量的關鍵,它是一種間接尋址方式。

引用:對象的引用是創建對象時的返回值(引用是new表達式的返回值)。

例:new A(); 這裏真正創建了一個對象,但我們沒有用句柄去持有(hold、拿著、保存)該引用。handle是變量,reference是一種變量值。

(1)句柄 a——常見為A a;

(2)創建對象——new A();這才是真正的創建對象。對象一般通過new表達式來創建。計算new表達式的值的整個過程,在微觀上完成了對象的創建和這個對象自己的成員變量的初始化,在宏觀上得到new表達式的值,這個值稱為新對象的引用;

(3)引用:new A()的值。引用可以簡單的看作對象占據內存空間的地址;通過對象的引用,就可以方便的與其他對象區別開來,引用就是對象獨特的身份標識。

(4)句柄的初始化:句柄 = 引用;即把引用賦值給句柄,如語句a=new A();完成句柄的初始化後,就可以用句柄遙控對象了

參考:

http://www.cnblogs.com/lsjwzh/archive/2010/05/08/1730480.html

3,傳值和傳引用問題的由來

C 語言中將一個數據作為參數傳遞給某個函數的時候,就有兩種方式:傳值,或是傳指針,它們的區別,可以用一個簡單的例子說明:

 1 void SwapValue(int a, int b) {
 2     int t = a;
 3     a = b;
 4     b = t;
 5 }
 6 void SwapPointer(int * a, int * b) {
 7     int t = * a;
 8     * a = * b;
 9     * b = t;
10 }
11 void main() {
12     int a = 0, b = 1;
13     printf("1 : a = %d, b = %d\n", a, b);
14     SwapValue(a, b);
15     printf("2 : a = %d, b = %d\n", a, b);
16     SwapPointer(&a, &b);
17     printf("3 : a = %d, b = %d\n", a, b);
18 }

運行結果:

1 : a = 0, b = 1
2 : a = 0, b = 1
3 : a = 1, b = 0

大家可以明顯的看到,按指針傳遞參數可以方便的修改通過參數傳遞進來的值,而按值傳遞就不行。

在java中:

使用類似 SwapValue 的方法仍然不能改變通過參數傳遞進來的簡單數據類型的值,但是如果是一個對象,則可能將其成員隨意更改。

這很像是 C 語言中傳值/傳指針的問題。但是 Java 中沒有指針,於是很多人就把這個問題演變成了傳值/傳引用的問題。

4,java中 的參數傳遞

 1 public class Test {
 2     public static void test(boolean test, int a, int[] b, String c,StringBuffer d) {
 3         test = ! test;
 4         a = 10;
 5         int temp = b[0];
 6         b[0] = b[1];
 7         b[1] = temp;
 8         c = "cc";
 9         d.append(" word");
10         System.out.println("In test(boolean) : test = " + test);
11         System.out.println("In a(int) : a = " + a);
12         System.out.println("In b(int[]) : b = " + b[0] + b[1]);
13         System.out.println("In c(String) : c = " + c);
14         System.out.println("In d(StringBuffer) : d = " + d);
15         System.out.println();        
16     }
17     public static void main(String[] args) {
18         boolean test = true;
19         int a = 1;
20         int[] b = {6,7};
21         String c = new String("c");
22         StringBuffer d = new StringBuffer("hello");
23         System.out.println("Before test(boolean) : test = " + test);
24         System.out.println("Before a(int) : a = " + a);
25         System.out.println("Before b(int[]) : b = " + b[0] + b[1]);
26         System.out.println("Before c(String) : c = " + c);
27         System.out.println("Before d(StringBuffer) : d = " + d);
28         System.out.println();        
29         test(test,a,b,c,d);
30         System.out.println("After test(boolean) : test = " + test);
31         System.out.println("After a(int) : a = " + a);
32         System.out.println("After b(int[]) : b = " + b[0] + b[1]);
33         System.out.println("After c(String) : c = " + c);
34         System.out.println("After d(StringBuffer) : d = " + d);
35     }
36 }

技術分享圖片

查閱了網上有好多關於這個問題的爭論,我覺得主要是對對象,引用,句柄這些概念的理解有所不同造成的。

基於上述概念和實驗結果,我們的結論是:

1)對於基本數據類型的參數傳遞是按值傳遞的,即傳入方法中的是基本數據類型的一個副本

例如實驗中的boolean類型的test和int類型的a,傳入方法改變其值,源值沒有改變。

2)對於引用數據類型的參數是按引用(對象的存儲地址值)傳遞的,即傳入方法的不是引用數據類型的實例(對象)的一個副本。

那為什麽上述實驗中String類型c並沒有改變源對象呢?

我們仔細看一下,第一步把“c”對象的引用傳給方法裏的句柄c(這裏的句柄(變量)c是方法裏的與main()函數裏的句柄c沒有關系);

        第二步在方法裏又把“cc”對象的引用賦給了方法裏的句柄c。(關鍵是這裏的賦值操作使方法裏的句柄c與main()函數裏的對象沒有了任何關系)

        所以,方法裏的句柄c裏存的引用是“cc”對象的地址,而main()函數裏句柄c裏存的引用是“c”對象的地址。

參考:

http://old.bccn.net/Article/kfyy/java/jszl/200601/3069.html

java是傳值還是傳引用