天真,居然還有人認爲java的參數傳遞方式是引用傳遞

代碼寫的越急,程序跑得越慢。—— Roy Carlson

時間過得真快,2020已經過去了一半,但是疫情好像還沒有真正的消滅,人們出行還是得帶着口罩,天氣越來越熱,受罪啊。

言歸正傳,都2020年了,居然還有人認爲java的參數傳遞方式是引用傳遞,今天我就來講一講java的參數傳遞,好好看,寫的不對的地方,請大聲說出來,反正我也不會改,憋壞了就不好了

基本數據類型傳遞

我們先來看一個普通的例子

package com.ymy.param;

/**
 * @ProjectName: demo
 * @Package: com.ymy.param
 * @ClassName: BaseTypeTest
 * @Author: 流星007
 * @Description: 基本數據類型傳遞
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/7/5 12:52
 * @Version: 1.0
 */
public class BaseTypeTest {

    public static void main(String[] args) {
        int a = 1;
        dosomthing(a);
        System.out.println("主函數a的值 = "+a);

    }

    private static void dosomthing(int a) {
        a = a-1;
        System.out.println("修改過後,a = "+a);

    }
}

這是一個很簡單的一個方法,在主函數main中對變量進行了初始化a=1,然後將a傳遞給dosomthing(),然後再dosomthing中輸出了修改之後的值,最後在主函數中打印a的值,你們覺得這幾句輸出中a的值分別是多少呢?

第一種:
修改過後,a = 0
主函數a的值 = 1
第二種:
修改過後,a = 0
主函數a的值 = 0
第三種:
修改過後,a = 1
主函數a的值 = 1

想要得到答案的話就得先明白參數傳遞的兩個類型:值傳遞和引用傳遞。

什麼是引用傳遞?
在C++中,函數參數的傳遞方式有引用傳遞。所謂引用傳遞是指在調用函數時將實際參數的地址傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數。

什麼是值傳遞?
值傳遞是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數。

我們再回過頭來看上面的例子,如果是引用傳遞的話打印結果應該是第二種情況,如果是值傳遞,打印結果應該是第一種情況,所以到底打印的結果是什麼呢?

我們一起看一看控制檯輸出

Connected to the target VM, address: '127.0.0.1:59333', transport: 'socket'
修改過後,a = 0
主函數a的值 = 1
Disconnected from the target VM, address: '127.0.0.1:59333', transport: 'socket'

Process finished with exit code 0

這就是第一種情況,很明顯,在dosomthing函數中修改了a的值,但是主函數中的a並沒有受到影響,所以肯定不會是引用傳遞,如果是引用傳遞,主函數的a應該會變成0,只有在參數傳遞的時候將主函數的中參數複製一份給dosomthing,才能在dosomthing中修改a不會對主函數造成影響,所以從基本數據類型來看,java的參數傳遞方式爲:值傳遞

這個時候你可能會有疑問了,這只是基本數據類型的傳遞方式,其他的參數類型呢?下面我們一起來看看引用類型和對象類型的傳遞方式。

follow me !!!!!

引用類型傳遞

我們都知道java中的String類型不屬於基本數據類型,它是一個引用類型,也可以說是一個對象,那麼它的傳遞方式是什麼呢?

我們還是先來看例子

package com.ymy.param;

/**
 * @ProjectName: demo
 * @Package: com.ymy.param
 * @ClassName: StringTypeTest
 * @Author: 流星007
 * @Description: String類型傳遞
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/7/5 14:22
 * @Version: 1.0
 */
public class StringTypeTest {

    public static void main(String[] args) {
        String a = "hello";
        dosomthing(a);
        System.out.println("主函數a的值 = "+a);

    }

    private static void dosomthing(String a) {
        a = a+" bug";
        System.out.println("修改過後,a = "+a);

    }

}

打印結果

修改過後,a = hello bug
主函數a的值 = hello

Process finished with exit code 0

我們發現主函數的a並沒有受到dosomthing函數的影響,所以這並不是引用傳遞,這個時候你說是因爲
a = a+" bug";這行代碼生成了新的對象,所以纔會導致數據不一致,我們先來看看a的賦值情況吧

// class version 52.0 (52)
// access flags 0x21
public class com/ymy/param/StringTypeTest {

