深入解析Java編程中方法的參數(shù)傳遞
在閱讀本文之前,根據(jù)自己的經(jīng)驗(yàn)和理解,大家可以先思考并選擇一下Java函數(shù)的參數(shù)傳遞方式:
A. 是按值傳遞的?
B. 按引用傳遞的?
C. 部分按值部分按引用?
此處暫不宣布正確答案,我們通過一個(gè)簡單的例子讓大家自己找答案:
1. 先定義一個(gè)類型Value
public static class Value { private String value = "value"; public String getValue() { return value; } public void setValue(String value) { this.value = value; } }
2. 寫兩個(gè)函數(shù)newValue和modifyValue:newValue會(huì)將入?yún)⒅赶蛞粋€(gè)新的對象,modifyValue會(huì)調(diào)用入?yún)⒌膕etValue方法修改對象的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方法,修改對象的內(nèi)部值 modifyValue(value2); System.out.println("After modify, HashCode = " + value2.hashCode() + ", value = " + value2.getValue()); }
4. 執(zhí)行結(jié)果日志:
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. 結(jié)果分析:
上述代碼這是非常常見的一種編程模式:在外圍定義|保存|獲取一個(gè)值或?qū)ο?,將這個(gè)對象作為參數(shù)傳入一個(gè)方法,在方法中修改對象的屬性、行為。但兩個(gè)方法newValue和modifyValue的修改方式不一樣,在方法調(diào)用之后,該對象在外圍看來也有很大的差別!如何理解這種差異呢?先溫故一下按值傳遞、按引用傳遞的概念:
* 按值傳遞意味著當(dāng)將一個(gè)參數(shù)傳遞給一個(gè)函數(shù)時(shí),函數(shù)接收的是原始值的一個(gè)副本。因此,如果函數(shù)修改了該參數(shù),僅改變副本,而原始值保持不變。
* 按引用傳遞意味著當(dāng)將一個(gè)參數(shù)傳遞給一個(gè)函數(shù)時(shí),函數(shù)接收的是原始值的內(nèi)存地址,而不是值的副本。因此,如果函數(shù)修改了該參數(shù),參數(shù)的原始值(函數(shù)塊之外的調(diào)用代碼中)也隨之改變。
正確的答案:A——Java函數(shù)是按值傳遞參數(shù)的!
分析一下日志:
* 第一段日志輸出,value1參數(shù)在newValue方法內(nèi)部被改為指向新對象,并輸出了新對象的hashCode和value值,但跳出newValue方法域之后,在main方法中的value1沒有發(fā)生任何變化,這符合按值傳遞的定義和特點(diǎn);如果是按引用傳遞,value1在調(diào)用newValue(Value value)方法之后,應(yīng)該是發(fā)生變化的。
* 第二段日志輸出,value2在modifyValue方法內(nèi)部進(jìn)行了setValue操作,hashCode不變而value被修改,離開modifyValue方法域之后,在main方法中value2確實(shí)發(fā)生了變更。使用過C++的人容易將這種現(xiàn)象理解為:按引用傳遞函數(shù)參數(shù)!因?yàn)檫@跟C++中的按引用傳遞像極了!但這里恰恰是最容易陷入誤區(qū)的地方!
兩段日志的不同現(xiàn)象背后所隱藏的是原理是:Java語言是按值傳遞參數(shù),按引用傳遞對象的;Java中所操作的對象其實(shí)都是操作對象的引用,object本身保存在“堆”中,而對象的“引用“保存在寄存器或“?!敝?。
偽代碼描述一下newValue方法和modifyValue方法的不同之處:
newValue{ Value_ref2 = value_ref1; // 按值傳入引用value_ref1,得到value_ref1的副本 value_obj2 = new Value(); // value_obj2被創(chuàng)建、初始化在“堆“中 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在作為參數(shù)傳入函數(shù)的時(shí)候,首先被復(fù)制了一份副本value1_ref2供函數(shù)域使用,此時(shí)這兩個(gè)ref都是指向同一個(gè)value_obj; newObject函數(shù)中的代碼[ value = new Value(); ] 其實(shí)是將value1_ref1指向了一個(gè)新的對象value_obj2;在這之后的set操作都是對新對象的操作;modifyValue函數(shù)是通過set方法直接操作value_obj1,這是跟newValue函數(shù)的不同之處。
通過值傳遞參數(shù)
調(diào)用一個(gè)方法時(shí)候需要提供參數(shù),你必須按照參數(shù)列表指定的順序提供。
例如,下面的方法連續(xù)n次打印一個(gè)消息:
public static void nPrintln(String message, int n) { for (int i = 0; i < n; i++) System.out.println(message); }
示例
下面的例子演示按值傳遞的效果。
該程序創(chuàng)建一個(gè)方法,該方法用于交換兩個(gè)變量。
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); // 調(diào)用swap方法 swap(num1, num2); System.out.println("After swap method, num1 is " + num1 + " and num2 is " + num2); } /** 交換兩個(gè)變量的方法 */ 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); } }
以上實(shí)例編譯運(yùn)行結(jié)果如下:
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
傳遞兩個(gè)參數(shù)調(diào)用swap方法。有趣的是,方法被調(diào)用后,實(shí)參的值并沒有改變。
相關(guān)文章
詳解SpringBoot中異步請求的實(shí)現(xiàn)與并行執(zhí)行
這篇文章主要為大家詳細(xì)介紹了在SpringBoot中如何是實(shí)現(xiàn)異步請求、并行執(zhí)行,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02Java ArrayList與LinkedList使用方法詳解
Java中容器對象主要用來存儲(chǔ)其他對象,根據(jù)實(shí)現(xiàn)原理不同,主要有3類常用的容器對象:ArrayList使用數(shù)組結(jié)構(gòu)存儲(chǔ)容器中的元素、LinkedList使用鏈表結(jié)構(gòu)存儲(chǔ)容器中的元素2022-11-11Java把數(shù)字格式化為貨幣字符串實(shí)例代碼
這篇文章主要介紹了Java把數(shù)字格式化為貨幣字符串實(shí)例代碼,需要的朋友可以參考下2014-02-02Java HashSet集合存儲(chǔ)遍歷學(xué)生對象代碼實(shí)例
這篇文章主要介紹了Java HashSet集合存儲(chǔ)遍歷學(xué)生對象代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04