Java的參數傳遞是「按值傳遞」仍是「按引用傳遞」?

Java 編程語言中最大的困惑之一就是: java 是按值傳遞仍是按引用傳遞。我在面試中常常會問面試者這個問題,但仍是有不少面試者對這個問題的理解不是很正確。
在這裏插入圖片描述
有不少面試者是這樣理解的:java

  • 若是傳遞類型爲基礎數據類型,則按值傳遞,
  • 若是傳遞類型爲類,則按引用傳遞。

這樣的理解正確嗎?他們甚至還能夠寫出示例代碼來驗證他們的想法,讓咱們來一塊兒看一看大多數人是如何驗證「基礎類型按值傳遞,非基礎類型按引用傳遞」這個想法的:程序員

基礎類型數據做爲參數傳遞面試

/**
 * 基礎類型數據做爲參數傳遞
 * @Author: danding
 * @Date: 2019/11/5
 */
public class TestParams {

    public static void main(String[] args){
        int x = 6;
        System.out.println("x的初始值爲:" + x);
        add(x);
        System.out.println("x的最終值爲:" + x);
    }

    public static void add(int x){
        x = x + 1;
        System.out.println("add 方法中的x值爲:" + x);
    }

}

運行結果:算法

x的初始值爲:6
add 方法中的x值爲:7
x的最終值爲:6

非基礎類型做爲參數傳遞
首先咱們定義一個類編程

/**
 * 定義一個女友的類
 * (簡陋了點,只有年齡,但不影響咱們使用呀)
 * @Author: danding
 * @Date: 2019/11/5
 */
public class GrilFriend {
    private int age;

    public GrilFriend(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

而後咱們將建立實例並做爲參數傳遞編程語言

/**
 * 基礎類型數據做爲參數傳遞
 * @Author: danding
 * @Date: 2019/11/5
 */
public class TestParams {

    public static void main(String[] args){
        GrilFriend gril = new GrilFriend(18);
        System.out.println("女友的初始年齡爲:" + gril.getAge());
        add(gril);
        System.out.println("女友的最長年齡爲:" + gril.getAge());
    }

    private static void add(GrilFriend friend){
        friend.setAge(friend.getAge()+1);
        System.out.println("女友在方法中的年齡爲:" + friend.getAge());
    }

}

運行結果:this

女友的初始年齡爲:18
女友在方法中的年齡爲:19
女友的最長年齡爲:19

非基礎類型做爲參數傳遞時,值的確被修改了。code

這個時候不少同窗經過以上兩個示例驗證,本身就得出了本身的結論:視頻

若是傳遞類型爲基礎數據類型,則按值傳遞,不然爲按引用傳遞。對象

在此說明,這個理解是錯誤的,錯誤的,錯誤的。下面咱們就來講說 Java中的參數傳遞究竟是按值傳遞仍是按引用傳遞?

首先說下正確的答案:Java 的參數傳遞,不論是基本數據類型仍是引用類型的參數,都是按值傳遞,沒有按引用傳遞!

首先,咱們應該瞭解按值傳遞或按引用傳遞的含義。

  • 按值傳遞:將方法參數值複製到另外一個變量,而後傳遞複製的對象,將其稱爲按值傳遞。
  • 按引用傳遞:將對實際參數的別名或引用傳遞給方法,將其稱爲按引用傳遞的緣由。

你個糟老頭子壞得狠,我信你個鬼,你這個解釋給我要給差評.....

且聽老夫(哦,不,是小編)慢慢道來...

當一個對象被看成參數傳遞到一個方法後,在此方法內能夠改變這個對象的屬性,那麼這裏究竟是「按值傳遞」仍是「按引用傳遞」?

答:是按值傳遞。Java 語言的參數傳遞只有「按值傳遞」。當一個實例對象做爲參數被傳遞到方法中時,參數的值就是該對象的引用的一個副本。指向同一個對象,對象的內容能夠在被調用的方法內改變,但對象的引用(不是引用的副本) 是永遠不會改變的。

基礎類型參數傳遞

這個上面的示例已經驗證了,爲按值傳遞,這個你們應該不會有什麼異議。

非基礎類型參數傳遞

咱們重點來講下對象類型做爲參數傳遞

先來看一下傳遞的例子:

public class TestParams {

    public static void main(String[] args){
        Person p1 = new Person();
        System.out.println(p1);
        change(p1);
        System.out.println(p1);
    }

    private static void change(Person p2){
        p2 = new Person();
    }

}

class Person{

}

運行結果

Person@677327b6
Person@677327b6

能夠看出兩次打印person的地址值是同樣的,即調用完change() 方法以後,person變量並無發生改變。

這個傳遞過程的示意圖以下:

當執行到Person p1 = new Person();代碼時,程序在堆內存中開闢了一塊內存空間用來存儲Person類的實例對象,同時在棧內存中開闢了一個存儲單元用來存儲該實例對象的引用,即上圖中person指向的存儲單元。

當執行到change(p1);代碼時,person做爲參數傳遞給change()方法,須要注意的是:person將本身存儲單元的內容傳遞給了change()方法的p2變量!此後,在change()方法中對p2的一切操做都是針對p2所指向的存儲單元,與person所指向的那個存儲單元沒有關係了!

這個時候該有同窗說了,那上面那個女友示例中,女友的年齡不是被在方法中修改了嗎?若是傳遞的是副本那不該該修改不了女友的年齡嗎?

若是咱們將女友中的代碼放到內存示例圖中走一遍,你應該就明白其中的道理了。
所謂引用副本,但其所指向的仍是真實的對象,因此修改的仍是真實對象上的屬性。

我但願上面的解釋能消除全部疑問,只須要記住Java 的參數傳遞,不論是基本數據類型仍是引用類型的參數,都是按值傳遞,沒有按引用傳遞!。當您將瞭解堆空間和棧內存以及存儲不一樣對象和引用的位置時,將會更加清楚,有關程序的詳細說明,請閱讀 Java Heap vs Stack


「不積跬步,無以致千里」,但願將來的你能:有夢爲馬 隨處可棲!加油,少年!

關注公衆號:「Java 知己」,天天更新Java知識哦,期待你的到來!

  • 發送「Group」,與 10 萬程序員一塊兒進步。
  • 發送「面試」,領取BATJ面試資料、面試視頻攻略。
  • 發送「玩轉算法」,領取《玩轉算法》系列視頻教程。
  • 千萬不要發送「1024」...
    每日福利
相關文章
相關標籤/搜索