  // compiled from: StringTypeTest.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 14 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/ymy/param/StringTypeTest; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
    // parameter  args
   L0
    LINENUMBER 17 L0
    LDC "hello"
    ASTORE 1
   L1
    LINENUMBER 18 L1
    ALOAD 1
    INVOKESTATIC com/ymy/param/StringTypeTest.dosomthing (Ljava/lang/String;)V
   L2
    LINENUMBER 19 L2
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "\u4e3b\u51fd\u6570a\u7684\u503c = "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L3
    LINENUMBER 21 L3
    RETURN
   L4
    LOCALVARIABLE args [Ljava/lang/String; L0 L4 0
    LOCALVARIABLE a Ljava/lang/String; L1 L4 1
    MAXSTACK = 3
    MAXLOCALS = 2

  // access flags 0xA
  private static dosomthing(Ljava/lang/String;)V
    // parameter  a
   L0
    LINENUMBER 24 L0
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ALOAD 0
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC " bug"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 0
   L1
    LINENUMBER 25 L1
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "\u4fee\u6539\u8fc7\u540e\uff0ca = "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L2
    LINENUMBER 27 L2
    RETURN
   L3
    LOCALVARIABLE a Ljava/lang/String; L0 L3 0
    MAXSTACK = 3
    MAXLOCALS = 1
}

這是上面代碼的字節碼代碼,我們可以清楚的看到a在賦值的時候都調用了StringBuilder的同String方法,現在我們來看看這個神奇的同String方法。

@Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }

這是StringBuilder中的toString方法,確實是new了一個新的對象,就算是a變成了一個新的對象,如果是引用傳遞,主函數的a就不會受影響嗎?這點我會講完對象類型傳遞之後在進行講解,請繼續往下看。

對象類型傳遞

其實上面的兩種其實很好區分,很多人都知道是值傳遞,很多人說java的傳遞方式是引用傳遞的原因就是出自這裏:傳遞的參數爲對象

有些人看到對象傳遞的時候會改變主函數的值,就認爲java的參數傳遞是引用傳遞,有些人又因爲基本數據類型不會隊主函數的值造成修改,所以他們的結論是:基本數據類型爲值傳遞;對象類型爲引用傳遞,想法很好,那我們現在一起來解開對象傳遞的神祕面紗,它到底是引用傳遞還是值傳遞呢?

go go go !!!!

還是老規矩,我們一起來看一個例子

package com.ymy.param.vo;

/**
 * @ProjectName: demo
 * @Package: com.ymy.param.vo
 * @ClassName: LolVo
 * @Author: 流星007
 * @Description: lol英雄屬性
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/7/5 15:11
 * @Version: 1.0
 */
public class LolVo {

    /**
     * 姓名
     */
    private String name;

    /**
     * 職業
     */
    private String profession;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getProfession() {
        return profession;
    }

    public void setProfession(String profession) {
        this.profession = profession;
    }

    @Override
    public String toString() {
        return "LolVo{" +
                "name='" + name + '\'' +
                ", profession='" + profession + '\'' +
                '}';
    }
}

package com.ymy.param;

import com.ymy.param.vo.LolVo;

/**
 * @ProjectName: demo
 * @Package: com.ymy.param
 * @ClassName: ObjectTypeTest
 * @Author: 流星007
 * @Description: 對象類型傳遞
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/7/5 15:16
 * @Version: 1.0
 */
public class ObjectTypeTest {

    public static void main(String[] args) {
        LolVo lolVo = new LolVo();
        lolVo.setName("無極劍聖");
        lolVo.setProfession("刺客");
        dosomthing(lolVo);
        System.out.println("主函數 lolVo = "+lolVo);

    }

    private static void dosomthing(LolVo lolVo) {
        lolVo.setProfession("戰士");
        System.out.println("dosomthing lolVo = "+lolVo);

    }


}

結果如下:

dosomthing lolVo = LolVo{name='無極劍聖', profession='戰士'}
主函數 lolVo = LolVo{name='無極劍聖', profession='戰士'}

Process finished with exit code 0

主函數中劍聖的職業是刺客,在dosomthing中將他修改成,我們發現主函數中劍聖的職業也被修改成戰士了,顯然這符合引用傳遞的條件,被調用方修改會影響到調用方也就是主函數,所以這個時候很多人就認爲java的對象傳遞的方式爲引用傳遞,如果你也是這麼認爲,那麼你就要認真看一下我後面的分析。

我們先來一個否定它是引用傳遞的例子,請看好,不要眨眼

package com.ymy.param;

