1. 程式人生 > 實用技巧 >Java值傳遞還是引用傳遞?

Java值傳遞還是引用傳遞?

回顧:

在程式設計語言中,將引數傳遞分為按值呼叫按引用呼叫。按值呼叫:表示方法接收的是呼叫者提供的。而按引用呼叫表示方法接收的是呼叫者提供的變數地址
一個方法可以修改傳遞引用所對應的變數值,而不能修改傳遞值呼叫所對應的變數值。

Java總是採用按值呼叫。方法得到的是所有引數值的一個拷貝,特別的,方法不能修改傳遞給它的任何引數變數的內容。

方法引數共有兩種型別:

  • 基本資料型別
  • 物件引用

1. 基本資料型別為引數

檢視一下的程式碼:

public class ParamTest {
    public static void main(String[] args) {
        int price = 5;
        doubleValue(price);
        System.out.print(price);
    }

    public static void doubleValue(int x) {
        x = 2 * x;
    }
}

【輸出結果】: 5

可以看到,這個方法執行之後,price的值並沒有變化。接下來,看一下doubleValue具體的執行過程為:

  1. x被初始化為price值的一個拷貝,即5
  2. x乘以2後等於10。但是price沒有變化,依然是5
  3. doubleValue執行完後,引數變數不再使用

  

2. 物件引用為引數

從上面的例子我們已經知道一個方法不能修改一個基本資料型別的引數。而物件引用作為引數就不同了。看下面的例子:

class Student {

    private float score;

    public Student(float score) {
        this.score = score;
    }

    public void setScore(float score) {
        this.score = score;
    }

    public float getScore() {
        return score;
    }


}

public class ParamTest {
    public static void main(String[] args) {
        Student stu = new Student(80);
        raiseScore(stu);
        System.out.print(stu.getScore());
    }

    public static void raiseScore(Student s) {
        s.setScore(s.getScore() + 10);
    }
}

【執行結果】:

90.0

可以看出,Student例項s的內容改變了。

具體執行過程為:

  1. s被賦予stu值的拷貝,這裡是一個物件的引用
  2. raiseScore方法應用於這個應用。s和stu指向同一物件,該物件的分數增加了10
  3. raiseScore方法結束後,s不再使用,stu指向的那個物件分數增加了10

3. 對物件是值呼叫還是引用傳遞?

首先編寫一個交換兩個學生的方法:

public static void swap(Student x, Student y) {
    Student temp = x;
    x = y;
    y = temp;
 } 

如果java對物件是採用的是引用傳遞,那個這個方法是可以的。那麼x,y物件的分數是交換的。看下面的例子:

class Student {

    private float score;

    public Student(float score) {
        this.score = score;
    }

    public void setScore(float score) {
        this.score = score;
    }

    public float getScore() {
        return score;
    }
}

public class ParamTest {
    public static void main(String[] args) {
        Student a = new Student(0);
        Student b = new Student(100);

        System.out.println("交換前:");
        System.out.println("a的分數:" + a.getScore() + "--- b的分數:" + b.getScore());

        swap(a, b);

        System.out.println("交換後:");
        System.out.println("a的分數:" + a.getScore() + "--- b的分數:" + b.getScore());
    }

    public static void swap(Student x, Student y) {
        Student temp = x;
        x = y;
        y = temp;
    }
}

【執行結果】:

交換前:
a的分數:0.0--- b的分數:100.0
交換後:
a的分數:0.0--- b的分數:100.0

可以看出,兩者並沒有實現交換。說明引用傳遞的說法是不正確的。接下來一步一步看看swap呼叫的過程:

  1. 將物件a,b的拷貝分別賦值給x,y,此時a和x指向同一物件,b和y指向同一物件
  2. swap方法體完成x,y的的交換,此時a,b並沒有變化
  3. 方法執行完成,x和y不再使用,a依舊指向new Student(0),b指向new Student(100)

首先,建立兩個物件:

然後,進入方法體,將物件a,b的拷貝分別賦值給x,y:

接著,交換x,y的值:

swap執行完成,x,y不再使用,回到建立時狀態。

從這個過程中可以看出,Java對物件採用的不是引用呼叫,實際上,物件引用進行的是值傳遞。

總結一下java中方法引數的使用情況:

  • 一個方法不能修改一個基本資料型別的引數(即數值型和布林型)
  • 一個方法可以改變一個物件引數的狀態
  • 一個方法不能讓物件引數引用一個新的物件