Питання Чи є Java "прохідним посиланням" або "прохідним значенням"?


Я завжди вважав, що Java була прохідний довідник.

Однак я бачив кілька публікацій в блозі (наприклад, цей блог), які стверджують, що це не так.

Я не думаю, що я розумію різницю, яку вони роблять.

Що таке пояснення?


5495


походження


Я вважаю, що більша частина плутанини з цього питання пов'язана з тим фактом, що різні люди мають різні визначення терміна "посилання". Люди, що надходять з фон С ++, припускають, що "довідка" повинна означати, що це означає в C ++, люди з C-фону вважають, що "посилання" повинні бути такими ж, як "покажчик" на їхній мові тощо. Чи правильно сказати, що Java проходить за посиланням, дійсно залежить від того, що має на увазі під "посиланням". - Gravity
Я намагаюся послідовно використовувати термінологію, знайдену на Стратегія оцінки стаття Слід зазначити, що, хоча в статті вказується, що умови дуже відрізняються від спільноти, це підкреслює, що семантика для call-by-value і call-by-reference відрізняються дуже важливим способом. (Особисто я волію використовувати call-by-object-sharing ці дні call-by-value[-of-the-reference], оскільки це описує семантику на високому рівні і не створює конфлікту з call-by-value, котрий є основна реалізація.)
@ Gravity: Ви можете піти і поставити свій коментар на ВЕЛИЧЕЙ рекламний щит або щось? Це все питання в двох словах. І це показує, що все це семантика. Якщо ми не погоджуємося з базовим визначенням посилання, то ми не погодимось на відповідь на це питання :) - MadConan
Я думаю, що плутанина - "пройти за посиланням" і "еталонною семантикою". Java - пропускна ціна з посиланням семантики. - spraff
@ Тяжкості, поки ти абсолютно правильно в тому, що люди, що надходять з C ++, інстинктивно інтуїтивно налаштують інтуїцію щодо терміна "reference", я особисто вважають, що тіло більше поховане в "by". "Прохід" смутне тим, що він абсолютно відрізняється від "Проходження" в Java. У C ++, однак це розмовно не так. У C ++ ви можете сказати "проходження посилання", і це є зрозуміло, що він пройде swap(x,y) тест.


Відповіді:


Java завжди є прохідний за вартістю. На жаль, вони вирішили назвати розташування об'єкта "посиланням". Коли ми передаємо значення об'єкта, ми пропускаємо його довідка до цього Це незрозуміло для початківців.

Це виглядає так:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false 
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

У наведеному вище прикладі aDog.getName() все одно повернеться "Max". Значення aDog в межах main не змінено в функції foo з Dog  "Fifi" оскільки посилання на об'єкт передано значенням. Якщо це було передано за посиланням, то aDog.getName() в main повернеться "Fifi" після дзвінка до foo.

Аналогічно:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

У наведеному вище прикладі Fifi це ім'я собаки після дзвінка до foo(aDog) тому що ім'я об'єкта було встановлено всередині foo(...). Будь-які операції, які foo виконує d такі, що для всіх практичних цілей вони виконуються aDog сама (крім коли d змінюється, щоб вказувати на інше Dog наприклад, як d = new Dog("Boxer"))


4741



Хіба це не трохи заплутує проблему з внутрішніми деталями? Немає принципової різниці між "передачею посилання" та "передачею значення посилання", якщо ви хочете сказати "значення внутрішнього покажчика на об'єкт". - izb
Але є виняткова різниця. Подивіться на перший приклад. Якщо це було просто передано за посиланням, aDog.name буде "Fifi". Це не так, що посилання, яке ви отримуєте, є посиланням на значення, яке, якщо перезаписуватись, буде відновлено при виході з функції. - erlando
@ Лоренцо: Ні, у Java все передається за значенням. Примітиви передаються за значенням, а посилання на об'єкт передаються за значенням. Самі об'єкти ніколи не передаються методу, але об'єкти завжди перебувають у купі, і лише посилання на об'єкт передано методу. - Esko Luontola
Моя спроба хорошого способу візуалізувати проходження об'єкта: уявіть собі повітряну кулю. Виклик fxn схожий на прив'язку другої рядка до кулі та передача рядка fxn. parameter = new Balloon () буде розрізати цю стрічку і створити нову кульку (але це не впливає на оригінальний куль). Параметр.pop () все одно поставить його хоча б тому, що він випливає з рядка до того самого, оригінального куля. Java проходить за значенням, але передане значення не є глибоким, це найвищий рівень, тобто примітив або покажчик. Не плутати це з глибоким прохідним значенням, коли об'єкт повністю клонований і переданий. - dhackner
Що заплутує те, що посилання на об'єкти є фактично вказівниками. На початку САН називав їх покажчиками. Тоді маркетинг поінформував, що «покажчик» був поганим словом. Але ви все ще бачите "правильну" номенклатуру в NullPointerException. - Prof. Falken


