Java是按引用傳遞還是按值傳遞?

Java是按引用傳遞還是按值傳遞?


許多編程語言都允許按引用或按值傳遞參數。在Java中,我們只能按value傳遞參數。這施加了一些限制,並且引起了疑問。例如,如果在方法中更改了參數值,方法執行後該值會怎樣?您可能還想知道Java如何管理內存堆中的對象值。該Java Challenger可幫助您解決有關Java中對象引用的這些以及其他常見問題。

獲取源代碼

在遵循示例的同時,您可以運行自己的測試。源碼地址: https://github.com/rafadelnero/javaworld-challengers

對象引用按值傳遞

Java中的所有對象引用均按值傳遞。這意味著該值的副本將傳遞給方法。但是訣竅在於傳遞值的副本也會更改對象的實際值。要了解原因,請從以下示例開始:

<code>public class ObjectReferenceExample {
\tpublic static void main(String... doYourBest) { \t Simpson simpson = new Simpson(); \t transformIntoHomer(simpson); \t System.out.println(simpson.name);\t}
\tstatic void transformIntoHomer(Simpson simpson) { \t simpson.name = "Homer";\t}}class Simpson {\tString name;}/<code>

您認為執行simpson.name該transformIntoHomer方法後會怎樣?

在這種情況下,它將是荷馬!原因是Java對象變量只是指向內存堆中實際對象的引用。因此,即使Java通過值將參數傳遞給方法,但是如果變量指向對象引用,則實際對象也將被更改。

如果您仍然不太清楚它是如何工作的,請看下圖。

Java是按引用傳遞還是按值傳遞?

拉斐爾·奇內拉託·德爾尼羅

原始類型是否按值傳遞?

像對象類型一樣,基本類型也按值傳遞。您能否在下面的代碼示例中推斷出原始類型會發生什麼?

<code>public class PrimitiveByValueExample {
\tpublic static void main(String... primitiveByValue) { \t int homerAge = 30; \t changeHomerAge(homerAge); \t System.out.println(homerAge);\t}
\tstatic void changeHomerAge(int homerAge) { \t homerAge = 35;\t}}/<code>

如果您確定該值將更改為30,那麼您是正確的。這是30,因為(再次)Java通過值傳遞對象參數。數字30只是值的副本,而不是實際值。基本類型在堆棧存儲器中分配,因此僅本地值將被更改。在這種情況下,沒有對象引用。


傳遞不可變的對象引用

如果我們對不可變的String對象進行了相同的測試該怎麼辦?

JDK包含許多不可變的類。實例包括包裝類型Integer,Double,Float,Long,Boolean,BigDecimal,和當然的非常公知的String類。

在下一個示例中,請注意當我們更改a的值時會發生什麼String。

<code>public class StringValueChange {
\tpublic static void main(String... doYourBest) { \t String name = ""; \t changeToHomer(name); \t System.out.println(name);\t}
\tstatic void changeToHomer(String name) { \t name = "Homer";\t}}/<code>

您認為輸出結果是什麼?如果您猜到了“”,那就恭喜!發生這種情況是因為String對象是不可變的,這意味著內的字段String是最終字段,無法更改。

使String類不可變使我們可以更好地控制Java最常用的對象之一。如果a的值String可以更改,則會產生很多錯誤。還要注意,我們沒有更改String類的屬性;相反,我們只是String為其分配了一個新值。在這種情況下,“荷馬”值將被傳遞到name在changeToHomer方法。該方法完成執行後,String“本壘打”將有資格被垃圾回收changeToHomer。即使無法更改對象,局部變量也會更改。


傳遞可變對象引用

與不同String,JDK中的大多數對象都是可變的,例如StringBuilder類。下面的示例與上一個示例相似,但是功能StringBuilder而不是String:

<code> static class MutableObjectReference {    public static void main(String... mutableObjectExample) {      StringBuilder name = new StringBuilder("Homer ");      addSureName(name);      System.out.println(name);    }
static void addSureName(StringBuilder name) { name.append("Simpson"); } } /<code>

您能否推斷出此示例的輸出?在這種情況下,因為我們正在使用可變對象,所以輸出將為“ Homer Simpson”。您可能期望Java中的任何其他可變對象具有相同的行為。

您已經瞭解到Java變量是通過值傳遞的,這意味著將傳遞值的副本。只要記住複製的值指向Java內存堆中的真實對象即可。按值傳遞仍然會更改實際對象的值。

接受對象引用挑戰!

在此Java Challenger中,我們將測試您從對象引用中學到的知識。在下面的代碼示例中,您將看到不可變String和可變的StringBuilder類。每個參數都作為參數傳遞給方法。知道Java僅按值傳遞,一旦執行了此類的main方法,您認為輸出是什麼?

<code>public class DragonWarriorReferenceChallenger {
public static void main(String... doYourBest) { StringBuilder warriorProfession = new StringBuilder("Dragon "); String warriorWeapon = "Sword "; changeWarriorClass(warriorProfession, warriorWeapon);
System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon); }
static void changeWarriorClass(StringBuilder warriorProfession, String weapon) { warriorProfession.append("Knight"); weapon = "Dragon " + weapon;
weapon = null; warriorProfession = null; }}/<code>

這些是選項,請在本文結尾處查看答案。

A:戰士=空武器=空B:戰士=龍武器=龍C:戰士=龍騎士武器=龍劍D:戰士=龍騎士武器=劍

發生什麼事了

上面示例中的第一個參數是warriorProfession變量,它是一個可變對象。第二個參數,武器是不可變的String:

<code> static void changeWarriorClass(StringBuilder warriorProfession, String weapon) {    ...  }/<code>

現在,讓我們分析一下該方法內部發生的情況。在此方法的第一行,我們將Knight值附加到warriorProfession變量。請記住,這warriorProfession是一個可變的對象;因此實際對象將被更改,並且其值將為“ Dragon Knight”。

<code>warriorProfession.append("Knight");/<code>

在第二條指令中,不變的局部String變量將更改為“ Dragon Sword”。但是,由於實物String是不可變的並且其屬性是最終的,因此實物永遠不會更改:

<code>weapon = "Dragon " + weapon;/<code>

最後,我們傳遞null給這裡的變量,而不傳遞給對象。只要它們仍然可以從外部訪問,這些對象將保持不變-在這種情況下,可以通過main方法訪問。而且,儘管局部變量將為null,但對象將不會發生任何事情:

<code>weapon = null;warriorProfession = null;/<code>

從所有這些我們可以得出結論,我們可變StringBuilder且不變的最終值String將是:

<code>System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon);/<code>

changeWarriorClass方法中唯一更改的值是warriorProfession,因為它是可變StringBuilder對象。請注意,它warriorWeapon沒有改變,因為它是一個不可變的String對象。

我們的挑戰者代碼的正確輸出為:

D:戰士=龍騎士武器=劍。

視頻挑戰!調試Java中的對象引用

調試是完全吸收編程概念並改善代碼的最簡單方法之一。在本視頻中,您可以在調試和解釋Java對象引用時進行後續操作。

視頻地址:https://youtu.be/HXvKLn5RkRQ


對象引用的常見錯誤

  • 嘗試通過引用更改不可變值。
  • 嘗試通過引用更改原始變量。
  • 當您在方法中更改可變對象參數時,期望真實對象不會更改。

關於對象引用要記住什麼

  • Java總是按值傳遞參數變量。
  • Java中的對象變量始終指向內存堆中的實際對象。
  • 可變對象的值傳遞給方法時可以更改。
  • 不可變對象的值即使傳遞了新值也無法更改。
  • “按值傳遞”是指傳遞值的副本。
  • “通過引用傳遞”是指在內存中傳遞變量的真實引用。


Java是按引用傳遞還是按值傳遞?


分享到:


相關文章: