Czy Java przekazuje odwołanie, czy przekazuje wartość?

Wiele języków programowania umożliwia przekazywanie parametrów przez odniesienie lub wartość . W Javie możemy przekazywać parametry tylko według wartości . To nakłada pewne ograniczenia i rodzi pytania. Na przykład, jeśli wartość parametru zostanie zmieniona w metodzie, co stanie się z wartością po wykonaniu metody? Możesz się także zastanawiać, jak Java zarządza wartościami obiektów na stercie pamięci. Ten program Java Challenger pomaga rozwiązać te i inne typowe pytania dotyczące odwołań do obiektów w języku Java.

Pobierz kod źródłowy

Uzyskaj kod dla tego programu Java Challenger. Możesz przeprowadzić własne testy, postępując zgodnie z przykładami.

Odwołania do obiektów są przekazywane według wartości

Wszystkie odwołania do obiektów w Javie są przekazywane według wartości. Oznacza to, że kopia wartości zostanie przekazana do metody. Ale sztuczka polega na tym, że przekazanie kopii wartości zmienia również rzeczywistą wartość obiektu. Aby zrozumieć, dlaczego, zacznij od tego przykładu:

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

Jak myślisz, co simpson.namebędzie po wykonaniu transformIntoHomermetody?

W tym przypadku będzie to Homer! Powodem jest to, że zmienne obiektów Java są po prostu odwołaniami wskazującymi na rzeczywiste obiekty w stercie pamięci. Dlatego nawet jeśli Java przekazuje parametry do metod według wartości, jeśli zmienna wskazuje na odniesienie do obiektu, rzeczywisty obiekt również zostanie zmieniony.

Jeśli nadal nie wiesz, jak to działa, spójrz na poniższy rysunek.

Rafael Chinelato Del Nero

Czy typy pierwotne są przekazywane przez wartość?

Podobnie jak typy obiektów, typy pierwotne są również przekazywane przez wartość. Czy możesz wywnioskować, co stanie się z typami pierwotnymi w poniższym przykładzie kodu?

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

Jeśli ustaliłeś, że wartość zmieni się na 30, masz rację. Jest 30, ponieważ (znowu) Java przekazuje parametry obiektu według wartości. Liczba 30 to tylko kopia wartości, a nie rzeczywista wartość. Typy prymitywne są przydzielane w pamięci stosu, więc zmieniana jest tylko wartość lokalna. W tym przypadku nie ma odniesienia do obiektu.

Przekazywanie niezmiennych odwołań do obiektów

A co by było, gdybyśmy zrobili ten sam test z niezmiennym Stringobiektem?

JDK zawiera wiele niezmiennych klas. Przykłady obejmują rodzaju opakowania są Integer, Double, Float, Long, Boolean, BigDecimal, i oczywiście dobrze znane Stringklasy.

W następnym przykładzie zwróć uwagę, co się stanie, gdy zmienimy wartość 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"; } } 

Jak myślisz, jaki będzie wynik? Jeśli zgadłeś „”, to gratulacje! Dzieje się tak, ponieważ Stringobiekt jest niezmienny, co oznacza, że ​​pola wewnątrz Stringsą ostateczne i nie można ich zmienić.

Uczynienie Stringklasy niezmienną daje nam lepszą kontrolę nad jednym z najczęściej używanych obiektów Javy. Gdyby Stringmożna zmienić wartość a , spowodowałoby to wiele błędów. Zwróć również uwagę, że nie zmieniamy atrybutu Stringklasy; zamiast tego po prostu przypisujemy mu nową Stringwartość. W tym przypadku wartość „Homer” zostanie przekazana namew changeToHomermetodzie. String„Homer” będą mogły być zbierane śmieci jak tylko changeToHomermetoda zakończy wykonywanie. Mimo że obiektu nie można zmienić, zmienna lokalna będzie.

Struny i nie tylko

Dowiedz się więcej o Stringklasie Java i nie tylko: Zobacz wszystkie posty Rafaela z serii Java Challengers.

Przekazywanie zmiennych odwołań do obiektów

W przeciwieństwie do tego Stringwiększość obiektów w JDK jest zmienna, podobnie jak StringBuilderklasa. Poniższy przykład jest podobny do poprzedniego, ale zawiera StringBuilderraczej niż 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"); } } 

Czy możesz wywnioskować wynik dla tego przykładu? W tym przypadku, ponieważ pracujemy ze zmiennym obiektem, wynikiem będzie „Homer Simpson”. Tego samego zachowania można się spodziewać po każdym innym zmiennym obiekcie w Javie.

Wiesz już, że zmienne Java są przekazywane według wartości, co oznacza, że ​​przekazywana jest kopia wartości. Pamiętaj tylko, że skopiowana wartość wskazuje na rzeczywisty obiekt w stercie pamięci Java. Przekazywanie wartości nadal zmienia wartość rzeczywistego obiektu.

Podejmij wyzwanie związane z odniesieniami do obiektów!

W tym programie Java Challenger sprawdzimy, czego nauczyłeś się na temat odwołań do obiektów. W poniższym przykładzie kodu zobaczysz niezmienną Stringi zmienną StringBuilderklasę. Każdy jest przekazywany jako parametr do metody. Wiedząc, że Java przekazuje tylko wartość, jak myślisz, jakie będą dane wyjściowe po wykonaniu głównej metody z tej klasy?

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

Oto opcje, sprawdź na końcu tego artykułu klucz odpowiedzi.

O : Warrior = null Weapon = null

B : Wojownik = Smok Broń = Smok

C : Wojownik = Dragon Knight Weapon = Dragon Sword

D : Wojownik = Smok Rycerz Broń = Miecz

Co się właśnie stało?

Pierwszym parametrem w powyższym przykładzie jest warriorProfessionzmienna, która jest obiektem modyfikowalnym. Drugi parametr, broń, jest niezmienny 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.
  • Możesz być zaskoczony, gdy dowiesz się, że prymitywne typy Javy są kontrowersyjne. W tej funkcji John I. Moore przedstawia argumenty za ich zatrzymaniem i nauczeniem się, jak z nich dobrze korzystać.
  • Rozwijaj swoje umiejętności programowania w języku Java w Java Dev Gym.
  • Jeśli spodobało Ci się debugowanie dziedziczenia Java, obejrzyj więcej filmów na playliście wideo Rafael's Java Challenges (filmy z tej serii nie są powiązane z JavaWorld).
  • Chcesz pracować nad projektami bezstresowymi i pisać kod wolny od błędów? Udaj się do NoBugsProject po swoją kopię No Bugs, No Stress - Twórz zmieniające życie oprogramowanie bez niszczenia swojego życia .

Ta historia: „Czy Java przekazuje przez odniesienie czy przekazuje wartość?” został pierwotnie opublikowany przez JavaWorld.