Java技術——Java中的引數傳值方式
1. 你覺得下面程式會輸出什麼
- publicstaticvoid change(String s) {
- s = “123”;
- }
- publicstaticvoid main(String args[]) {
- String s = “abc”;
- change(s);
- System.out.println(s);
- }
問我s會輸出什麼。
我想起了各種排序演算法直接把一個int陣列array,通過引數傳遞給一個方法,在方法裡完成資料的移動後,直接在main()函式中輸出array[]中的值就是修改過的。所以我回答說,那應該輸出
然後面試官說,不對,是輸出abc,你覺得原因是什麼。
我想了想回答說,String是final的,所以值是不變的。
接著面試官寫下了如下程式碼:
- publicstaticvoid main(String[] args) {
- MyClass myClass = new MyClass();
- change(myClass);
- System.out.println(myClass.val);
- }
- privatestaticvoid change(MyClass myClass) {
- myClass = new MyClass();
- myClass.val = 2
- }
- publicstaticclass MyClass{
- int val = 1;
- }
問我MyClass類不是final的,但為什麼這裡還是會輸出1。我有點被問懵了,其實後來面試結果又仔細看一下,是自己忽略掉了第八行程式碼,其實把第八行程式碼註釋掉,肯定就輸出2了。
2. Java引數傳值
2.1 值傳遞
先從一個例子說起:
- publicstaticvoid change(int i, int j) {
- inttemp = i;
- i =j;
- j =temp;
- }
- publicstaticvoid main(String[] args) {
- inta = 3;
- intb = 4;
- change(a,b);
- System.out.println(a);
- System.out.println(b);
- }
輸出為:
值傳遞是指方法呼叫時,實際引數把它的值傳遞給對應方法的形參,如change()方法中i和j,也在記憶體空間中分配了儲存單元,這樣形參在change()方法中的改變不會影響實際引數的值。
值傳遞的資料型別包括,八種基本資料型別和String。
2.2 引用傳遞
值傳遞的資料型別包括,八種基本資料型別和String,八種資料型別還比較好理解,比如2.1中的int,但是String並不是基本資料型別,這要怎麼理解呢。比如下面這個例子。
- publicclass Example {
- String str = new String("abc");
- char[] ch = { 'a', 'b', 'c' };
- publicstaticvoid main(String args[]) {
- Example ex = new Example();
- ex.change(ex.str, ex.ch);
- System.out.println(ex.str);
- System.out.println(ex.ch);
- }
- publicvoid change(String str, char ch[]) {
- //ch = new char[]{'a','b','c'};
- str = "change";
- ch[0] = 'c';
- }
- }
這個例子的輸出為:
- abc
- cbc
要知道String和char陣列都是引用型別,不是基本型別。
當我們把str作為引數傳入方法後,會新建另一個變數,已經不是原來的變量了,但是他們指向的資料區域都一樣,所以如果你在方法中改變了str指向的資料區域,即執行str = "change",那也只是改變新建的另一個變數所指向的資料區域,即指向一個新物件"change",str仍然指向原來的資料區域。所以會輸出abc。
但是對於char陣列的例子呢,即物件型別,也就是Object的子類(除了String),是把ch的引用傳遞進來,即引用傳遞。這樣裡面和外面的ch都指向了相同的資料區域,執行ch[0] = 'c',就會把這個資料區域裡的第一個字元改成c,並沒有改變內部ch的資料地址,所以這個修改也會反映到外部的ch。所以會輸出cbc。如果你改變了內部ch所指向的資料區域,即把上面程式碼中註釋的那一行開啟,即執行ch = new char[]{'a','b','c'},這樣ch已經指向一個新的資料區域。輸出的結果肯定也就是abc了。
這樣在1中面試官給的兩段程式碼也就很容易解釋了。
3 兩種資料傳遞總結
值傳遞和引用傳遞的本質區別在於是否在傳遞的時候進行物件的記憶體拷貝。
基本型別是由於在JVM中儲存區域不同於普通物件所以傳遞前會拷貝,傳遞的是拷貝後的值,但是物件在傳遞的時候不拷貝,直接傳“引用值”,指向同一片物件堆記憶體區域,當然要注意String這種特殊情況。