天天看點

面試再問值傳遞與引用傳遞,把這篇文章砸給他!

按值調用(call by value)

按引用調用(call by reference)

所謂的按值調用表示方法接收的是調用着提供的值,而按引用調用則表示方法接收的是調用者提供的變量位址(如果是C語言的話來說就是指針啦,當然java并沒有指針的概念)。

這裡我們需要注意的是一個方法可以修改傳遞引用所對應的變量值,而不能修改傳遞值調用所對應的變量值,這句話相當重要,這是按值調用與引用調用的根本差別,當然如果還不了解,沒關系,下面就要圖文并茂的徹底分析啦。

前面我們說過java中并不存在引用調用,這點是沒錯的,因為java程式設計語言确實是采用了按值調用,即call by value。也就是說方法得到的是所有參數值的一個拷貝,方法并不能修改傳遞給它的任何參數變量的内容。下面我們來看一個例子:

package com.zejian.test;
/**
 * java中的按值調用
 * @author zejian
 */
public class CallByValue {

    private static int x=10;

    public static void updateValue(int value){
        value = 3 * value;
    }

    public static void main(String[] args) {
        System.out.println("調用前x的值:"+x);
        updateValue(x);
        System.out.println("調用後x的值:"+x);
    }

}      

. 運作程式,結果如下:

調用前x的值:10
調用後x的值:10      

可以看到x的值并沒有變化,接下來我們一起來看一下具體的執行過程:

面試再問值傳遞與引用傳遞,把這篇文章砸給他!

分析:

1)value被初始化為x值的一個拷貝(也就是10)

2)value被乘以3後等于30,但注意此時x的值仍為10!

3)這個方法結束後,參數變量value不再使用,被回收。

結論:

當傳遞方法參數類型為基本資料類型(數字以及布爾值)時,一個方法是不可能修改一個基本資料類型的參數。

當然java中除了基本資料類型還有引用資料類型,也就是對象引用,那麼對于這種資料類型又是怎麼樣的情況呢?

我們還是一樣先來看一個例子:

聲明一個User對象類型:

package com.zejian.test;
public class User {
    private String name;
    private int age;
    public User(String name, int age) {
        this.name=name;
        this.age=age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}      

執行類如下:

package com.zejian.test;
/**
 * java中的按值調用
 * @author zejian
 */
public class CallByValue {
    private static User user=null;
    public static void updateUser(User student){
        student.setName("Lishen");
        student.setAge(18);
    }

    public static void main(String[] args) {
        user = new User("zhangsan",26);
        System.out.println("調用前user的值:"+user.toString());
        updateUser(user);
        System.out.println("調用後user的值:"+user.toString());
    }
}      

. 運作結果如下:

調用前user的值:User [name=zhangsan, age=26]
調用後user的值:User [name=Lishen, age=18]
      

很顯然,User的值被改變了,也就是說方法參數類型如果是引用類型的話,引用類型對應的值将會被修改,下面我們來分析一下這個過程:

面試再問值傳遞與引用傳遞,把這篇文章砸給他!

過程分析:

1)student變量被初始化為user值的拷貝,這裡是一個對象的引用。

2)調用student變量的set方法作用在這個引用對象上,user和student同時引用的User對象内部值被修改。

3)方法結束後,student變量不再使用,被釋放,而user還是沒有變,依然指向User對象。

當傳遞方法參數類型為引用資料類型時,一個方法将修改一個引用資料類型的參數所指向對象的值。

雖然到這裡兩個資料類型的傳遞都分析完了,也明白的基本資料類型的傳遞和引用資料類型的傳遞差別,前者将不會修改原資料的值,而後者将會修改引用所指向對象的值。

可通過上面的執行個體我們可能就會覺得java同時擁有按值調用和按引用調用啊,可惜的是這樣的了解是有誤導性的,雖然上面引用傳遞表面上展現了按引用調用現象,但是java中确實隻有按值調用而沒有按引用調用。

到這裡估計不少人都蒙逼了,下面我們通過一個反例來說明(回憶一下開頭我們所說明的按值調用與按引用調用的根本差別)。

package com.zejian.test;
/**
 * java中的按值調用
 * @author zejian
 */
public class CallByValue {
    private static User user=null;
    private static User stu=null;

    /**
     * 交換兩個對象
     * @param x
     * @param y
     */
    public static void swap(User x,User y){
        User temp =x;
        x=y;
        y=temp;
    }

    public static void main(String[] args) {
        user = new User("user",26);
        stu = new User("stu",18);
        System.out.println("調用前user的值:"+user.toString());
        System.out.println("調用前stu的值:"+stu.toString());
        swap(user,stu);
        System.out.println("調用後user的值:"+user.toString());
        System.out.println("調用後stu的值:"+stu.toString());
    }
}      

我們通過一個swap函數來交換兩個變量user和stu的值,在前面我們說過,如果是按引用調用那麼一個方法可以修改傳遞引用所對應的變量值,也就是說如果java是按引用調用的話,那麼swap方法将能夠實作資料的交換,而實際運作結果是:

調用前user的值:User [name=user, age=26]

調用前stu的值:User [name=stu, age=18]

調用後user的值:User [name=user, age=26]

調用後stu的值:User [name=stu, age=18]
      

我們發現user和stu的值并沒有發生變化,也就是方法并沒有改變存儲在變量user和stu中的對象引用。swap方法的參數x和y被初始化為兩個對象引用的拷貝,這個方法交換的是這兩個拷貝的值而已,最終,所做的事都是白費力氣罷了。在方法結束後x,y将被丢棄,而原來的變量user和stu仍然引用這個方法調用之前所引用的對象。

面試再問值傳遞與引用傳遞,把這篇文章砸給他!

這個過程也充分說明了java程式設計語言對對象采用的不是引用調用,實際上是對象引用進行的是值傳遞,當然在這裡我們可以簡單了解為這就是按值調用和引用調用的差別,而且必須明白即使java函數在傳遞引用資料類型時,也隻是拷貝了引用的值罷了,之是以能修改引用資料是因為它們同時指向了一個對象,但這仍然是按值調用而不是引用調用。

總結:

一個方法不能修改一個基本資料類型的參數(數值型和布爾型)。

一個方法可以修改一個引用所指向的對象狀态,但這仍然是按值調用而非引用調用。

上面兩種傳遞都進行了值拷貝的過程。

參考資料:java核心卷1