Gaat Java door verwijzing of door waarde?

In veel programmeertalen kunnen parameters worden doorgegeven via referentie of waarde . In Java kunnen we parameters alleen op waarde doorgeven . Dit legt enkele grenzen op en roept ook vragen op. Als de parameterwaarde bijvoorbeeld in de methode wordt gewijzigd, wat gebeurt er dan met de waarde na uitvoering van de methode? U kunt zich ook afvragen hoe Java objectwaarden in de geheugenheap beheert. Deze Java Challenger helpt u bij het oplossen van deze en andere veelgestelde vragen over objectreferenties in Java.

Haal de broncode op

Verkrijg de code voor deze Java Challenger. U kunt uw eigen tests uitvoeren terwijl u de voorbeelden volgt.

Objectreferenties worden op waarde doorgegeven

Alle objectreferenties in Java worden op waarde doorgegeven. Dit betekent dat een kopie van de waarde wordt doorgegeven aan een methode. Maar de truc is dat het doorgeven van een kopie van de waarde ook de werkelijke waarde van het object verandert. Begin met dit voorbeeld om te begrijpen waarom:

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

Wat denk je dat het simpson.namezal zijn nadat de transformIntoHomermethode is uitgevoerd?

In dit geval is het Homer! De reden hiervoor is dat Java-objectvariabelen eenvoudigweg verwijzingen zijn die verwijzen naar echte objecten in de geheugenheap. Daarom, ook al geeft Java parameters door aan methoden op waarde, als de variabele naar een objectverwijzing verwijst, zal het echte object ook worden gewijzigd.

Als je nog steeds niet helemaal duidelijk bent hoe dit werkt, bekijk dan de onderstaande afbeelding.

Rafael Chinelato Del Nero

Worden primitieve typen doorgegeven op waarde?

Net als objecttypen worden ook primitieve typen op waarde doorgegeven. Kun je afleiden wat er met de primitieve typen zal gebeuren in het volgende codevoorbeeld?

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

Als je hebt vastgesteld dat de waarde zou veranderen in 30, heb je gelijk. Het is 30 omdat (alweer) Java objectparameters op waarde doorgeeft. Het getal 30 is slechts een kopie van de waarde, niet de echte waarde. Primitieve typen worden toegewezen in het stapelgeheugen, dus alleen de lokale waarde wordt gewijzigd. In dit geval is er geen objectreferentie.

Doorgeven van onveranderlijke objectreferenties

Wat als we dezelfde test zouden doen met een onveranderlijk Stringobject?

De JDK bevat veel onveranderlijke klassen. Voorbeelden hiervan zijn de types wrapper Integer, Double, Float, Long, Boolean, BigDecimal, en natuurlijk de zeer bekende Stringklasse.

Kijk in het volgende voorbeeld wat er gebeurt als we de waarde van a veranderen 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"; } } 

Wat denk je dat de output zal zijn? Als je "" geraden hebt, dan gefeliciteerd! Dat gebeurt omdat een Stringobject onveranderlijk is, wat betekent dat de velden binnen de Stringdefinitief zijn en niet kunnen worden gewijzigd.

Door de Stringklasse onveranderlijk te maken, krijgen we een betere controle over een van Java's meest gebruikte objecten. Als de waarde van a Stringzou kunnen worden gewijzigd, zou dit veel bugs veroorzaken. Merk ook op dat we een attribuut van de Stringklasse niet wijzigen ; in plaats daarvan kennen we er gewoon een nieuwe Stringwaarde aan toe. In dit geval wordt de waarde "Homer" doorgegeven namein de changeToHomermethode. De String"Homer" komt in aanmerking voor garbagecollection zodra de changeToHomermethode is voltooid. Hoewel het object niet kan worden gewijzigd, is de lokale variabele dat wel.

Snaren en meer

Meer informatie over Java's Stringklas en meer: ​​bekijk alle berichten van Rafael in de Java Challengers-serie.

Veranderlijke objectreferenties doorgeven

In tegenstelling tot de Stringmeeste objecten in de JDK zijn veranderlijk, net als de StringBuilderklasse. Het onderstaande voorbeeld is vergelijkbaar met het vorige, maar bevat StringBuildermeer dan 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"); } } 

Kun je de output voor dit voorbeeld afleiden? In dit geval, omdat we werken met een veranderlijk object, is de uitvoer "Homer Simpson". Je zou hetzelfde gedrag kunnen verwachten van elk ander veranderlijk object in Java.

U hebt al geleerd dat Java-variabelen op waarde worden doorgegeven, wat betekent dat een kopie van de waarde wordt doorgegeven. Onthoud gewoon dat de gekopieerde waarde verwijst naar een echt object in de Java-geheugenheap. Het doorgeven van waarde verandert nog steeds de waarde van het echte object.

Ga de uitdaging van objectreferenties aan!

In deze Java Challenger testen we wat je hebt geleerd over objectreferenties. In het onderstaande codevoorbeeld ziet u de onveranderlijke Stringen de veranderlijke StringBuilderklasse. Elk wordt als parameter aan een methode doorgegeven. Wetende dat Java alleen waarde doorgeeft, wat denkt u dat de uitvoer zal zijn zodra de hoofdmethode van deze klasse is uitgevoerd?

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

Hier zijn de opties, kijk aan het einde van dit artikel voor de antwoordsleutel.

A : Warrior = null Wapen = null

B : Warrior = Dragon Weapon = Dragon

C : Warrior = Dragon Knight Weapon = Dragon Sword

D : Warrior = Dragon Knight Wapen = Zwaard

Wat is er net gebeurd?

De eerste parameter in het bovenstaande voorbeeld is de warriorProfessionvariabele, die een veranderlijk object is. De tweede parameter, wapen, is een onveranderlijke 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.
  • Het zal je misschien verbazen te horen dat Java's primitieve typen controversieel zijn. In deze functie pleit John I. Moore ervoor om ze te houden en ze goed te leren gebruiken.
  • Blijf uw Java-programmeervaardigheden ontwikkelen in de Java Dev Gym.
  • Als je het opsporen van fouten in Java-overerving leuk vond, bekijk dan meer video's in Rafael's Java Challenges video-afspeellijst (video's in deze serie zijn niet gelieerd aan JavaWorld).
  • Wil je aan stressvrije projecten werken en foutvrije code schrijven? Ga naar NoBugsProject voor uw exemplaar van No Bugs, No Stress - Creëer levensveranderende software zonder uw leven te vernietigen .

Dit verhaal: "Gaat Java voorbij aan verwijzing of aan waarde?" is oorspronkelijk gepubliceerd door JavaWorld.