Передает ли Java по ссылке или по значению?

Многие языки программирования позволяют передавать параметры по ссылке или по значению . В Java мы можем передавать параметры только по значению . Это накладывает некоторые ограничения, а также вызывает вопросы. Например, если в методе изменяется значение параметра, что происходит со значением после выполнения метода? Вы также можете задаться вопросом, как Java управляет значениями объектов в куче памяти. Этот Java Challenger поможет вам решить эти и другие распространенные вопросы о ссылках на объекты в Java.

Получите исходный код

Получите код для этого Java Challenger. Вы можете запускать свои собственные тесты, следуя примерам.

Ссылки на объекты передаются по значению

Все ссылки на объекты в Java передаются по значению. Это означает, что копия значения будет передана методу. Но хитрость в том, что передача копии значения также изменяет реальное значение объекта. Чтобы понять почему, начните с этого примера:

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

Как вы думаете, что simpson.nameбудет после выполнения transformIntoHomerметода?

В этом случае это будет Гомер! Причина в том, что объектные переменные Java - это просто ссылки, указывающие на реальные объекты в куче памяти. Следовательно, даже если Java передает параметры методам по значению, если переменная указывает на ссылку на объект, реальный объект также будет изменен.

Если вам все еще не совсем понятно, как это работает, взгляните на рисунок ниже.

Рафаэль Чинелато дель Неро

Примитивные типы передаются по значению?

Как и типы объектов, примитивные типы также передаются по значению. Можете ли вы сделать вывод, что произойдет с примитивными типами в следующем примере кода?

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

Если вы определили, что значение изменится на 30, вы правы. Это 30, потому что (опять же) Java передает параметры объекта по значению. Число 30 - это просто копия значения, а не реальное значение. Примитивные типы размещаются в стековой памяти, поэтому будет изменено только локальное значение. В этом случае ссылка на объект отсутствует.

Передача неизменяемых ссылок на объекты

Что, если бы мы провели тот же тест с неизменяемым Stringобъектом?

JDK содержит множество неизменяемых классов. Примеры включают типы обертки Integer, Double, Float, Long, Boolean, BigDecimal, и, конечно, очень хорошо известный Stringкласс.

В следующем примере обратите внимание на то, что происходит, когда мы меняем значение 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"; } } 

Как вы думаете, какой будет результат? Если вы угадали «», поздравляю! Это происходит потому, что Stringобъект неизменен, а это означает, что поля внутри Stringявляются окончательными и не могут быть изменены.

Создание Stringнеизменяемого класса дает нам лучший контроль над одним из наиболее часто используемых объектов Java. Если значение a Stringможно изменить, это создаст много ошибок. Также обратите внимание, что мы не меняем атрибут Stringкласса; вместо этого мы просто присваиваем ему новое Stringзначение. В этом случае nameв changeToHomerметод будет передано значение «Гомер» . String«Гомер» будет иметь право быть мусора , как только changeToHomerметод завершает выполнение. Несмотря на то, что объект нельзя изменить, локальная переменная будет.

Струны и многое другое

Узнайте больше о Stringклассе Java и многом другом: см. Все сообщения Рафаэля в серии Java Challengers.

Передача ссылок на изменяемые объекты

В отличие от этого String, большинство объектов в JDK являются изменяемыми, как и StringBuilderкласс. Приведенный ниже пример аналогичен предыдущему, но имеет , StringBuilderа не 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"); } } 

Можете ли вы сделать вывод для этого примера? В этом случае, поскольку мы работаем с изменяемым объектом, на выходе будет «Гомер Симпсон». Вы можете ожидать такого же поведения от любого другого изменяемого объекта в Java.

Вы уже узнали, что переменные Java передаются по значению, что означает, что передается копия значения. Просто помните, что скопированное значение указывает на реальный объект в куче памяти Java. Передача по значению по-прежнему изменяет значение реального объекта.

Примите вызов ссылки на объекты!

В этом Java Challenger мы проверим, что вы узнали о ссылках на объекты. В приведенном ниже примере кода вы видите неизменяемый Stringи изменяемый StringBuilderклассы. Каждый из них передается как параметр методу. Зная, что Java передает только значение, как вы думаете, что будет на выходе после выполнения основного метода этого класса?

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

Вот варианты, проверьте конец этой статьи для ключа ответа.

A : Воин = null Оружие = null

B : Воин = Оружие Дракона = Дракон

C : Воин = Оружие Рыцаря Дракона = Меч Дракона

D : Воин = Рыцарь Дракона Оружие = Меч

Что только что произошло?

Первым параметром в приведенном выше примере является warriorProfessionпеременная, которая является изменяемым объектом. Второй параметр, оружие, является неизменным 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.
  • Вы можете быть удивлены, узнав, что примитивные типы Java противоречивы. В этой статье Джон И. Мур приводит доводы в пользу их сохранения и умения правильно ими пользоваться.
  • Продолжайте развивать свои навыки программирования на Java в Java Dev Gym.
  • Если вам понравилась отладка наследования Java, посмотрите другие видео в плейлисте Rafael's Java Challenges (видео из этой серии не связаны с JavaWorld).
  • Хотите работать над проектами без стресса и писать код без ошибок? Перейдите в проект NoBugsProject, чтобы получить копию книги « Нет ошибок, нет стресса - создавайте программы, изменяющие жизнь, не разрушая вашу жизнь» .

Эта история «Передает ли Java по ссылке или по значению?» изначально был опубликован JavaWorld.