成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

引用傳遞和值傳遞(pass by value vs pass by reference)

longmon / 1354人閱讀

摘要:字節(jié)碼驗(yàn)證于是就寫了以下的類,用來驗(yàn)證然后,然后,看字節(jié)碼如下圖。以上,就是整個關(guān)于引用傳遞和值傳遞的理解,有說的不對的,望指正。

寫這個的原因主要是今天看到了知乎的一個問題,發(fā)現(xiàn)自己有些地方有點(diǎn)懵逼,寫下來記錄一下,知乎上排名第一的答案說的很清楚,不過看了以后依舊有點(diǎn)迷迷糊糊,所以自己寫了個幾行代碼測試。
首先上一個,感覺比較對的結(jié)論:
**Horstmann的《java核心技術(shù)》(中文第8版P115-P117)原文描述:
”java程序設(shè)計語言總是采用值調(diào)用。也就是說,方法得到的是所有參數(shù)值的一個拷貝,特別是,方法不能修改傳遞給它的任何參數(shù)變量的內(nèi)容?!?br>”有些程序員(甚至是本書的作者),認(rèn)為java程序設(shè)計語言對對象采用的是引用調(diào)用,實(shí)際上這種理解是不對的?!?*
然后補(bǔ)充幾句我的理解:

首先,Java在傳遞過程中,傳遞的只有值,但是表現(xiàn)出來的形式,卻既有值傳遞也有引用傳遞,因此,沒必要糾結(jié)于名字,能理解原理即可。

在傳遞對象進(jìn)函數(shù)時,對象的所有數(shù)據(jù)會被拷貝到局部變量中,這也就導(dǎo)致了局部變量修改其成員變量值時會導(dǎo)致原始的變量的成員變量值產(chǎn)生響應(yīng)的改變,因?yàn)樗麄兂钟械某蓡T變量的引用指向了同一個地址塊(內(nèi)存空間)。

而對于傳遞8種基本變量時,也只是拷貝了值,因此對基本變量其本身的修改,無法導(dǎo)致原始變量的的修改。

不過這里需要考慮特殊情況,就是String,其表現(xiàn)形式和8種基本變量一樣,具體下文有分析,而對于String為何要這么做,我也不清楚,不是很懂 jvm 和 Java 的設(shè)計。

一. 值類型和引用類型(此處先不考慮String)的傳遞:
public class Student {
    int age;
    String name;
}
public class TestReference {
    public static void main(String[] args){
        Student student = new Student();
        student.age = 10;
        System.out.println(student.age);//10
        addAge(student);
        System.out.println(student.age);//11
        addAge(student.age);
        System.out.println(student.age);//11
    }
    
    static public void addAge(Student paramStudent){
        paramStudent.age = 11;
    }
   
    static public void addAge(int paramAge){
        paramAge = 12;
    }
}

對以上代碼進(jìn)行解釋

首先addAge(student)調(diào)用的是addAge(Student paramStudent),該部分其實(shí)很好理解,首先,paramStudet對象,拷貝了傳入的studet對象所有的數(shù)據(jù),因此paramStudet它所指向的地址,其實(shí)和student是一樣的,所以,當(dāng)paramStudent改變它的age值時,其觸發(fā)的操作和student改變age的值是一樣的 ,因?yàn)樗麄兌贾赶蛄送粋€地址塊。

其次addAge(student.age)調(diào)用的是addAge(int paramAge),也很好理解,paramAge也只是拷貝了studet.age的值,此處為10,然后改變了paramAge的值,但此時paramAge與引用類型不同,它保存的只有一個值,所以其實(shí)這個parmaAge作為一個局部變量,并不能對原本的student.age產(chǎn)生任何影響

二. String的問題: 1. String問題來源

上面的例子其實(shí)很好搞清楚,但是我在碰到String的時候就有點(diǎn)懵逼了,如果調(diào)用以下方法,結(jié)果會如注釋顯示。

