最近在項目中發現了相似以下的代碼。java
import com.alibaba.fastjson.JSONObject; public class ValuePassParamTest { public static void main(String[] args) { JSONObject paramJson=new JSONObject(); paramJson.put("date","1993_12_22"); addJson(paramJson); System.out.println("paramJson:"+paramJson.toString()); } public static JSONObject addJson(JSONObject paramJson){ String value=paramJson.getString("date"); if(value!=null && value.contains("_")) { value=value.replace("_",""); } paramJson.put("date",value); return paramJson; } }
這段代碼指望原來的paramJson不改變,只改變方法參數中的paramJson。
結果卻發現對形參paramJson進行替換操做,實參也跟着改變了。
除了這種傳參數,相似的還有賦值。json
JSONObject paramJson=new JSONObject(); paramJson.put("date","1993_12_12"); JSONObject paramJson2=paramJson; paramJson2.put("name","lin");
一樣發現,修改了paramJson2,會影響paramJson。
那麼,爲何對這些非基本類型的對象進行賦值或傳參,修改新對象,會影響舊對象?app
java中方法參數傳遞方式(以及賦值)是按值傳遞的。
若是參數是基本類型(以及String類型),傳遞的是基本類型(以及String)的變量值。
若是參數是引用類型,傳遞的是該參數所引用的對象在堆中地址值。
值,就是指存儲實際內容的內存塊。
引用,就是指向帶有存儲值的內存塊,自身不存儲實際值。.net
Java 將內存空間分爲堆和棧。
基本類型直接在棧中存儲數值,而引用類型是將引用放在棧中,實際存儲的值是放在堆中,經過棧中的引用指向堆中存放的數據。code
JSONObject不是基本類型,而是引用類型。
原來的paramJson指向了某塊內存,而方法參數中的paramJson也指向了相同的內存。
當內存塊中存儲的值改變時,任何指向內存塊的引用都會隨着改變。對象
1.方法參數(形參)爲基本類型以及String,以下。blog
public class ParamTransfer { public void changeValue(int param){ param = param + 1; System.out.println("方法內的值:"+param); } public static void main(String[] args) { int value = 1; ParamTransfer pt = new ParamTransfer(); pt.changeValue(value); System.out.println("main方法的值:"+value); } }
運行結果爲:內存
方法內的值:2 main方法的值:1
結論:get
方法參數(形參)爲基本類型以及String,方法參數改變時,不會改變原來的實參。
2.方法參數(形參)爲引用類型,以下:io
public class ParamTransfer{ public void changeValue(StringBuffer param) { param = param.append("World!"); System.out.println("方法內的值:"+param); } public static void main(String[] args) { ParamTransfer pt = new ParamTransfer(); StringBuffer value = new StringBuffer("Hello "); pt.changeValue(value); System.out.println("main方法的值:"+value); } }
運行結果爲:
方法內的值:Hello World! main方法的值:Hello World!
結論:
方法參數(形參)爲引用類型,方法參法改變時,原來的實參隨之改變。 由於方法參數和原來的實參指向同一內存塊。 當內存塊中存儲的值改變時,任何指向內存塊的引用都會隨着改變。
3.方法參數(形參)爲引用類型,新建對象,方法參數改變,不會改變原來的參數。
public class ParamTransfer{ public void changeValue(StringBuffer param) { StringBuffer sb = new StringBuffer("Hi "); param = sb; param.append("World!"); System.out.println("方法內的值:"+param); } public static void main(String[] args) { ParamTransfer pt = new ParamTransfer(); StringBuffer value = new StringBuffer("Hello "); pt.changeValue(value); System.out.println("main方法的值:"+value); } }
運行結果以下:
方法內的值:Hi World! main方法的值:Hello
結論:
方法參數(形參)爲引用類型,新建對象,開闢新的內存塊,將新對象賦值給形參,那麼形參和實參指向的就是不一樣的內存塊,方法參數改變,不會改變原來的實參。
4.新建對象,並賦值爲原來的值,以下:
public class ParamTransfer{ public void changeValue(StringBuffer param) { StringBuffer sb = new StringBuffer("Hi"); sb=param; sb.append("World"); System.out.println("方法內的值:"+param); } public static void main(String[] args) { ParamTransfer pt = new ParamTransfer(); StringBuffer value = new StringBuffer("Hello"); pt.changeValue(value); System.out.println("main方法的值:"+value); } }
運行結果:
方法內的值:HelloWorld main方法的值:HelloWorld
結論:
方法參數(形參)爲引用類型,新建對象,開闢新的內存塊,那麼新對象和實參指向的就是不一樣的內存塊。 將形參賦值給新對象,賦值以後,新對象和實參又指向同一個內存塊。 因爲指向同一個內存塊,新對象改變,實參也隨之改變。
參考資料:
https://www.zhihu.com/question/31203609
https://blog.csdn.net/newmoons/article/details/51512481