Я тільки помітив, що ви посилалися моя стаття.

Техніка Java заявляє, що все в Java - прохідне значення. У Java не існує такої речі, як "pass-by-reference".

Ключ до розуміння цього полягає в тому, що щось на кшталт

Dog myDog;

є ні собака; це насправді є покажчик до собаки.

Що це означає, коли є

Dog myDog = new Dog("Rover");
foo(myDog);

ти по суті пройдеш адреса із створеного Dog об'єкт до foo метод

(Я кажу по суті, оскільки покажчики Java - це не прямі адреси, але найлегше їх думати про це)

Припустимо, що Dog об'єкт знаходиться на адресі пам'яті 42. Це означає, що ми передаємо 42 методу.

якщо метод визначений як

public void foo(Dog someDog) {
    someDog.setName("Max");     // AAA
    someDog = new Dog("Fifi");  // BBB
    someDog.setName("Rowlf");   // CCC
}

давайте подивимося, що відбувається.

  • параметр someDog встановлено значення 42
  • в рядку "AAA"
    • someDog дотримується Dog це вказує на (the Dog об'єкт за адресою 42)
    • це Dog (на адресу 42) попросили змінити своє ім'я на Макс
  • в рядку "BBB"
    • новий Dog створюється Скажімо, він знаходиться за адресою 74
    • ми призначаємо параметр someDog до 74
  • в рядку "CCC"
    • Деякі допи слідують до Dog це вказує на (the Dog об'єкт за адресою 74)
    • це Dog (на адресу 74) пропонується змінити своє ім'я на Rowlf
  • Тоді ми повернемося

Тепер давайте подумаємо про те, що відбувається поза методом:

Зробив myDog змінити?

Там ключ.

Маючи це на увазі myDog це покажчик, а не фактичний Dog, відповідь НІ. myDog як і раніше має значення 42; він все ще вказує на оригінал Dog(але зауважте, що з-за лінії "AAA", його ім'я зараз "Макс" - все-таки та сама собака; myDogзначення не змінилося.)

Це ідеально підходить для слідуйте адресу та зміну того, що в кінці його; однак не змінює змінну.

Java працює точно як C. Ви можете призначити покажчик, перенести вказівник на метод, дотримуватися покажчика в методі та змінити дані, на які вказано. Тим не менш, ви не можете змінити місце, де цей покажчик показує.

У C ++, Ada, Pascal та інших мовах, які підтримують проміжні посилання, ви можете фактично змінити передану змінну.

Якщо Java мала семантику пропуску за типом, то foo Метод, який ми визначили вище, змінитиме де myDog вказував, коли він призначений someDog онлайн BBB.

Подумайте про еталонні параметри як про псевдоніми для змінної, що передається. При призначенні цього псевдоніма така змінна, яку було передано.


2639



Ось чому загальний рефрен "Java не має вказівників" настільки оманливим. - Beska
Ви помиляєтесь, імхо. "Зважаючи на те, що myDog є покажчиком, а не фактичною Собакою, відповідь НІ. MyDog як і раніше має значення 42, він все ще вказує на оригінальний Собак". myDog має значення 42, але аргумент його назви тепер містить "Макс", а не "Rover" на лінії // AAA. - Özgür
Подумайте про це таким чином. Хтось має адресу Енн Арбор, MI (моє рідне місто, GO BLUE!) На пропущеній папері під назвою "annArborLocation". Ви копіюєте його на папері під назвою "моє призначення". Ви можете їхати до "моємустану" і посадити дерево. Можливо, ви змінили щось про місто в цьому місці, але воно не змінює LAT / LON, написане на будь-якому паперовому носії. Ви можете змінити LAT / LON на "myDestination", але це не змінює "annArborLocation". Чи допомагає це? - Scott Stanchfield
@ Шотт Стенчфілд: Я прочитав вашу статтю приблизно рік тому, і це дійсно допомогло зрозуміти щось для мене. Дякую! Можу я смиренно підказати трохи додатково: слід зазначити, що насправді є певний термін, який описує цю форму "виклик за значенням, де цінність є довідником", яку винайшов Барбара Лісков для опису стратегії оцінки своєї мови CLU у 1974 р., Щоб уникнути плутанини, таку як ця стаття адресована: дзвоніть, поділившись (іноді називають дзвінок шляхом обміну об'єктами або просто дзвонити за об'єктом), що в значній мірі ідеально описує семантику. - Jörg W Mittag
@Gevorg - мови C / C ++ не володіють поняттям "покажчик". Є й інші мови, які використовують покажчики, але не дозволяють використовувати ті ж типи маніпуляцій з покажчиками, які дозволяють C / C ++. Java має покажчики; вони просто захищені від зла. - Scott Stanchfield


Java завжди передає аргументи за значенням NOT за посиланням.


Дозвольте мені пояснити це через приклад:

public class Main{
     public static void main(String[] args){
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }
     public static void changeReference(Foo a){
          Foo b = new Foo("b");
          a = b;
     }
     public static void modifyReference(Foo c){
          c.setAttribute("c");
     }
}

Я поясню це поетапно:

  1. Декларування посилання на ім'я f типу Foo і призначити його новому об'єкту типу Foo з атрибутом "f".

    Foo f = new Foo("f");
    

    enter image description here

  2. З боку методу, посилання на тип Foo з ім'ям a оголошено, і спочатку йому призначено null.

    public static void changeReference(Foo a)
    

    enter image description here

  3. Як ви називаєте метод changeReference, посилання a буде призначено об'єкту, який буде переданий як аргумент.

    changeReference(f);
    

    enter image description here

  4. Декларування посилання на ім'я b типу Foo і призначити його новому об'єкту типу Foo з атрибутом "b".

    Foo b = new Foo("b");
    

    enter image description here

  5. a = b повторно призначає посилання a НЕ f до об'єкта, чий атрибут є "b".

    enter image description here


  6. Як ви телефонуєте modifyReference(Foo c) метод, довідник c створюється і призначається об'єкту з атрибутом "f".

    enter image description here

  7. c.setAttribute("c"); змінить атрибут об'єкта, на який посилається c вказує на це, і це той самий об'єкт, на який посилається f вказує на це.

    enter image description here

Я сподіваюсь, що ви тепер розумієте, як протікають об'єкти як аргументи в Java :)


1392



+1 приємні речі. хороші діаграми. Я також знайшов приємну лаконічну сторінку тут adp-gmbh.ch/php/pass_by_reference.html Добре, я визнаю, що це написано на PHP, але це принцип розуміння різниці, яку я вважаю важливою (і як маніпулювати цією різницею вашими потребами). - DaveM
@ Eng.Fouad Це хороше пояснення, але якщо a вказує на той самий об'єкт, що й f (і ніколи не отримує власної копії об'єкта f вказує на), будь-які зміни в об'єкті, створені за допомогою a слід змінити f а також (оскільки вони обидва працюють з одним і тим же об'єктом), так що в якийсь момент a повинен отримати свою власну скопіювати об'єкта f вказує на. - Mr D
@MrD, коли "a" вказує на той самий об'єкт "f" також вказує, тоді будь-яка зміна, зроблена на цей об'єкт за допомогою "a", також може бути помітна через "f", АЛЕ НЕ ЗМІНА "f". 'f' як і раніше вказує на один і той же об'єкт. Ви можете повністю змінити об'єкт, але ви ніколи не зможете змінити те, що 'f' вказує. Це основне питання, яке чомусь деякі люди просто не можуть зрозуміти. - Mike Braun
Це найкраще пояснення, яке я знайшов. До речі, як щодо базової ситуації? Наприклад, аргумент повинен мати тип int, він все ще передає копію int змінної до аргументу? - allenwang
Діаграма була дуже корисною - Ivan Kaloyanov


Це дасть вам певні уявлення про те, як Java дійсно працює, тому що в наступній дискусії про проходження Java за посиланням або проходження за значенням ви просто посміхнетеся :-)

