Java passe-t-il par référence ou passe-t-il par valeur?

De nombreux langages de programmation permettent de passer des paramètres par référence ou par valeur . En Java, nous ne pouvons passer des paramètres que par valeur . Cela impose des limites et soulève également des questions. Par exemple, si la valeur du paramètre est modifiée dans la méthode, qu'advient-il de la valeur après l'exécution de la méthode? Vous pouvez également vous demander comment Java gère les valeurs des objets dans le tas de mémoire. Ce Java Challenger vous aide à résoudre ces questions et d'autres courantes sur les références d'objet en Java.

Obtenez le code source

Obtenez le code de ce Java Challenger. Vous pouvez exécuter vos propres tests tout en suivant les exemples.

Les références d'objet sont passées par valeur

Toutes les références d'objet en Java sont passées par valeur. Cela signifie qu'une copie de la valeur sera transmise à une méthode. Mais l'astuce est que passer une copie de la valeur change également la valeur réelle de l'objet. Pour comprendre pourquoi, commencez par cet exemple:

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

Que pensez-vous du simpson.nametestament après l' transformIntoHomerexécution de la méthode?

Dans ce cas, ce sera Homer! La raison en est que les variables d'objet Java sont simplement des références qui pointent vers des objets réels dans le tas de mémoire. Par conséquent, même si Java transmet les paramètres aux méthodes par valeur, si la variable pointe vers une référence d'objet, l'objet réel sera également modifié.

Si vous ne savez toujours pas comment cela fonctionne, regardez la figure ci-dessous.

Rafael Chinelato Del Nero

Les types primitifs sont-ils passés par valeur?

Comme les types d'objets, les types primitifs sont également passés par valeur. Pouvez-vous déduire ce qui arrivera aux types primitifs dans l'exemple de code suivant?

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

Si vous avez déterminé que la valeur passerait à 30, vous avez raison. C'est 30 parce que (encore une fois) Java passe les paramètres d'objet par valeur. Le nombre 30 est juste une copie de la valeur, pas la valeur réelle. Les types primitifs sont alloués dans la mémoire de la pile, donc seule la valeur locale sera modifiée. Dans ce cas, il n'y a pas de référence d'objet.

Passer des références d'objets immuables

Et si nous faisions le même test avec un Stringobjet immuable ?

Le JDK contient de nombreuses classes immuables. Les exemples incluent les types d'enveloppe Integer, Double, Float, Long, Boolean, BigDecimal, et bien sûr le très bien connu Stringclasse.

Dans l'exemple suivant, remarquez ce qui se passe lorsque nous modifions la valeur de a 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"; } } 

Selon vous, quel sera le résultat? Si vous avez deviné «», félicitations! Cela se produit parce qu'un Stringobjet est immuable, ce qui signifie que les champs à l'intérieur du Stringsont définitifs et ne peuvent pas être modifiés.

Rendre la Stringclasse immuable nous donne un meilleur contrôle sur l'un des objets les plus utilisés de Java. Si la valeur de a Stringpouvait être modifiée, cela créerait beaucoup de bogues. Notez également que nous ne changeons pas un attribut de la Stringclasse; au lieu de cela, nous lui attribuons simplement une nouvelle Stringvaleur. Dans ce cas, la valeur «Homer» sera transmise namedans la changeToHomerméthode. Le String«Homer» sera éligible pour être ramassé dès que la changeToHomerméthode aura terminé son exécution. Même si l'objet ne peut pas être modifié, la variable locale le sera.

Cordes et plus

En savoir plus sur la Stringclasse de Java et plus: voir tous les articles de Rafael dans la série Java Challengers.

Passer des références d'objets mutables

Contrairement à String, la plupart des objets du JDK sont modifiables, comme la StringBuilderclasse. L'exemple ci-dessous est similaire au précédent, mais présente des fonctionnalités StringBuilderplutôt que 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"); } } 

Pouvez-vous déduire la sortie de cet exemple? Dans ce cas, comme nous travaillons avec un objet mutable, la sortie sera «Homer Simpson». Vous pouvez vous attendre au même comportement de tout autre objet mutable en Java.

Vous avez déjà appris que les variables Java sont passées par valeur, ce qui signifie qu'une copie de la valeur est transmise. N'oubliez pas que la valeur copiée pointe vers un objet réel dans le tas de mémoire Java. Passer par valeur modifie toujours la valeur de l'objet réel.

Relevez le défi des références d'objets!

Dans ce Java Challenger, nous testerons ce que vous avez appris sur les références d'objets. Dans l'exemple de code ci-dessous, vous voyez les classes immuable Stringet mutable StringBuilder. Chacun est passé en tant que paramètre à une méthode. Sachant que Java ne passe que par valeur, quelle sera selon vous la sortie une fois la méthode principale de cette classe exécutée?

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

Voici les options, consultez la fin de cet article pour la clé de réponse.

A : Guerrier = null Arme = null

B : Guerrier = Dragon Arme = Dragon

C : guerrier = arme de chevalier dragon = épée de dragon

D : guerrier = arme de chevalier dragon = épée

Qu'est-ce qui vient de se passer?

Le premier paramètre de l'exemple ci-dessus est la warriorProfessionvariable, qui est un objet mutable. Le deuxième paramètre, l'arme, est un immuable 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.
  • Vous pourriez être surpris d'apprendre que les types primitifs de Java sont controversés. Dans cette fonctionnalité, John I. Moore plaide pour les conserver et apprendre à bien les utiliser.
  • Continuez à développer vos compétences en programmation Java dans Java Dev Gym.
  • Si vous avez aimé déboguer l'héritage Java, regardez plus de vidéos dans la liste de lecture vidéo Java Challenges de Rafael (les vidéos de cette série ne sont pas affiliées à JavaWorld).
  • Vous voulez travailler sur des projets sans stress et écrire du code sans bogue? Rendez-vous sur le NoBugsProject pour votre copie de No Bugs, No Stress - Créez un logiciel qui change la vie sans détruire votre vie .

Cette histoire, "Java passe-t-il par référence ou passe-t-il par valeur?" a été initialement publié par JavaWorld.