Предава ли 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стойност. В този случай стойността “Homer” ще бъде предадена 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"); } } 

Можете ли да изведете резултата за този пример? В този случай, тъй като работим с изменяем обект, изходът ще бъде „Homer 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 : Warrior = null Оръжие = null

Б : Воин = Драконово оръжие = Дракон

В : Воин = Оръжие на рицар-дракон = Драконов меч

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, разгледайте още видеоклипове в плейлиста за видео предизвикателства на Java на Rafael (видеоклипове от тази поредица не са свързани с JavaWorld).
  • Искате ли да работите по проекти без стрес и да пишете код без грешки? Насочете се към NoBugsProject за вашето копие на No Bugs, No Stress - Създайте променящ живота софтуер, без да унищожавате живота си .

Тази история, "Предава ли Java препратка или предава стойност?" е първоначално публикуван от JavaWorld.