Крок перший, будь ласка, вилучіть з розуму це слово, яке починається з "p" _ _ _ _ _ _ _ _ ", особливо якщо ви прийшли з інших мов програмування. Java і 'p' не можуть бути написані в тій же книзі, форумі або навіть txt.

На другому етапі пам'ятайте, що коли ви передаєте об'єкт в метод, ви передаєте посилання на об'єкт, а не сам об'єкт.

  • Студентка: Майстер, це означає, що Java є довідником про проходження?
  • Магістр: Коник, немає

Тепер подумайте, що посилання / змінна об'єкта є / є:

  1. Перемінна містить біти, які повідомляють JVM, як перейти до згаданого об'єкта в пам'яті (Heap).
  2. При передачі аргументів до методу ви НЕ передаєте еталонну змінну, але копію бітів у еталонній змінній. Щось подібне: 3bad086a. 3bad086a - це спосіб дістатися до переданого об'єкта.
  3. Отже, ви просто передаєте 3bad086a, що це значення посилання.
  4. Ви передаєте значення посилання, а не сама посилання (а не об'єкт).
  5. Це значення насправді КОПІЮВАНА та дано методу.

У наступному (не намагайтеся скомпілювати / виконати це ...):

1. Person person;
2. person = new Person("Tom");
3. changeName(person);
4.
5. //I didn't use Person person below as an argument to be nice
6. static void changeName(Person anotherReferenceToTheSamePersonObject) {
7.     anotherReferenceToTheSamePersonObject.setName("Jerry");
8. }

Що сталося?

  • Перемінна людина створюється в рядку №1, і на початку він нульовий.
  • Створено новий об'єкт Person у рядку № 2, що зберігається в пам'яті, і змінну людина дається посилання на об'єкт Person. Тобто, його адреса. Скажімо, 3bad086a.
  • Перемінна людина тримаючи адресу об'єкта, передається до функції у рядку №3.
  • У рядку № 4 ви можете слухати звук мовчання
  • Перевірте коментар на рядку №5
  • Метод локальної змінної -anotherReferenceToTheSamePersonObject- створюється, а потім відбувається магія у рядку № 6:
    • Змінна / посилання людина копіюється біт-на-біт і передається до anotherReferenceToTheSamePersonObject всередині функції
    • Немає нових випадків створення особи.
    • Обидва "людина"і"anotherReferenceToTheSamePersonObject"тримайте те саме значення 3bad086a.
    • Не намагайтеся цього, але людина == anotherReferenceToTheSamePersonObject буде правдою.
    • Обидві змінні мають ідентичні копії посилання, і вони обидва відносяться до одного об'єкта Person, SAME Object on the Heap і NOT A COPY.

Картина варта тисячі слів:

Pass by Value

Зауважте, що стрілки anotherReferenceToTheSamePersonObject спрямовані до об'єкта, а не до змінної людини!

Якщо ви цього не зрозуміли, то просто довіряйте мені і пам'ятайте, що краще це сказати Java проходить за значенням. Добре, пройти за контрольним значенням. О добре, ще краще пропуск за копією змінної вартості! ;)

Тепер не соромтеся ненавидіти мене, але зауважте, що з огляду на це немає відмінностей між передачею примітивних типів даних і об'єктів коли говорять про метод аргументів.

Ви завжди передаєте копію бітів значення посилання!

  • Якщо це примітивний тип даних, ці біти містять значення самого типу первісних даних.
  • Якщо це об'єкт, біти будуть містити значення адреси, яка повідомляє JVM, як дістатися до об'єкта.

Java є прохідною величиною, оскільки в межах методу ви можете змінити об'єкт, на який посилається, скільки завгодно, але незалежно від того, наскільки важко ви спробуєте, ви ніколи не зможете змінити передану змінну, яка буде тримати посилання (а не p _ _ _ _ _ _ _) той самий об'єкт незалежно від того, що!


Функція changeName, описана вище, ніколи не зможе змінити фактичний вміст (бітові значення) пройденої довідки. Іншими словами, changeName не може змусити особу звернутися до іншого об'єкта.


Звичайно, ви можете скоротити його коротко, і просто скажете це Java є прохідною цінністю!


651



Ви маєте на увазі покажчики? .. Якщо я правильно це отримає, то в public void foo(Car car){ ... }, car є місцевим для foo і він містить купу розташування об'єкта? Так що, якщо я змінюся carзначення російською мовою car = new Car(), це вкаже на різні об'єкти на купі? і якщо я змінюся carімунітет власності на car.Color = "Red", Об'єкт у купі, на який вказує car буде змінено Крім того, це в C # так само? Будь-ласка дайте відповідь! Дякую! - dpp
@domanokz Ти вбиваєш мене, будь ласка, не кажи цього слова знову! ;) Зауважте, що я міг би відповісти на це питання, не кажучи вже про "посилання". Це термінологічний випадок, і "p" роблять це гірше. На жаль, мені та Шотту є різні погляди на це. Я думаю, що ви зрозуміли, як це працює в Java, тепер ви можете називати його пропуском по суті вартості, спільного обміну об'єктами, побічним копіювання змінної вартості або не соромтеся придумати щось інше! Мені дуже не подобається, поки ви отримаєте, як це працює, і що в змінній типу Object: просто адресу Поштова скринька! ;) - Gevorg
Схоже, ви просто дав посилання? Я збираюсь захищати той факт, що Java все ще є копіюваною мовою оригіналу. Той факт, що це копійоване посилання, не змінює термінологію. Обидва НОМЕРИ все ще вказують на той самий об'єкт. Це аргумент пуристів ... - John Strickler
Я думаю, Java розроблена з вказівниками на увазі. Інакше, чому NullPointerException існує? Тим не менш, для цього чудового пояснення, отримання покажчиків просто ускладнить це - brain storm
Так що впадіть на теоретичну лінію № 9 System.out.println(person.getName()); що з'явиться? "Том" або "Джеррі"? Це останнє, що допоможе мені перешкодити цій плутанині. - TheBrenny


