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進行替換操作,實參也跟着改變了。
除了這種傳參數,類似的還有賦值。

 JSONObject paramJson=new JSONObject();
 paramJson.put("date","1993_12_12");
JSONObject paramJson2=paramJson;
paramJson2.put("name","lin");

同樣發現,修改了paramJson2,會影響paramJson。
那么,為什么對這些非基本類型的對象進行賦值或傳參,修改新對象,會影響舊對象?

值傳遞

java中方法參數傳遞方式(以及賦值)是按值傳遞的。
如果參數是基本類型(以及String類型),傳遞的是基本類型(以及String)的變量值。
如果參數是引用類型,傳遞的是該參數所引用的對象在堆中地址值。

值,就是指存儲實際內容的內存塊。
引用,就是指向帶有存儲值內存塊的變量,自身不存儲實際值。

基本類型和引用類型

Java 將內存空間分為堆和棧。
基本類型直接在棧中存儲數值,而引用類型是將變量放在棧中,實際存儲的值是放在堆中,通過棧中的變量指向堆中存放的數據。

JSONObject不是基本類型,而是引用類型。
原來的paramJson指向了某塊內存,而方法參數中的paramJson也指向了相同的內存。
當內存塊中存儲的值改變時,任何指向內存塊的引用都會隨着改變。

示例

1.方法參數(形參)為基本類型以及String,如下。

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

結論:

方法參數(形參)為基本類型以及String,方法參數改變時,不會改變原來的實參。

2.方法參數(形參)為引用類型,如下:

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
https://blog.csdn.net/u010469514/article/details/80838678 (String到底是值傳遞還是引用傳遞?)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM