一、
Java編程中,當對象作爲參數傳遞給方法時候,是按引用傳遞的,但是有的人會說這實質上是按值傳遞的。其實兩者說的都不錯,只是理解的方式不一樣罷了,二者的原理其實是一樣的。
二、
下面通過一個例子來詳細說明Java對象作爲方法參數的時候會出現的情況:
import java.util.ArrayList;
import java.util.List;
/**
* 測試java的引用傳值
*
* Created by Xuyh at 2017/01/05 上午 10:27.
*/
public class SetParamTest {
public static void main(String... args) {
// 集合對象,列表[a, b, c]
List<String> testParam = new ArrayList<String>();
testParam.add("a");
testParam.add("b");
testParam.add("c");
System.out.println("Old: " + testParam.toString() + "\r\n");
// 引用(地址)傳值,並直接修改testParam指向對象的值
change(testParam);
System.out.println("Change1: " + testParam.toString() + "\r\n");
testParam.add("a");
// 引用(地址)傳值,修改指向對象之後再將地址返回
testParam = change2(testParam);
System.out.println("Change2: " + testParam.toString() + "\r\n");
// 集合對象變爲[b, c, d, e]
testParam.add("d");
testParam.add("e");
// 引用(地址)傳值,方法內部new新集合對象並將地址賦給方法局部參數,
// 並不改變testParam的值,testParam仍然指向原地址
sort(testParam);
System.out.println("sort1: " + testParam.toString() + "\r\n");
// 引用(地址)傳值,方法內部new新集合對象並將地址賦給方法局部參數,
// 並將新地址返回,再賦值給testParam,因此testParam指向的對象發生改變
testParam = sort2(testParam);
System.out.println("sort2: " + testParam.toString() + "\r\n");
}
public static void change(List<String> param) {
if (param.contains("a"))
param.remove("a");
}
public static List<String> change2(List<String> param) {
if (param.contains("a"))
param.remove("a");
return param;
}
public static void sort(List<String> param) {
List<String> newParam = new ArrayList<String>();
for (int i = param.size() - 1; i >= 0; i--) {
newParam.add(param.get(i));
}
param = newParam;
}
public static List<String> sort2(List<String> param) {
List<String> newParam = new ArrayList<String>();
for (int i = param.size() - 1; i >= 0; i--) {
newParam.add(param.get(i));
}
return newParam;
}
}
上面的例子簡單的說就是一個列表,[a, b, c],首先通過兩個方法把其中的字符”a”去除,然後通過兩個方法對列表[b, c, d, e]進行倒序排序。同時在操作的過程中不斷將main方法中的testParam輸出出來。
接着請看程序運行的結果:
Old: [a, b, c]
Change1: [b, c]
Change2: [b, c]
sort1: [b, c, d, e]
sort2: [e, d, c, b]
由結果可以看到,兩個change方法都將原來的testParam改變了,而sort方法只有一個改變了testParam的值,這個有趣的現象接下來要好好說說了。下面從四個比較典型的方法來介紹不同情況下對象作爲值傳入方法時候內存中的情況,以便我們更好理解java的方法傳值原理。
三、
change方法
public static void change(List<String> param) {
if (param.contains("a"))
param.remove("a");
}
testParam作爲參數傳遞給param時候,內存內是這樣的:
testParam的地址值複製給了change方法局部變量param,於是param指向了與testParam相同的一個對象。注意這時候param的引用(地址)值是testParam的一份拷貝。
Change方法中將列表的”a”元素去除時候的情況是這樣的:
此時testParam與param指向的對象(同一個)發生了改變,所以方法執行完畢之後輸出testParam是發生改變的。
change2方法
public static List<String> change2(List<String> param) {
if (param.contains("a"))
param.remove("a");
return param;
}
與change方法類似,當對象在方法中發生改變時候,內存中的情況是這樣的:
testParam = change2(testParam);
方法執行到最後,param會返回,也就是param的引用(地址)值會返回,然後又拷貝給testParam(其實是多此一舉,二者的地址值完全相同)。所以方法執行完成之後輸出的testParam是發生改變的。
sort方法
public static void sort(List<String> param) {
List<String> newParam = new ArrayList<String>();
for (int i = param.size() - 1; i >= 0; i--) {
newParam.add(param.get(i));
}
param = newParam;
}
調用方法時候內存中是這樣的
接着,方法中new了一個新的對象,並在處理排序之後將新對象的引用(地址)賦值給param,這時候內存中的情況是這樣的:
可以看到,testParam與param指向的已經不是同一個地址了,並且testParam指向的對象仍然沒有發生改變。因此方法執行完畢之後輸出的結果看來,sort方法是沒有用的。
sort2方法
public static List<String> sort2(List<String> param) {
List<String> newParam = new ArrayList<String>();
for (int i = param.size() - 1; i >= 0; i--) {
newParam.add(param.get(i));
}
return newParam;
}
同sort方法類似,執行sort2方法前內存中是這樣的:
新對象排序完成後是這樣的:
這時候testParam與param都是指向原來的對象
方法的最後把newParam返回,即將newParam的引用(地址)值返回,在main方法中將其賦值給testParam
testParam = sort2(testParam);
然後內存中就是這樣的了:
因此經過返回後的sort2方法是有作用的。
四、
小結:
通過以上的分析可以得出,Java中對象作爲方法參數傳遞的是對象引用(地址)值的拷貝,通俗的講,就是拷貝了一把鑰匙,main方法中的鑰匙跟被調用方法中參數的鑰匙對應能打開的房間是相同的,但是是不同的鑰匙。如果在被調用方法中,參數鑰匙被修改了(new了一個新對象並賦值給參數鑰匙),不會影響main方法鑰匙指向的房間。這點是值得注意的。
因此如果想要在被調用方法中不修改參數指向的對象,可以通過new一個新的對象來實現,但是如果想要new一個新的對象並使main方法該對象變成這個新的對象,就要將其作爲返回值返回並賦值給舊對象變量了。