Java завжди проходить за значенням, без винятків коли-небудь.

Отже, як це може бути зовсім незрозумілим для кожного, і вважаю, що Java проходить за посиланням, або думаю, що вони мають приклад Java, що діє як прохід за посиланням? Головне - Java ніколи забезпечує прямий доступ до значень предмети самі, в будь-який обставини Єдиний доступ до об'єктів - через довідка до цього об'єкту. Оскільки об'єкти Java є завжди доступ за допомогою посилання, а не безпосередньо, звичайно говорити про поля та змінні і аргументи методу як бути об'єкти, коли педантично вони тільки посилання на об'єкти. Плутанина випливає з цього (строго кажучи, неправильна) зміна номенклатури.

Отже, при виклику методу

  • Для примітивних аргументів (int, long, і т. д.), прохід за значенням є фактичне значення примітиву (наприклад, 3).
  • Для об'єктів прохід за значенням є значенням посилання на об'єкт.

Так що, якщо у вас є doSomething(foo) і public void doSomething(Foo foo) { .. } два копії Foos посилання що вказують на ті ж об'єкти.

Природно, що проходження за значенням посилання на об'єкт виглядає дуже схожим (і практично не відрізняється) від передачі об'єкта за посиланням.


561



Оскільки значення примітивів незмінними (наприклад, String), різниця між цими двома випадками не є дійсно актуальною. - Paŭlo Ebermann
Точно Для всіх, що ви можете розповісти через спостережувану поведінку JVM, примітиви можуть бути передані за посиланням і можуть жити на купі. Вони цього не роблять, але це ніяк не можна спостерігати. - Gravity
примітиви незмінні? це нове в Java 7? - Carlos Heuberger
@ CarlosHeuberger, "примітиви незмінні?". Справа в тому, що ви можете робити вигляд що "примітиви" насправді (змінні) посилання до незмінних об'єктів. - Aaron McDaid
Покажчики незмінні, примітиви взагалі мінливі. Рядок також не є примітивним, це об'єкт. Крім того, основна структура String є змінним масивом. Єдина незмінна річ про це - це довжина, яка є притаманною природі масивів. - kingfrito_5005


