Java都是值傳遞

很多人看到這個(gè)可能都很疑惑,Java不是分為值傳遞和引用傳遞嘛?怎么會(huì)都是值傳遞呢?
確實(shí),在很多時(shí)候我們都會(huì)說:Java傳遞分為值傳遞(基本數(shù)據(jù))和引用傳遞(引用數(shù)據(jù):對(duì)象、字符串),這是最容易接受的。但是,仔細(xì)查閱資料發(fā)現(xiàn),Java中只有值傳遞。
那么為什么還要區(qū)分值傳遞和引用傳遞呢?

public class CallByValue {
    
    private static int x=10;
    
    public static void updateValue(int value){
        value = 3 * value;
    }
    
    public static void main(String[] args) {
        System.out.println("調(diào)用前x的值:"+x);
        updateValue(x);
        System.out.println("調(diào)用后x的值:"+x);
    }

運(yùn)行結(jié)果:

調(diào)用前x的值:10
調(diào)用后x的值:10

可以看到x的值并沒有變化,接下來我們一起來看一下具體的執(zhí)行過程:


分析:

  • value被初始化為x值的一個(gè)拷貝(也就是10)
  • value被乘以3后等于30,但注意此時(shí)x的值仍為10!
  • 這個(gè)方法結(jié)束后,參數(shù)變量value不再使用,被回收。

結(jié)論:當(dāng)傳遞方法參數(shù)類型為基本數(shù)據(jù)類型(數(shù)字以及布爾值)時(shí),一個(gè)方法是不可能修改一個(gè)基本數(shù)據(jù)類型的參數(shù)。

當(dāng)然java中除了基本數(shù)據(jù)類型還有引用數(shù)據(jù)類型,也就是對(duì)象引用,那么對(duì)于這種數(shù)據(jù)類型又是怎么樣的情況呢?

聲明一個(gè)User對(duì)象類型:

public class User {
    private String name;
    private int age;
    public User(String name, int age) {
        this.name=name;
        this.age=age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

執(zhí)行類如下:

public class CallByValue {
    private static User user=null;
    public static void updateUser(User student){
        student.setName("Lishen");
        student.setAge(18);
    }
    
    public static void main(String[] args) {
        user = new User("zhangsan",26);
        System.out.println("調(diào)用前user的值:"+user.toString());
        updateUser(user);
        System.out.println("調(diào)用后user的值:"+user.toString());
    }

運(yùn)行結(jié)果:

調(diào)用前user的值:User [name=zhangsan, age=26]
調(diào)用后user的值:User [name=Lishen, age=18]

很顯然,User的值被改變了,也就是說方法參數(shù)類型如果是引用類型的話,引用類型對(duì)應(yīng)的值將會(huì)被修改


image.png

分析:

  • student變量被初始化為user值的拷貝,這里是一個(gè)對(duì)象的引用。
  • 調(diào)用student變量的set方法作用在這個(gè)引用對(duì)象上,user和student同時(shí)引用的User對(duì)象內(nèi)部值被修改。
  • 方法結(jié)束后,student變量不再使用,被釋放,而user還是沒有變,依然指向User對(duì)象。

結(jié)論:當(dāng)傳遞方法參數(shù)類型為引用數(shù)據(jù)類型時(shí),一個(gè)方法將修改一個(gè)引用數(shù)據(jù)類型的參數(shù)所指向?qū)ο蟮闹怠?/p>

通過上面的實(shí)例我們可能就會(huì)覺得java同時(shí)擁有按值調(diào)用和按引用調(diào)用啊,可惜的是這樣的理解是有誤導(dǎo)性的,雖然上面引用傳遞表面上體現(xiàn)了按引用調(diào)用現(xiàn)象,但是java中確實(shí)只有按值調(diào)用而沒有按引用調(diào)用。
下面我們通過一個(gè)反例來說明:

public class CallByValue {
    private static User user=null;
    private static User stu=null;
    
    /**
     * 交換兩個(gè)對(duì)象
     * @param x
     * @param y
     */
    public static void swap(User x,User y){
        User temp =x;
        x=y;
        y=temp;
    }
    
    public static void main(String[] args) {
        user = new User("user",26);
        stu = new User("stu",18);
        System.out.println("調(diào)用前user的值:"+user.toString());
        System.out.println("調(diào)用前stu的值:"+stu.toString());
        swap(user,stu);
        System.out.println("調(diào)用后user的值:"+user.toString());
        System.out.println("調(diào)用后stu的值:"+stu.toString());
    }
}

我們通過一個(gè)swap函數(shù)來交換兩個(gè)變量user和stu的值,在前面我們說過,如果是按引用調(diào)用那么一個(gè)方法可以修改傳遞引用所對(duì)應(yīng)的變量值,也就是說如果java是按引用調(diào)用的話,那么swap方法將能夠?qū)崿F(xiàn)數(shù)據(jù)的交換,而實(shí)際運(yùn)行結(jié)果是:

調(diào)用前user的值:User [name=user, age=26]
調(diào)用前stu的值:User [name=stu, age=18]
調(diào)用后user的值:User [name=user, age=26]
調(diào)用后stu的值:User [name=stu, age=18]

我們發(fā)現(xiàn)user和stu的值并沒有發(fā)生變化,也就是方法并沒有改變存儲(chǔ)在變量user和stu中的對(duì)象引用。swap方法的參數(shù)x和y被初始化為兩個(gè)對(duì)象引用的拷貝,這個(gè)方法交換的是這兩個(gè)拷貝的值而已,最終,所做的事都是白費(fèi)力氣罷了。在方法結(jié)束后x,y將被丟棄,而原來的變量user和stu仍然引用這個(gè)方法調(diào)用之前所引用的對(duì)象。

這個(gè)過程也充分說明了java程序設(shè)計(jì)語言對(duì)對(duì)象采用的不是引用調(diào)用,實(shí)際上是對(duì)象引用進(jìn)行的是值傳遞,當(dāng)然在這里我們可以簡單理解為這就是按值調(diào)用和引用調(diào)用的區(qū)別,而且必須明白即使java函數(shù)在傳遞引用數(shù)據(jù)類型時(shí),也只是拷貝了引用的值罷了,之所以能修改引用數(shù)據(jù)是因?yàn)樗鼈兺瑫r(shí)指向了一個(gè)對(duì)象,但這仍然是按值調(diào)用而不是引用調(diào)用。

String和StringBuffer
public class ReferencePkValue1 {
    public static void main(String[] args){
        ReferencePkValue1 pk=new ReferencePkValue1();
        //String類似基本類型,值傳遞,不會(huì)改變實(shí)際參數(shù)的值
        String test1="Hello";
        pk.change(test1);
        System.out.println(test1);
         
        //StringBuffer和StringBuilder等是引用傳遞
        StringBuffer test2=new StringBuffer("Hello");
        pk.change(test2);
         
        System.out.println(test2.toString());
    }
     
    public void change(String str){
        str=str+"world";
    }
     
    public void change(StringBuffer str){
        str.append("world");
    }
}

結(jié)果:

Hello
Helloworld

這里我們可以簡單地理解為:
String類似基本類型,值傳遞,不會(huì)改變實(shí)際參數(shù)的值;StringBuffer是引用傳遞

總結(jié):

  • 基本數(shù)據(jù)類型傳值,對(duì)形參的修改不會(huì)影響實(shí)參;
  • 引用類型傳引用,形參和實(shí)參指向同一個(gè)內(nèi)存地址(同一個(gè)對(duì)象),所以對(duì)參數(shù)的修改會(huì)影響到實(shí)際的對(duì)象;
  • 上面兩種傳遞都進(jìn)行了值拷貝的過程。

參考資料:
https://www.cnblogs.com/binyue/p/3862276.html
https://www.cnblogs.com/pan1042/p/11275497.html
https://www.cnblogs.com/laipDIDI/articles/2524309.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容