如何理解Java中參數傳遞只能傳值?

之前學習C#的時候,是徹底在工做崗位上學習,一些底層較爲深刻的道理都不是很清楚。現在學習了Java,對於Java參數傳遞只能傳值,不能傳引用(指針)感到很困惑,在C#中不是經常說把某個引用傳遞到函數中嗎?甚至C#有至關簡便的ref、out參數,明明白白的傳引用。java

通過一番探索,得出的結論代表,Java中我無論你究竟是傳值仍是傳引用,只須要記住原生數據類型(值類型)和String做爲參數傳遞的時候,其本來的值都不會發生改變;而引用類型在做爲參數傳遞時,函數中對其的操做,都會反饋到引用所指向的值。程序員

這個結論不是和C#如出一轍嘛,除了C#能夠方便的使用ref、out之外……編程

下面對Java中參數傳遞只能傳值進行解釋,事實證實,之前我對於C#的傳值仍是傳引用理解的也不夠深刻:函數

先申明:java中的參數傳遞都是值傳遞,沒有引用傳遞。
值傳遞的概念:值傳遞會爲所傳遞的對象(這裏的對象不是java裏的對象object,而是通常意義上的對象)從新開闢一塊空間,因而對所傳對象的操做不會影響到原對象。
現象:有種說法是java中基本數據類型做參數是值傳遞,對象作參數是引用傳遞。其實是錯誤的,真正的引用傳遞,是相似C語言中的指針傳遞,和C#中的ref參數。
理解:爲何對象作參數也是值傳遞呢?
分析:對象作參數傳遞的是對象的引用,暫且能夠理解成對象的地址。因爲是值傳遞,那麼被傳遞的對象自己是不會有任何變化的,因此該對象的引用是不會發生任何變化的。學習

 

一、先舉一個原生數據類型的例子:測試

測試函數:this

    public static void ValueTrans(int a){
        a = 2;//新產生一個值,開闢一個值類型出來,故值不會被修改。
    }

測試此函數的代碼:spa

        int a = 1;
        System.out.println("a's value(before) = " + a);
        ValueTrans(a);
        System.out.println("a's value(after) = " + a);

打印出的結果:設計

a's value(before) = 1
a's value(after) = 1

安裝Java值傳遞的思想來考慮,這個結果是顯而易見的,pass by value,我只是把這個數值傳給你使用。指針

二、那麼Integer如何理解呢?

測試函數:

    public static void ValueTrans2(Integer a){
        a = new Integer(2);
    }


其中a也是一個對象,那麼這麼傳入,是否是傳引用呢?調用了這個函數,a的int值會不會被修改呢?

測試此函數的代碼:

        Integer a2 = new Integer(1);
        System.out.println("a2's value(before) = " + a2);
        ValueTrans2(a2);
        System.out.println("a2's value(after) = " + a2);

打印出的結果:

a2's value(before) = 1
a2's value(after) = 1

結果代表,值仍舊沒有被修改。

如何解釋這個現象:

    理解這裏的關鍵是區分對象和引用。這裏聲明的a2是一個引用,而不是一個對象(只是Java把它設計爲看上去好像是對象同樣)。這個引用它指向了一個對象,這個對象就是後面用new關鍵字生成的對象。所以,能夠說a2指向了一個Integer對象。
    在調用ValueTrans2方法的時候,程序將a2做爲參數傳遞給ValueTrans2方法了。這裏仍然是值傳遞,在ValueTrans2調用過程當中,會產生一份新的引用(不妨叫作y)。此時,a2和y指向了同一個對象。

    可是此時,咱們讓y指向另一個對象, y=new Integer(2); 此時a2和y就指向了不一樣的對象。y修改了它指向的對象的屬性,很顯然不會影響到a2指向的對象。

    (這裏不妨說明一下引用類型的傳遞,如我把一個引用類型x傳入函數中,這裏也是值傳遞,在函數被調用的過程當中,產生了一個新的引用y,x和y指向同一個對象。此時修改y指向的對象,也即修改x所指向的對象!那麼引用類型的傳遞,會形成x所指向的值被修改)

圖解以下:

三、如何理解String?

若是你理解了2當中Integer的傳遞方式,那麼我想String也不難理解。咱們新建一個String,

String string = "old String";


實際上就是

String string2 = new String("old String");

Java的機制,形成當你使用一個String如"old String"時,會新建一個對象出來。

本着負責任的態度,仍舊把相應的測試函數貼上來:

    public static void StringTrans(String string){
        string = "new String";
    }

測試函數的代碼:

        String string = "old String";
        System.out.println("string's value(before) = " + string);
        StringTrans(string);
        System.out.println("string's value(after) = " + string);

打印出的結果:

string's value(before) = old String
string's value(after) = old String

緣由就是,實際上函數StringTrans是複製了一個引用指向原來的對象,可是函數又讓這個引用指向了新的String對象。這不會對原來的引用和原來的對象形成任何影響!

四、如何理解引用類型是傳值?

測試自定義類:

public class People {
    public int age;
    public String name;
    
    public People(int age, String name){
        this.age = age;
        this.name = name;
    }
}

測試的函數:

    public static void ReferTrans(People people){
        people.age = 2;
        people.name = "lisi";
    }

測試函數的代碼:

        People people = new People(1, "zhangsan");
        System.out.println("people's age(before) = " + people.age);
        System.out.println("people's name(before) = " + people.name);
        ReferTrans(people);
        System.out.println("people's age(after) = " + people.age);
        System.out.println("people's name(after) = " + people.name);

打印的結果:

people's age(before) = 1
people's name(before) = zhangsan
people's age(after) = 2
people's name(after) = lisi

設新建的對象是PEOPLE,其引用是people,在調用ReferTrans方法是,仍然是值傳遞,方法賦值了一個引用people2,people2仍舊指向PEOPLE,因此操做people2對對象進行修改,即爲對PEOPLE進行修改(String的話在此時已經new了一個新對象出來),和使用people的效果同樣。表現出現來現象就是,當引用類型傳入函數時,函數中對其的修改,都會表如今原來的引用類型上。

圖解以下:

 

我一直認爲只給分析過程,不給結論的都是耍流氓的行爲。

結論

一、在實際的使用過程當中,咱們沒必要糾結那麼多,原生數據類型和String,在傳入函數的時候,無論函數裏面作什麼,他的值都不會被修改。引用類型傳遞到函數中,只要函數中不是讓參數指向一個新建的對象,那麼函數中的修改會反映到此引用類型上。

二、平時的處理上,能夠把String和原生數據類型放在同一類的狀況下使用,由於Java內部使用了不少的機制,讓String看起來跟原生數據類型同樣。可是String歸根結底是一個引用類型,如在進行比較時,其餘原生數據類型可使用==,而String必須使用equals方法才能比較其內部的值(這點C#作的很是好,直接使用==,不讓程序員過度的瞭解底層,我在作C#編程的過程當中,甚至歷來都沒有以爲string和值類型有什麼不一樣)。

三、Java確實是只能傳值,不能傳引用。所謂傳引用,只在C、C++的傳指針和C#的ref中存在。

相關文章
相關標籤/搜索