Java передає посилання за значенням.

Таким чином, ви не можете змінити посилання, на яке потрапляє.


279



Але те, що постійно повторюється "ви не можете змінити значення об'єктів, що передаються в аргументах", явно помилково. Можливо, ви не зможете зробити їх посиланнями на інший об'єкт, але ви можете змінити їх вміст, натискаючи їхні методи. ІМО це означає, що ви втрачаєте всі переваги посилань і не отримуєте додаткових гарантій. - Timmmm
Я ніколи не сказав: "Ви не можете змінити значення об'єктів, переданих в аргументах". Я скажу: "Ви не можете змінити значення посилання об'єкта, переданого як аргумент методу", що є справжнім твердженням про мову Java. Очевидно, ви можете змінити стан об'єкта (якщо воно не є незмінним). - ScArcher2
Пам'ятайте, що ви не можете передавати об'єкти в java; об'єкти залишаються на купі. Покажчики до об'єктів можуть бути передані (які копіюються в рамку стека для викликуваного методу). Таким чином, ви ніколи не змінюєте передане значення (покажчик), але ви можете вільно стежити за ним і змінювати речі на купі, на яку вона вказує. Це прохідне значення. - Scott Stanchfield
Схоже, ви просто дав посилання? Я збираюсь захищати той факт, що Java все ще є копіюваною мовою оригіналу. Той факт, що це копійоване посилання, не змінює термінологію. Обидва НОМЕРИ все ще вказують на той самий об'єкт. Це аргумент пуристів ... - John Strickler
Java не передає об'єкт, він передає значення покажчика на об'єкт. Це створює новий покажчик на розташування пам'яті вихідного об'єкта в новій змінній. Якщо в методі змінюється значення (адреса пам'яті, на яку вона вказує) цієї змінної покажчика, то оригінальний покажчик, який використовується в caller-методі, залишається незміненим. Якщо ви викликаєте параметр посиланням, то це той факт, що це а скопіювати оригінальної довідки, а не сама оригінальна довідка, так що тепер є дві посилання на об'єкт, що означає, що це прохідне значення - theferrit32


Я відчуваю, що сперечатися про "прохідний референт проти прохідного значення" не є надзвичайно корисним.

Якщо ви скажете: "Ява є прохідною за будь-яким (посиланням / значенням)", в будь-якому випадку ви не надасте повну відповідь. Ось додаткова інформація, яка, сподіваюсь, допоможе зрозуміти, що відбувається в пам'яті.

Збій курс на стек / купу, перш ніж ми отримаємо реалізацію Java: Цінності йдуть і виходять зі столу в гарному порядку, як набір тарілок в кафетерії. Пам'ять в купі (також відома як динамічна пам'ять) є випадковою і дезорганізованою. JVM просто знаходить простір там, де це можливо, і звільняє його, оскільки змінні, які використовують його, більше не потрібні.

