首頁  >  文章  >  Java  >  深入解析Java程式設計中方法的參數傳遞

深入解析Java程式設計中方法的參數傳遞

高洛峰
高洛峰原創
2017-01-19 15:11:121105瀏覽

在閱讀本文之前,根據自己的經驗和理解,大家可以先思考並選擇一下Java函數的參數傳遞方式: 
A. 是按值傳遞的? 
B. 按引用傳遞的? 
C. 部分按值部分按引用?
此處暫不宣布正確答案,我們透過一個簡單的例子讓大家自己找答案: 
1. 先定義一個型別Value

public static class Value {
  private String value = "value";
  public String getValue() { return value; }
  public void setValue(String value) { this.value = value; }
}

2. 寫兩個函數newValue和modifyValue:newValue會將入參指向一個新的對象,modifyValue會呼叫入參的setValue方法來修改物件的value值。

public static void newValue(Value value) {
  value = new Value();
  value.setValue("new value");
  System.out.println("In newValue, HashCode = " + value.hashCode() + ", value = " + value.getValue());
}
    
public static void modifyValue(Value value) {
  value.setValue("new value");
  System.out.println("In modifyValue, HashCode = " + value.hashCode() + ", value = " + value.getValue());
}

3. 簡單的測試程式碼

public static void main(String[] args) {
  Value value1 = new Value();
  System.out.println("Before modify, HashCode = " + value1.hashCode() + ", value = " + value1.getValue());
  // 将value1指向新的Value对象
  newValue(value1);
  System.out.println("After modify, HashCode = " + value1.hashCode() + ", value = " + value1.getValue() + "\n");
  Value value2 = new Value();
  System.out.println("Before modify, HashCode = " + value2.hashCode() + ", value = " + value2.getValue());
  // 使用object的set方法,修改对象的内部值
  modifyValue(value2);
  System.out.println("After modify, HashCode = " + value2.hashCode() + ", value = " + value2.getValue());
}

4. 執行結果日誌:

Before modify, HashCode = 12677476, value = value
In newValue, HashCode = 33263331, value = new value
After modify, HashCode = 12677476, value = value
  
Before modify, HashCode = 6413875, value = value
In modifyValue, HashCode = 6413875, value = new value
After modify, HashCode = 6413875, value = new value

5. 結果分析: 
上述程式碼模式是非常常見的一種程式設計模式 |一個值或對象,將這個物件作為參數傳入一個方法,在方法中修改對象的屬性、行為。但兩個方法newValue和modifyValue的修改方式不一樣,在方法呼叫之後,物件在外圍看來也有很大的差別!如何理解這種差異呢?先溫故一下按值傳遞、按引用傳遞的概念: 
* 按值傳遞意味著當將一個參數傳遞給一個函數時,函數接收的是原始值的一個副本。因此,如果函數修改了該參數,僅改變副本,而原始值保持不變。 
* 按引用傳遞意味著當將一個參數傳遞給一個函數時,函數接收的是原始值的記憶體位址,而不是值的副本。因此,如果函數修改了該參數,則參數的原始值(函數塊之外的呼叫程式碼中)也隨之改變。 
正確的答案:A-Java函數是按值傳遞參數的!
分析一下日誌: 
* 第一段日誌輸出,value1參數在newValue方法內部被改為指向新對象,並輸出了新對象的hashCode和value值,但跳出newValue方法域之後,在main方法中的value1沒有發生任何變化,這符合按值傳遞的定義和特徵;如果是按引用傳遞,value1在呼叫newValue(Value value)方法之後,應該是發生變化的。 
* 第二段日誌輸出,value2在modifyValue方法內部進行了setValue操作,hashCode不變而value被修改,離開modifyValue方法域之後,在main方法中value2確實發生了變更。使用過C++的人容易將這種現象理解為:依引用傳遞函數參數!因為這跟C++中的按引用傳遞像極了!但這裡恰恰是最容易陷入迷思的地方!
兩段日誌的不同現象背後所隱藏的是原理是:Java語言是按值傳遞參數,按引用傳遞對象的;Java中所操作的對像其實都是操作對象的引用,object本身保存在“堆”中,而物件的「引用「保存在暫存器或「堆疊」中。 
偽代碼描述newValue方法和modifyValue方法的不同: 

newValue{
  Value_ref2 = value_ref1;  // 按值传入引用value_ref1,得到value_ref1的副本
  value_obj2 = new Value();  // value_obj2被创建、初始化在“堆“中
  value_ref2 -> value_obj2;  // value_ref2 指向value_obj2
value_ref2 ->value_obj2.setValue(“xxx”); // value_obj2 的value被修改
printValueObj2();      // 此处打印的是obj2的值
}
modifyValue{
  Value_ref2 = value_ref1;  // 按值传入引用value_ref1,得到value_ref1的副本
value_ref2 ->value_obj1.setValue(“xxx”); // value_obj1 的value被修改
printValueObj1();      // 此处打印的是obj1的值
}

   

夠清楚了吧! value1_ref1在作為參數傳入函數的時候,首先被複製了一份副本value1_ref2供函數域使用,此時這兩個ref都是指向同一個value_obj;newObject函數中的程式碼[ value = new Value(); ]其實是將value1_ref1指向了一個新的物件value_obj2;這之後的set操作都是對新物件的操作;modifyValue函數是透過set方法直接操作value_obj1,這是跟newValue函數的不同之處。

透過值傳遞參數
呼叫一個方法時候需要提供參數,你必須按照參數列表指定的順序提供。
例如,下面的方法連續n次列印一個訊息:

public static void nPrintln(String message, int n) {
 for (int i = 0; i < n; i++)
  System.out.println(message);
}

範例
下面的範例示範按值傳遞的效果。
該程式建立一個方法,該方法用於交換兩個變數。

public class TestPassByValue {
 
  public static void main(String[] args) {
   int num1 = 1;
   int num2 = 2;
 
   System.out.println("Before swap method, num1 is " +
             num1 + " and num2 is " + num2);
 
   // 调用swap方法
   swap(num1, num2);
   System.out.println("After swap method, num1 is " +
             num1 + " and num2 is " + num2);
  }
  /** 交换两个变量的方法 */
  public static void swap(int n1, int n2) {
   System.out.println("\tInside the swap method");
   System.out.println("\t\tBefore swapping n1 is " + n1
              + " n2 is " + n2);
   // 交换 n1 与 n2的值
   int temp = n1;
   n1 = n2;
   n2 = temp;
 
   System.out.println("\t\tAfter swapping n1 is " + n1
              + " n2 is " + n2);
  }
}

   

以上實例編譯運作結果如下:

Before swap method, num1 is 1 and num2 is 2
    Inside the swap method
        Before swapping n1 is 1 n2 is 2
        After swapping n1 is 2 n2 is 1
After swap method, num1 is 1 and num2 is 2

   

傳遞兩個參數呼叫swap方法。有趣的是,方法被呼叫後,實參的值並沒有改變。

更多深入解析Java程式設計中方法的參數傳遞相關文章請關注PHP中文網!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn