Går Java med referens eller passerar värde?

Många programmeringsspråk tillåter överföring av parametrar genom referens eller värde . I Java kan vi bara skicka parametrar efter värde . Detta ställer vissa gränser och väcker också frågor. Till exempel, om parametervärdet ändras i metoden, vad händer med värdet efter metodkörning? Du kanske också undrar hur Java hanterar objektvärden i minneshögen. Denna Java Challenger hjälper dig att lösa dessa och andra vanliga frågor om objektreferenser i Java.

Hämta källkoden

Hämta koden för denna Java Challenger. Du kan köra dina egna tester medan du följer exemplen.

Objektreferenser skickas efter värde

Alla objektreferenser i Java skickas efter värde. Detta innebär att en kopia av värdet skickas till en metod. Men tricket är att att skicka en kopia av värdet också ändrar objektets verkliga värde. För att förstå varför, börja med det här exemplet:

 public class ObjectReferenceExample { public static void main(String... doYourBest) { Simpson simpson = new Simpson(); transformIntoHomer(simpson); System.out.println(simpson.name); } static void transformIntoHomer(Simpson simpson) { simpson.name = "Homer"; } } class Simpson { String name; } 

Vad tror du simpson.namekommer att vara efter att transformIntoHomermetoden har körts?

I det här fallet blir det Homer! Anledningen är att Java-objektvariabler helt enkelt är referenser som pekar på riktiga objekt i minneshög. Därför ändras även det verkliga objektet, även om Java skickar parametrar till metoder efter värde, om variabeln pekar på en objektreferens.

Om du fortfarande inte är helt klar över hur detta fungerar, ta en titt på figuren nedan.

Rafael Chinelato Del Nero

Passeras primitiva typer av värde?

Liksom objekttyper skickas också primitiva typer av värde. Kan du härleda vad som händer med de primitiva typerna i följande kodexempel?

 public class PrimitiveByValueExample { public static void main(String... primitiveByValue) { int homerAge = 30; changeHomerAge(homerAge); System.out.println(homerAge); } static void changeHomerAge(int homerAge) { homerAge = 35; } } 

Om du bestämde att värdet skulle ändras till 30, har du rätt. Det är 30 eftersom (igen) Java skickar objektparametrar efter värde. Siffran 30 är bara en kopia av värdet, inte det verkliga värdet. Primitiva typer tilldelas i stackminnet, så endast det lokala värdet kommer att ändras. I det här fallet finns det ingen objektreferens.

Passerar oföränderliga objektreferenser

Vad händer om vi gjorde samma test med ett oföränderligt Stringobjekt?

JDK innehåller många oföränderliga klasser. Exempel innefattar omslagstyper Integer, Double, Float, Long, Boolean, BigDecimal, och naturligtvis mycket välkänd Stringklass.

Lägg märke till vad som händer när vi ändrar värdet på a i nästa exempel String.

 public class StringValueChange { public static void main(String... doYourBest) { String name = ""; changeToHomer(name); System.out.println(name); } static void changeToHomer(String name) { name = "Homer"; } } 

Vad tror du att produktionen blir? Om du gissade “” så grattis! Det händer för att ett Stringobjekt är oföränderligt, vilket innebär att fälten inuti Stringär slutgiltiga och inte kan ändras.

Att göra Stringklassen oföränderlig ger oss bättre kontroll över ett av Java mest använda objekt. Om värdet på a Stringkunde ändras skulle det skapa många buggar. Observera också att vi inte ändrar ett attribut för Stringklassen; istället tilldelar vi det helt enkelt ett nytt Stringvärde. I detta fall överförs "Homer" -värdet till namei changeToHomermetoden. Den String”Homer” kommer att vara berättigade att skräp samlas så snart changeToHomermetoden slutför utförande. Även om objektet inte kan ändras kommer den lokala variabeln att vara.

Strängar och mer

Lär dig mer om Javas Stringklass och mer: Se alla Rafaels inlägg i Java Challengers-serien.

Passerar muterbara objektreferenser

Till skillnad från String, de flesta objekt i JDK är mutabla, som StringBuilderklassen. Exemplet nedan liknar det föregående, men har StringBuildersnarare än String:

 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"); } } 