Гаразд. По-перше, місцеві примітиви переходять у стек. Отже, цей код:

int x = 3;
float y = 101.1f;
boolean amIAwesome = true;

призводить до цього:

primitives on the stack

Коли ви оголошувати та створювати екземпляр об'єкта. Фактичний об'єкт йде на купу. Що відбувається в стекі? Адреса об'єкта на купі. Програмісти C ++ називають це покажчиком, але деякі розробники Java виступають проти слова «покажчик». Що б не було. Просто знайте, що адреса об'єкта йде на стек.

Подібно до цього:

int problems = 99;
String name = "Jay-Z";

a b*7ch aint one!

Масив є об'єктом, тому він також йде на купу. А як щодо об'єктів в масиві? Вони отримують власний простір, а адреса кожного об'єкта йде всередині масиву.

JButton[] marxBros = new JButton[3];
marxBros[0] = new JButton("Groucho");
marxBros[1] = new JButton("Zeppo");
marxBros[2] = new JButton("Harpo");

marx brothers

Отже, що проходить під час виклику методу? Якщо ви передаєте в об'єкт, то, що ви насправді передаєте, це адреса об'єкта. Деякі можуть сказати "значення" адреси, а деякі кажуть, що це лише посилання на об'єкт. Це генезис священної війни між прихильниками "посилання" та "цінність". Те, що ви називаєте, не настільки важливе, як ви розумієте, що те, що проходить, - це адреса до об'єкту.

private static void shout(String name){
    System.out.println("There goes " + name + "!");
}

public static void main(String[] args){
    String hisName = "John J. Jingleheimerschmitz";
    String myName = hisName;
    shout(myName);
}

Один рядок створюється і простір для нього виділяється в купі, а адреса до рядка зберігається в стекі і дається ідентифікатор hisName, оскільки адреса другого рядка така ж, як і перша, не створюється нова Стрічка і не виділяється жодне нове місце кучі, але стек створює новий ідентифікатор. Тоді ми називаємо shout(): створений новий фрейм стека та новий ідентифікатор, name створюється і призначається адреса вже існуючої String.

la da di da da da da

Отже, значення, посилання? Ви говорите "картопля".


198



Тим не менш, ви повинні прослідкувати за більш складним прикладом, коли функція, як видається, змінює зміну, на адресу якої вона має посилання. - Brian Peterson
Люди не "танцюють навколо справжнього питання" стеків проти купи, тому що це ні справжнє питання. У найкращому випадку, це деталі реалізації, а в гіршому - неправильно. (Цілком можливо, що об'єкти живуть у стекі, google "escape analysis". І величезна кількість об'єктів містить примітиви, які, ймовірно, ні живете на стекі.) Справжнє питання точно різниця між типовими посиланнями та типами вартості, зокрема, що значення змінної еталонного типу є довідником, а не об'єктом, на який він посилається. - cHao
Це "деталізація виконання", тому що Java ніколи не вимагає, щоб фактично показати вам, де об'єкт живе в пам'яті, і насправді, здається, визнається уникати просочившись цю інформацію. Це може поставити об'єкт на стек, і ви ніколи не знаєте. Якщо ви дбаєте, ви зосереджуєтеся на неправильній справі - і в цьому випадку це означає ігнорувати справжню проблему. - cHao
І в будь-якому випадку, "примітиви йдуть в стек" невірно. Примітивний локальні змінні йти на стек (Якщо вони, звичайно, не були оптимізовані.) Але тоді, так і місцеві змінні посилання. І первісні члени, визначені в об'єкті, живуть скрізь, де живе об'єкт. - cHao
Погодьтеся з коментарями тут. Стек / куча є сторонньою проблемою, а не релевантною. Деякі змінні можуть бути в стеку, деякі - в статичній пам'яті (статичні змінні), а велика кількість живих на купі (всі змінні члена об'єкта). NONE цих змінних можна передавати за посиланням: з викличного методу НІКОЛИ не можна змінювати значення змінної, яка передана як аргумент. Отже, в Java не існує проміжок посилання. - fishinear


Щоб показати контраст, порівняйте наступне C ++ і Java фрагменти:

У C ++: Примітка. Поганий код - витік пам'яті!  Але це демонструє точку зору.

void cppMethod(int val, int &ref, Dog obj, Dog &objRef, Dog *objPtr, Dog *&objPtrRef)
{
    val = 7; // Modifies the copy
    ref = 7; // Modifies the original variable
    obj.SetName("obj"); // Modifies the copy of Dog passed
    objRef.SetName("objRef"); // Modifies the original Dog passed
    objPtr->SetName("objPtr"); // Modifies the original Dog pointed to 
                               // by the copy of the pointer passed.
    objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                   // leaving the original object alone.
    objPtrRef->SetName("objRefPtr"); // Modifies the original Dog pointed to 
                                    // by the original pointer passed. 
    objPtrRef = new Dog("newObjPtrRef"); // Modifies the original pointer passed
}

int main()
{
    int a = 0;
    int b = 0;
    Dog d0 = Dog("d0");
    Dog d1 = Dog("d1");
    Dog *d2 = new Dog("d2");
    Dog *d3 = new Dog("d3");
    cppMethod(a, b, d0, d1, d2, d3);
    // a is still set to 0
    // b is now set to 7
    // d0 still have name "d0"
    // d1 now has name "objRef"
    // d2 now has name "objPtr"
    // d3 now has name "newObjPtrRef"
}

У Java

public static void javaMethod(int val, Dog objPtr)
{
   val = 7; // Modifies the copy
   objPtr.SetName("objPtr") // Modifies the original Dog pointed to 
                            // by the copy of the pointer passed.
   objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                  // leaving the original object alone.
}

public static void main()
{
    int a = 0;
    Dog d0 = new Dog("d0");
    javaMethod(a, d0);
    // a is still set to 0
    // d0 now has name "objPtr"
}

Java має лише два типи проходження: за значенням для вбудованих типів, а також за значенням покажчика для типів об'єктів.


161



+1, я би також додав Dog **objPtrPtr до прикладу C ++ таким чином ми можемо змінити те, що покажчик "вказує". - Amro
Це одна з найкращих відповідей, яку я бачив до цих пір. В основному він уникає несуттєвого аргументу семантики "посилання на вартість посилання", присутнього в іншому місці, а замість цього обговорює механіку того, що насправді відбувається. Мені довелося подати більшість інших "прохідних" відповідей -1 через їх суперечливий або фактично невірний вміст, але цей отримує +1. - Aaron


Java передає посилання на об'єкти за значенням.


145



Так просто, як це! - Ivan Kaloyanov