public static void main(String[] args){
    Student student = new Student();
    student.age = 10;
    student.name = "dove";
    changeName(student);
    System.out.println(student.name);//dove_2
    changeName(student.name);
    System.out.println(student.name);//dove_2
    changeName2(student.name); 
    System.out.println(student.name);//dove_2         
}      
static void changeName(Student paramStudent){
    paramStudent.name = "dove_2";
}
static void changeName(String paramName){
    paramName = "dove_3";
}
static void changeName2(String paramName){
    paramName += "233";
}

changeName2(String paramName)此處講道理被調(diào)用后應(yīng)該是"dove_2233",因?yàn)?b>String是一個引用類型,也就是說此處的parmaName應(yīng)該是指向和傳入的參數(shù)指向了相同的一個地址塊,然后對指向的內(nèi)存進(jìn)行了修改,然而結(jié)果并不是,原因就在于String是一個不可變的類型(為啥不可變呢,具體可以看String類的實(shí)現(xiàn),它是一個final class,并且其內(nèi)部正真保存著字符串的value[]也是不可變的(final),所以意味著修改Sting是不可能的)。

2.腦洞猜想可能情況

所以猜測上述的changeName2過程類似于

    FuckString fuckString = new FuckString();//paramName
    FuckString fuckString2 = new FuckString(fuckString);//構(gòu)造出的新的值
    fuckString = fuckString2;//把paramName指向構(gòu)造出的新值

然后,這就有點(diǎn)想不通了,不可變的類型,String 的 + 是怎么弄的呢?打個斷點(diǎn)試試看,強(qiáng)制進(jìn)入,發(fā)現(xiàn)跳轉(zhuǎn)到了StringBuilder的構(gòu)造方法里,這說明應(yīng)該是構(gòu)造了一個新的StringBuilder對象。

?

同時,底部的Debug里拋出了個錯誤,說是無法獲取StringBuilder.toString(),也就進(jìn)一步證明此處有新的String的產(chǎn)生。
?
到這里基本上就驗(yàn)證了我的猜想,String +會產(chǎn)生一個新的String對象,既然這樣,反編譯下,看下字節(jié)碼,估計基本就搞定這個懵逼的問題了。

3.字節(jié)碼驗(yàn)證

于是就寫了以下的類,用來驗(yàn)證:

public class Main {
    public static void main(String[] args){
        String s = "dove";
        s += "233";
    }
}

然后javac,然后javap -c,看字節(jié)碼,如下圖。


??

嘗試著解釋下該部分代碼(不是很看的懂字節(jié)碼,所以有些解釋可能不是很規(guī)范,不過講道理大概意思不會差很遠(yuǎn))

String s = "dove";部分字節(jié)碼及解釋

       0: ldc           #2                  // String dove
       2: astore_1

第0行,將一個常量加載到操作數(shù)棧,也就是把“dove”這玩意,放進(jìn)了操作數(shù)棧(也不知道是什么東西,蛤蛤)
第2行,將一號數(shù)值(下劃線1代表一號,大概理解,不是很準(zhǔn)確)從操作數(shù)棧存儲到局部變量表,說白了就是把“dove”給存了起來?

s += "233";部分字節(jié)碼及解釋

       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."":()V
      10: aload_1    
      11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      14: ldc           #6                  // String 233
      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

第3行,這個很明顯,不google也知道,new StringBuilder(),也就是搞了個StringBUilder的實(shí)例。
第6行,Java虛擬機(jī)提供了一些用于直接操作操作數(shù)棧,不是很懂,貌似對整體理解影響不大,先過。
第7行,invokespecial 調(diào)用一些需要特殊處理的實(shí)例方法,包括實(shí)例初始化方法、私有方法和父類方法,此處應(yīng)該是在初始化StringBuilder對象。
第10行,將1號局部變量(下劃線1指代一號變量)加載到操作棧,這里應(yīng)該是指“dove”
第11行,調(diào)用對象的實(shí)例方法,此處就是調(diào)用StringBuilder.append,也就是把“dove”加到了StringBuilder
第14行,將一個常量加載到操作數(shù)棧,就是把“233”載入
第16行,調(diào)用對象的實(shí)例方法,此處就是調(diào)用StringBuilder.append,把“233”給加到“StringBuilder”中
第19行,調(diào)用對象的實(shí)例方法,此處就是調(diào)用StringBuilder.toString,而該方法,會觸發(fā)new String()的操作,因此,會返還一個新的String對象

4.最終結(jié)論:

從腦洞斷點(diǎn)以及最后的字節(jié)碼分析可以看出,s +="233",會導(dǎo)致一個新的String對象生成,也就是說,調(diào)用changeName2(String paramName)會使得paramName指向一個新的String對象,這樣就意味著,對該數(shù)據(jù)的改變并不會影響本身student.name的值,由此,String懵逼的問題也解決了。

以上,就是整個關(guān)于Java引用傳遞和值傳遞的理解,有說的不對的,望指正。

發(fā)現(xiàn)上次寫的時候忘了圖片,現(xiàn)在加上。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/64995.html

相關(guān)文章

  • JS淬煉: Primitive vs. Object

    摘要:值傳遞引用傳遞是值傳遞,是引用傳遞。但這影響會根據(jù)父類是屬于還是而有微妙差別。我們設(shè)想有一個父類,和兩個繼承了他的子類和。這時,子類修改該不會影響到父類本身,更不會傳遞到其他子類上。 Javascript有兩種基本數(shù)據(jù)類型,Primitive和Object。Object是properties的聚合,其property可以是Object也可以是Primitive。Primitive只有v...

    Hancock_Xu 評論0 收藏0
  • [Interview] Pass-by-value vs. Pass-by Reference

    Java is pass-by-value. Pass by value: make a copy in memory of the actual parameters value that is passed in. Pass by reference: pass a copy of the address of the actual parameter. This code will no...

    Lowky 評論0 收藏0
  • 搞定PHP面試 - 深入了解引用

    摘要:引用可以被看作是文件系統(tǒng)中的硬鏈接。如果具有引用的數(shù)組被復(fù)制,其值不會解除引用。如果試圖這樣從函數(shù)返回引用,將會報錯,因?yàn)楹瘮?shù)在試圖返回一個表達(dá)式的結(jié)果而不是一個引用的變量。這并不意味著變量內(nèi)容被銷毀了。 1. 什么是引用 在 PHP 中引用是指用不同的名字訪問同一個變量內(nèi)容。PHP 中的變量名和變量內(nèi)容是不一樣的, 因此同樣的內(nèi)容可以有不同的名字。最接近的比喻是 Unix 的文件名和...

    fox_soyoung 評論0 收藏0
  • JavaScript 是傳值調(diào)用還是傳引用調(diào)用?

    摘要:所以傳遞給函數(shù)的值是這個值,所以函數(shù)執(zhí)行結(jié)束原始變量并不會改變。傳值調(diào)用在傳值調(diào)用中,傳遞給函數(shù)參數(shù)是函數(shù)被調(diào)用時所傳實(shí)參的拷貝。引用類型變量的值是一個指針,指向堆內(nèi)存中的實(shí)際對象。所以傳共享調(diào)用也可以說是傳值調(diào)用。 1. 例子 先來看兩個個來自于 《JavaScript 高級程序設(shè)計》P70-P71 的兩個例子。 1.1. 基本類型參數(shù)傳遞 function addTen(num) ...

    darkbug 評論0 收藏0
  • 剖析Laravel隊(duì)列系統(tǒng)--準(zhǔn)備隊(duì)列作業(yè)

    摘要:原文鏈接我們推送到隊(duì)列的每個作業(yè)都存儲在按執(zhí)行順序排序的某些存儲空間中,該存儲位置可以是數(shù)據(jù)庫,存儲或像這樣的第三方服務(wù)。這個數(shù)字從開始,在每次運(yùn)行作業(yè)時不斷增加。 原文鏈接https://divinglaravel.com/queue-system/preparing-jobs-for-queue Every job we push to queue is stored in som...

    marek 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<