平時程式宣告變數或方法參數,套到期望執行的程式邏輯,就能讓寫好的邏輯動起來,但你是否曾想過程式在執行時,背後如何使用定義好的資料,今天第三天配合流程控制來闡述變數的運行機制
開始前先來弄清楚相關名詞定義,程式資料的取得方式分為下列兩種使用方式:
傳值(Pass By Value): 取得參數或變數保存的實際值
傳址(Pass By Refrence): 直接操作目標的記憶體位置
在了解相關名詞後,現在來印證Java是基於傳值方式來操作資料
public static void main(String[] args) {
int age = 15;
modifyAge(age);
System.out.printf("main Method age %d\n", age);
// output: I Method modifyAge value is 15
}
static void modifyAge(int a) {
a = 10;
System.out.printf("I Method modifyAge value is %d\n", a);
// output: I Method modifyAge value is 10
}
這段程式碼先在 main方法定義了age 變數並設初始值為15,從打印的結果可以得知外面的age變數,並沒有因為modifyAge方法重新賦值為10,而改變了實際值
現在上第二個範例程式來做比對
static class Son {
private String name;
public String getName() {
return this.name;
}
public void setName(String _name) {
this.name = _name;
}
}
static void changeName(Son s) {
s.setName("Bob");
}
public static void main(String[] args) {
// 建立實例物件
Son instance = new Son();
instance.setName("Jeffy");
System.out.println(instance.getName());
// output: Jeffy
changeName(instance);
System.out.println(instance.getName());
// output: Bob
}
上述程式碼依序分別打印了 Jeffy和Bob字串,與第一個範例差異在於,外面的局部變數instance屬性name,被changeName方法修改後,也跟著被修改了,在回到開頭的解釋的傳值與傳址的名詞定義,範例一的結果較貼近傳值,第二個範例則是傳址,但其實Java是以傳值方式傳遞參數至方法中
現在修改 changeName的實作邏輯
static void changeName(Son s) {
s.setName("Bob");
// 將參數重新賦值到新的 Son實例
s = new Son();
s.setName("Lucy");
System.out.printf("changeName son is %s \n", s.getName());
// output: changeName son is Lucy
}
實際執行後可以從打印結果發現,外部的變數 instance 屬性並沒影響,反而 changeName的參數 s,就像重新定位了不同參考值,這個行為說明了Java是值參考,原理在於參數s是複製了變數instance的值到方法中
如果是傳址方式使用參數,那麼行為會變成傳遞變數instance的「記憶體位址」到s參數中,此時在 changeName內部將參數 s重新賦值,相當於將對外面的 instance 變數進行操作
參考
菜豬工程師(https://matthung0807.blogspot.com/2019/01/java-pass-by-valuepass-by-reference.html)