Kan du dra slutsatsen för detta exempel? I det här fallet, eftersom vi arbetar med ett föränderligt objekt, kommer utmatningen att vara "Homer Simpson." Du kan förvänta dig samma beteende från alla andra föränderliga objekt i Java.

Du har redan lärt dig att Java-variabler skickas efter värde, vilket innebär att en kopia av värdet skickas. Kom bara ihåg att det kopierade värdet pekar på ett riktigt objekt i Java-minneshögen. Att passera med värde ändrar fortfarande värdet på det verkliga objektet.

Ta objektreferensutmaningen!

I den här Java Challenger testar vi vad du har lärt dig om objektreferenser. I kodexemplet nedan ser du den oföränderliga Stringoch den muterbara StringBuilderklassen. Varje skickas som en parameter till en metod. Att veta att Java bara passerar med värde, vad tror du kommer att vara resultatet när huvudmetoden från den här klassen har körts?

 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; } } 

Här är alternativen, kontrollera svarstangenten i slutet av den här artikeln.

A : Krigare = null Vapen = null

B : Warrior = Dragon Weapon = Dragon

C : Warrior = Dragon Knight Weapon = Dragon Sword

D : Warrior = Dragon Knight Weapon = Sword

Vad har just hänt?

Den första parametern i exemplet ovan är warriorProfessionvariabeln, som är ett föränderligt objekt. Den andra parametern, vapen, är en oföränderlig String:

 static void changeWarriorClass(StringBuilder warriorProfession, String weapon) { ... } 

Now let’s analyze what is happening inside this method. At the first line of this method, we append the Knight value to the warriorProfession variable. Remember that warriorProfession is a mutable object; therefore the real object will be changed, and the value from it will be “Dragon Knight.”

 warriorProfession.append("Knight"); 

In the second instruction, the immutable local String variable will be changed to “Dragon Sword.” The real object will never be changed, however, since String is immutable and its attributes are final:

 weapon = "Dragon " + weapon; 

Finally, we pass null to the variables here, but not to the objects. The objects will remain the same as long as they are still accessible externally--in this case through the main method. And, although the local variables will be null, nothing will happen to the objects:

 weapon = null; warriorProfession = null; 

From all of this we can conclude that the final values from our mutable StringBuilder and immutable String will be:

 System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon); 

The only value that changed in the changeWarriorClass method was warriorProfession, because it’s a mutable StringBuilder object. Note that warriorWeapon did not change because it’s an immutable String object.

The correct output from our Challenger code would be:

D: Warrior=Dragon Knight Weapon=Sword.

Video challenge! Debugging object references in Java

Debugging is one of the easiest ways to fully absorb programming concepts while also improving your code. In this video you can follow along while I debug and explain object references in Java.

Common mistakes with object references

  • Trying to change an immutable value by reference.
  • Trying to change a primitive variable by reference.
  • Expecting the real object won't change when you change a mutable object parameter in a method.

What to remember about object references

  • Java always passes parameter variables by value.
  • Object variables in Java always point to the real object in the memory heap.
  • A mutable object’s value can be changed when it is passed to a method.
  • An immutable object’s value cannot be changed, even if it is passed a new value.
  • “Passing by value” refers to passing a copy of the value.
  • “Passing by reference” refers to passing the real reference of the variable in memory.

Learn more about Java

  • Get more quick code tips: Read all of Rafael's posts in the JavaWorld Java Challengers series.
  • Learn more about mutable and immutable Java objects (such as String and StringBuffer) and how to use them in your code.
  • Du kanske blir förvånad över att Java: s primitiva typer är kontroversiella. I den här funktionen gör John I. Moore argument för att behålla dem och lära sig att använda dem väl.
  • Fortsätt bygga dina Java-programmeringskunskaper i Java Dev Gym.
  • Om du gillade felsökning av Java-arv, kolla in fler videor i Rafaels Java-utmaningar-videospellista (videor i denna serie är inte anslutna till JavaWorld).
  • Vill du arbeta med stressfria projekt och skriva felfri kod? Gå över till NoBugsProject för din kopia av No Bugs, No Stress - Skapa livsförändrande programvara utan att förstöra ditt liv .

Denna berättelse, "Går Java genom referens eller skickas med värde?" publicerades ursprungligen av JavaWorld.