import com.ymy.param.vo.LolVo;

/**
 * @ProjectName: demo
 * @Package: com.ymy.param
 * @ClassName: ObjectTypeTest
 * @Author: 流星007
 * @Description: 對象類型傳遞
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日頭條:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/7/5 15:16
 * @Version: 1.0
 */
public class ObjectTypeTest {

    public static void main(String[] args) {
        LolVo lolVo = new LolVo();
        lolVo.setName("無極劍聖");
        lolVo.setProfession("刺客");
        dosomthing(lolVo);
        System.out.println("主函數 lolVo = "+lolVo);

    }

    private static void dosomthing(LolVo lolVo) {
        lolVo = new LolVo();
        lolVo.setProfession("戰士");
        System.out.println("dosomthing lolVo = "+lolVo);

    }


}

做了小小的改動,僅僅只是在dosomthing方法中加了一行代碼:lolVo = new LolVo();

我們再來看運行結果是什麼呢?還會和上面一樣嗎?

dosomthing lolVo = LolVo{name='null', profession='戰士'}
主函數 lolVo = LolVo{name='無極劍聖', profession='刺客'}

Process finished with exit code 0

我們發現主函數中劍聖的屬性變回了刺客,並沒有受到dosomthing函數的影響,如果是引用傳遞的話,主函數中劍聖的職業應該是戰士而不是刺客。這是爲什麼呢?爲什麼是應用傳遞主函數中劍聖的職業因該是戰士呢?

下面我們一起來分析一波

我們假設對象的傳遞方式爲引用傳遞
在這裏插入圖片描述
這是堆棧中的信息,當我們將對象lolVo傳遞給dosomthing的時候,是克隆了一個對象出來還是原來的那個對象呢?我們知道,不管傳遞的是不是它本身,值都是內存的地址引用,我們現在先假設主函數沒有複製,是直接將lolVo傳遞給了dosomthing,那圖形應該是什麼樣的呢?
在這裏插入圖片描述
如果是引用傳遞,格式是不是應該是這樣呢?main主函數和dosomthing函數公用一個lolVo,這個時候我們在dosomthing函數中執行了一句:lolVo = new LolVo();
那又會變成什麼樣呢?
在這裏插入圖片描述
在dosomthing方法中我們new了一個新的LolVo對象,並且將這個新的對象賦值給了lolVo,那是不是代表着main主函數核dosomthing函數中的lolVo應該是一樣的呢,我們再來回顧一下上面的代碼

private static void dosomthing(LolVo lolVo) {
        lolVo = new LolVo();
        lolVo.setProfession("戰士");
        System.out.println("dosomthing lolVo = "+lolVo);

    }

我們做了修改之後,主函數劍聖的職業並沒有修改成戰士,所以,說java是引用傳遞是說不通的,鬧我們再來看看它正確的流程應該是什麼樣的呢?

在這裏插入圖片描述
儘管dosomthing對參數的修改會影響調用方,但是它還是屬於值傳遞,會影響調用方是因爲java轉遞的時候拷貝的是對象的引用地址。

舉個栗子:比如某公司開發了一套員工的內部管理系統,有一個管理員的賬號,你把這個賬號給了你的同事,他直接使用你這個賬號,這就是引用傳遞,如果你是在用戶管理中添加了一條管理員的用戶,再將這個賬號給你的同事,這就是值傳遞,還需要解釋一下,爲什麼值傳遞會影響調用方,如果你給痛的賬號,你同事改了用戶名稱,這對你是不是沒有影響,但如果他手抖把員工全刪了,你這邊還能看到員工信息嗎?就是這個道理,lolVo引用地址相當於管理員賬號,系統內的功能相當於LolVo對象,你修改自己的賬號對別人當然沒有影響,但是你把系統搞沒了,你覺得有影響嗎?這就是爲什麼java的參數傳遞方式爲值傳遞卻能影響調用方。

總結

文章發佈了一會就有人說java的參數傳遞方式是引用傳遞,我呢,如果你說的是對的,那我會虛心接受,所以我就認爲我的講解除了問題,然後我就去oracle官方找了一波,官方給出的也是值傳遞。
在這裏插入圖片描述
英文看不懂?那就右擊翻譯成中文
在這裏插入圖片描述

官方demo地址:https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html
如果還有人和你說java的參數傳遞是引用傳遞的話,請他來看一下我這篇博客,我把他勸退一下。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章