Является ли Java «передачей по ссылке» или «передачей по значению»?

Я всегда думал, что Java была по ссылке .

Тем не менее, я видел пару постов в блоге (например, этот блог ), которые утверждают, что это не так.

Я не думаю, что понимаю разницу, которую они проводят.

Какое объяснение?

2.09.2008 20:14:29
Я считаю, что большая путаница в этом вопросе связана с тем, что разные люди имеют разные определения термина «ссылка». Люди из C ++ полагают, что «ссылка» должна означать то же, что и в C ++, люди из C полагают, что «ссылка» должна быть такой же, как «указатель» в их языке, и так далее. Правильно ли говорить, что Java проходит по ссылке, на самом деле зависит от того, что подразумевается под «ссылкой».
Gravity 30.07.2011 07:23:05
Я стараюсь последовательно использовать терминологию, найденную в статье « Стратегия оценки» . Следует отметить, что, хотя в статье указывается, что термины сильно различаются в зависимости от сообщества, в ней подчеркивается, что семантика для call-by-valueи call-by-referenceотличается очень важным образом . (Лично я предпочитаю использовать call-by-object-sharingэти дни более call-by-value[-of-the-reference], так как это описывает семантику на высоком уровне и не создает конфликта call-by-value, который является базовой реализацией.)
user166390 15.12.2011 06:12:03
@Gravity: Вы можете пойти и поместить свой комментарий на ОГРОМНЫЙ рекламный щит или что-то? Вот и вся проблема в двух словах. И это показывает, что все это семантика. Если мы не согласны с базовым определением ссылки, мы не согласимся с ответом на этот вопрос :)
MadConan 12.11.2013 20:58:58
Я думаю, что путаница заключается в «передаче по ссылке» против «ссылочной семантики». Java передается по значению со ссылочной семантикой.
spraff 27.03.2014 13:54:30
@Gravity, хотя вы абсолютно правы в том, что у людей, пришедших из C ++, инстинктивно будет другой набор интуиции в отношении термина «ссылка», я лично считаю, что тело более скрыто в «by». «Передача» сбивает с толку тем, что она абсолютно отличается от «Передачи» в Java. В C ++, однако, в разговорной речи это не так. В C ++ вы можете сказать «передача ссылки», и понятно, что она пройдет swap(x,y)тест .
user4229245 23.03.2015 13:44:28
30 ОТВЕТОВ

Java всегда передается по значению, без исключений, никогда .

Так почему же это может смущать любого, кто верит, что Java передается по ссылке, или думает, что у него есть пример того, как Java действует как передача по ссылке? Ключевым моментом является то, что Java никогда не обеспечивает прямого доступа к значениям самих объектов ни при каких обстоятельствах. Единственный доступ к объектам - через ссылку на этот объект. Поскольку доступ к объектам Java всегда осуществляется через ссылку, а не напрямую, обычно говорят о полях, переменных и аргументах методов как об объектах , когда педантично они являются только ссылками на объекты .Путаница проистекает из этого (строго говоря, неверного) изменения в номенклатуре.

Итак, при вызове метода

  • Для аргументов примитива ( int, longи т. Д.) Передача по значению является фактическим значением примитива (например, 3).
  • Для объектов передача по значению является значением ссылки на объект .

Так что если у вас есть doSomething(foo)и public void doSomething(Foo foo) { .. }два Foos скопировали ссылки, которые указывают на одни и те же объекты.

Естественно, передача по значению ссылки на объект очень похожа на передачу объекта по ссылке (и практически не отличается от нее).

679
22.01.2016 03:37:07
Поскольку значения примитивов являются неизменяемыми (например, String), разница между этими двумя случаями на самом деле не имеет значения.
Paŭlo Ebermann 3.02.2011 01:23:04
В точку. Для всего, что вы можете сказать через наблюдаемое поведение JVM, примитивы могут передаваться по ссылке и могут жить в куче. Они не делают, но это на самом деле не наблюдается в любом случае.
Gravity 31.07.2011 04:50:55
примитивы неизменны? это новое в Java 7?
user85421-Banned 22.08.2011 11:26:09
Указатели неизменны, примитивы вообще изменчивы. Строка тоже не примитив, это объект. Более того, базовая структура String является изменяемым массивом. Единственная неизменная вещь об этом - длина, которая является неотъемлемой природой массивов.
kingfrito_5005 12.08.2015 18:44:10
Это еще один ответ, указывающий на семантическую природу аргумента. Определение ссылки, предоставленное в этом ответе, сделало бы Java «передачей по ссылке». Автор, по сути, допускает то же самое в последнем абзаце, заявляя, что он «практически не отличается от« передачи по ссылке ». Я сомневаюсь, что OP спрашивает из-за желания понять реализацию Java, а скорее понять, как правильно использовать Java. Если бы это было неразличимо на практике, тогда не было бы никакого смысла в заботе, и даже думать об этом было бы пустой тратой времени.
Loduwijk 2.08.2017 15:54:08

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

Таким образом, вы не можете изменить ссылку, которая будет передана.

326
2.09.2008 20:20:08
Но то, что постоянно повторяется: «Вы не можете изменить значение объектов, передаваемых в аргументах», явно ложно. Возможно, вы не сможете заставить их ссылаться на другой объект, но вы можете изменить их содержимое, вызвав их методы. ИМО это означает, что вы теряете все преимущества ссылок и не получаете никаких дополнительных гарантий.
Timmmm 24.07.2011 19:13:09
Я никогда не говорил: «Вы не можете изменить значение объектов, переданных в аргументах». Я скажу: «Вы не можете изменить значение ссылки на объект, переданной в качестве аргумента метода», что является истинным утверждением о языке Java. Очевидно, что вы можете изменить состояние объекта (если оно не является неизменным).
ScArcher2 1.08.2011 14:19:38
Имейте в виду, что вы не можете передавать объекты в Java; объекты остаются в куче. Указатели на объекты могут быть переданы (которые копируются в кадр стека для вызываемого метода). Таким образом, вы никогда не изменяете переданное значение (указатель), но вы можете следить за ним и изменять объект в куче, на который он указывает. Это передача по значению.
Scott Stanchfield 26.01.2012 22:17:56
Похоже, вы только что передали ссылку ? Я собираюсь отстаивать тот факт, что Java все еще является скопированным языком передачи по ссылке. Тот факт, что это скопированная ссылка, не меняет терминологию. Обе ссылки все еще указывают на один и тот же объект. Это аргумент пуриста ...
John Strickler 12.03.2014 17:38:26
Java не передает объект, он передает значение указателя на объект. Это создает новый указатель на эту ячейку памяти исходного объекта в новой переменной. Если вы измените значение (адрес памяти, на который он указывает) этой переменной-указателя в методе, то исходный указатель, используемый в вызывающем методе, останется неизменным. Если вы называете параметр ссылкой, то тот факт, что это копия исходной ссылки, а не самой исходной ссылки, так что теперь имеется две ссылки на объект, означает, что он передается по значению
theferrit32 19.11.2014 16:38:17

По сути, переназначение параметров объекта не влияет на аргумент, например,

private void foo(Object bar) {
    bar = null;
}

public static void main(String[] args) {
    String baz = "Hah!";
    foo(baz);
    System.out.println(baz);
}

распечатает "Hah!"вместо null. Причина, по которой это работает, заключается в том, что barэто копия значения baz, которое является просто ссылкой на "Hah!". Если бы это была сама фактическая ссылка, то fooпереопределил bazбы null.

162
10.07.2018 09:57:33
Я бы скорее сказал, что bar является копией ссылочной базы baz (или псевдонима baz), которая изначально указывает на один и тот же объект.
MaxZoom 11.02.2015 05:36:27
не немного ли отличается класс String от всех других классов?
Mehdi Karamosly 10.12.2018 21:53:15

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

173
2.09.2008 20:23:30

Java всегда передается по значению . К сожалению, когда мы передаем значение объекта, мы передаем ссылку на него. Это сбивает с толку новичков.

Это выглядит так:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    // 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
    aDog == oldDog; // true
}

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()in mainвозвращается ."Fifi"foo

Точно так же:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
    // but it is still the same dog:
    aDog == oldDog; // 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, но это не возможно изменить значение переменного aDogсам.

5771
14.01.2019 23:10:29
Разве это не немного путает проблему с внутренними деталями? Нет никакой концептуальной разницы между «передачей ссылки» и «передачей значения ссылки», при условии, что вы подразумеваете «значение внутреннего указателя на объект».
izb 8.09.2008 14:58:53
Но есть небольшая разница. Посмотрите на первый пример. Если бы он был просто передан по ссылке, aDog.name был бы "Fifi". Это не так - ссылка, которую вы получаете, является ссылкой на значение, которая при перезаписи будет восстановлена ​​при выходе из функции.
erlando 11.09.2008 06:55:48
@Lorenzo: Нет, в Java все передается по значению. Примитивы передаются по значению, а ссылки на объекты передаются по значению. Сами объекты никогда не передаются методу, но объекты всегда находятся в куче, и в метод передается только ссылка на объект.
Esko Luontola 12.02.2009 23:02:20
Моя попытка найти хороший способ визуализации прохождения объекта: представьте воздушный шар. Вызов fxn подобен привязке второй строки к воздушному шару и передаче линии fxn. Параметр = новый Balloon () обрежет эту строку и создаст новый воздушный шар (но это не влияет на исходный воздушный шар). Параметр.pop () будет по-прежнему отображать его, потому что он следует за строкой в ​​тот же самый оригинальный шарик. Java передается по значению, но передаваемое значение не является глубоким, оно находится на самом высоком уровне, то есть примитив или указатель. Не путайте это с глубокой передачей по значению, когда объект полностью клонируется и передается.
dhackner 20.10.2010 16:38:55
Что сбивает с толку, так это то, что ссылки на объекты на самом деле являются указателями. В начале SUN называл их указателями. Тогда маркетинг сообщил, что «указатель» был плохим словом. Но вы все еще видите «правильную» номенклатуру в исключении NullPointerException.
Prof. Falken contract breached 9.02.2011 09:46:44

Короче говоря, у объектов Java есть некоторые очень специфические свойства.

В общем, Java , имеет примитивные типы ( int, bool, char, doubleи т.д.), которые передаются непосредственно по значению. Тогда у Java есть объекты (все, что происходит от java.lang.Object). Объекты на самом деле всегда обрабатываются с помощью ссылки (ссылка - это указатель, который вы не можете коснуться). Это означает, что, по сути, объекты передаются по ссылке, так как ссылки обычно не интересны. Это, однако, означает, что вы не можете изменить объект, на который указывает объект, так как сама ссылка передается по значению.

Это звучит странно и сбивает с толку? Давайте рассмотрим, как C реализует передачу по ссылке и передачу по значению. В С соглашением по умолчанию является передача по значению. void foo(int x)передает int по значению. void foo(int *x)это функция , которая не желает , чтобы int a, но указатель на междунар: foo(&a). Можно использовать это с &оператором для передачи адреса переменной.

Отнесите это на C ++, и у нас есть ссылки. Ссылки в основном (в этом контексте) являются синтаксическим сахаром, который скрывает указатель части уравнения: void foo(int &x)вызывается foo(a), когда сам компилятор знает, что это ссылка, и aдолжен быть передан адрес не ссылки . В Java все переменные, ссылающиеся на объекты, на самом деле относятся к ссылочному типу, фактически вызывая вызов по ссылке для большинства целей и задач без детального контроля (и сложности), предоставляемого, например, C ++.

26
4.05.2014 09:33:48
Я думаю, что это очень близко к моему пониманию объекта Java и его ссылки. Объект в Java передается методу посредством эталонной копии (или псевдонима).
MaxZoom 11.02.2015 05:58:48

Различие, или, может быть, просто то, что я помню, как у меня было такое же впечатление, как у оригинального плаката, заключается в следующем: Java всегда передается по значению. Все объекты (в Java, все, кроме примитивов) в Java являются ссылками. Эти ссылки передаются по значению.

39
30.04.2014 04:50:16
Я нахожу ваше предпоследнее предложение очень вводящим в заблуждение. Это неправда, что «все объекты в Java являются ссылками». Это только ссылки на те объекты, которые являются ссылками.
Dawood ibn Kareem 25.01.2017 10:09:26

Как уже упоминали многие люди, Java всегда передается по значению

Вот еще один пример, который поможет вам понять разницу ( классический пример обмена ):

public class Test {
  public static void main(String[] args) {
    Integer a = new Integer(2);
    Integer b = new Integer(3);
    System.out.println("Before: a = " + a + ", b = " + b);
    swap(a,b);
    System.out.println("After: a = " + a + ", b = " + b);
  }

  public static swap(Integer iA, Integer iB) {
    Integer tmp = iA;
    iA = iB;
    iB = tmp;
  }
}

Печать:

До: а = 2, б = 3
После: а = 2, б = 3

Это происходит потому, что iA и iB являются новыми локальными ссылочными переменными, которые имеют одинаковое значение переданных ссылок (они указывают на a и b соответственно). Таким образом, попытка изменить ссылки iA или iB изменится только в локальной области, а не вне этого метода.

36
3.09.2008 20:01:59

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

public class PassByCopy{
    public static void changeName(Dog d){
        d.name = "Fido";
    }
    public static void main(String[] args){
        Dog d = new Dog("Maxx");
        System.out.println("name= "+ d.name);
        changeName(d);
        System.out.println("name= "+ d.name);
    }
}
class Dog{
    public String name;
    public Dog(String s){
        this.name = s;
    }
}

вывод Java PassByCopy:

имя = макс
имя = фидо

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

31
12.01.2009 20:47:43
«передача по копии» - это то, что означает передача по значению .
T.J. Crowder 7.08.2016 08:46:02
@TJCrowder. «Передача копией» может быть более подходящим способом выражения той же концепции для целей Java: потому что семантически это очень затрудняет использование идеи, сродни передаче по ссылке, что вам и нужно в Java.
mike rodent 24.02.2020 19:28:30
@mikerodent - Извините, я не следил за этим. :-) «Передача по значению» и «передача по ссылке» являются правильными терминами искусства для этих понятий. Мы должны использовать их (предоставляя их определения, когда они нужны людям), а не придумывать новые. Выше я сказал, что «передача по копии» означает «передача по значению», но на самом деле неясно, что означает «передача по копии» - копия чего? Значение? (Например, ссылка на объект.) Сам объект? Поэтому я придерживаюсь условий искусства. :-)
T.J. Crowder 25.02.2020 07:51:02

Я создал ветку, посвященную этим вопросам для любых языков программирования здесь .

Ява также упоминается . Вот краткое резюме:

  • Java передает его параметры по значению
  • «по значению» это единственный способ в Java передать параметр методу
  • использование методов из объекта, заданного в качестве параметра, изменит объект, так как ссылки указывают на исходные объекты. (если этот метод сам изменяет некоторые значения)
26
23.05.2017 12:02:58

Я только заметил, что вы ссылались на мою статью .

Спецификация Java говорит, что все в Java передается по значению. В Java нет такого понятия, как «передача по ссылке».

Ключом к пониманию этого является то, что

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
  • на линии "ААА"
    • someDogследует к Dogнему указывает ( Dogобъект по адресу 42)
    • что Dog(тот, по адресу 42) попросили изменить его имя на Макс
  • на линии "ВВВ"
    • новый Dogсоздан. Допустим, он по адресу 74
    • мы назначаем параметр someDog74
  • на линии "CCC"
    • SomeDog следует на Dogэто указывает ( Dogобъект по адресу 74)
    • что Dog(тот по адресу 74) попросили изменить его имя на Rowlf
  • тогда мы вернемся

Теперь давайте подумаем о том, что происходит вне метода:

Изменился myDog?

Там ключ.

Учитывая, что myDogэто указатель , а не факт Dog, ответ НЕТ. myDogпо-прежнему имеет значение 42; он по-прежнему указывает на оригинал Dog(но обратите внимание, что из-за строки «AAA» его имя теперь «Макс» - все тот же Dog; myDogзначение не изменилось.)

Совершенно верно следовать за адресом и менять то, что в конце; это не меняет переменную, однако.

Java работает точно так же, как C. Вы можете назначить указатель, передать указатель методу, следовать указателю в методе и изменить данные, на которые он указывал. Однако вы не можете изменить, куда указывает этот указатель.

В C ++, Ada, Pascal и других языках, которые поддерживают передачу по ссылке, вы действительно можете изменить переданную переменную.

Если бы у Java была семантика передачи по ссылке, то fooметод, который мы определили выше, изменился бы там, куда myDogуказывал, когда он был назначен someDogв строке BBB.

Представьте, что ссылочные параметры являются псевдонимами для передаваемой переменной. Когда этот псевдоним назначен, переменная также передается.

3000
28.08.2015 15:32:38
Вот почему распространенный рефрен «У Java нет указателей» вводит в заблуждение.
Beska 2.04.2009 17:52:18
Вы не правы, имхо. «Помня, что myDog - это указатель, а не настоящий Dog, ответ НЕТ. MyDog по-прежнему имеет значение 42; он по-прежнему указывает на исходный Dog». myDog имеет значение 42, но его аргумент name теперь содержит «Max», а не «Rover» в строке // AAA.
Özgür 6.04.2009 05:22:09
Подумайте об этом таким образом. У кого-то есть адрес Энн Арбор, Мичиган (мой родной город, GO СИНИЙ!) На листке бумаги под названием "annArborLocation". Вы копируете его на лист бумаги под названием «myDestination». Вы можете доехать до «myDestination» и посадить дерево. Возможно, вы изменили что-то о городе в этом месте, но это не изменит LAT / LON, который был написан на любой бумаге. Вы можете изменить LAT / LON на «myDestination», но это не изменит «annArborLocation». Это помогает?
Scott Stanchfield 23.04.2009 13:06:40
@ Скотт Стэнчфилд: Я прочитал вашу статью около года назад, и это действительно помогло мне прояснить ситуацию. Спасибо! Позвольте мне смиренно предложить небольшое дополнение: вы должны упомянуть, что на самом деле существует определенный термин, описывающий эту форму «вызов по значению, где значение является ссылкой», который был изобретен Барбарой Лисков для описания стратегии оценки ее языка CLU в 1974, чтобы избежать путаницы, подобной той, о которой говорится в вашей статье: вызов посредством совместного использования (иногда называемый вызовом посредством совместного использования объекта или просто вызов по объекту ), который в значительной степени прекрасно описывает семантику.
Jörg W Mittag 7.09.2010 22:12:35
@Gevorg - Языки C / C ++ не владеют понятием «указатель». Существуют другие языки, которые используют указатели, но не допускают тех же типов манипулирования указателями, которые разрешены в C / C ++. У Java есть указатели; они просто защищены от вреда.
Scott Stanchfield 30.12.2011 18:30:02

Чтобы показать контраст, сравните следующие фрагменты 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 имеет только два типа передачи: по значению для встроенных типов и по значению указателя для типов объектов.

191
27.09.2016 14:42:57
+1 Я бы также добавил Dog **objPtrPtrк примеру C ++, чтобы мы могли изменить то, на что «указывает» указатель.
Amro 2.07.2014 03:59:12
Но это не отвечает на данный вопрос в Java. На самом деле все, что есть в методе Java - это Pass By Value, не более того.
tauitdnmd 6.11.2018 09:11:44
Этот пример просто доказывает, что Java использует очень эквивалентный указатель в C нет? Где проходить по значению в C в основном только для чтения? В конце концов вся эта тема непонятна
amdev 21.02.2019 12:58:41

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

9
30.04.2014 04:52:02

Суть дела в том, что слово « ссылка» в выражении «передача по ссылке» означает нечто совершенно отличное от обычного значения слова « ссылка» в Java.

Обычно в Java ссылка означает ссылку на объект . Но технические термины, передаваемые по ссылке / значению из теории языка программирования, говорят о ссылке на ячейку памяти, содержащую переменную , что является чем-то совершенно другим.

116
6.02.2018 10:18:14
@Gevorg - Тогда что такое «NullPointerException»?
Hot Licks 22.09.2013 14:09:21
@Hot: К сожалению, названное исключение до того, как Java установил четкую терминологию. Семантически эквивалентное исключение в c # называется NullReferenceException.
JacquesB 23.09.2013 14:08:10
Мне всегда казалось, что использование «ссылки» в терминологии Java - это аффект, препятствующий пониманию.
Hot Licks 23.09.2013 15:46:14
Я научился называть эти так называемые «указатели на объекты» как «дескриптор объекта». Это уменьшило двусмысленность.
moronkreacionz 29.12.2015 21:36:08

Вы никогда не можете перейти по ссылке в Java, и один из очевидных способов - это когда вы хотите вернуть более одного значения из вызова метода. Рассмотрим следующий фрагмент кода на C ++:

void getValues(int& arg1, int& arg2) {
    arg1 = 1;
    arg2 = 2;
}
void caller() {
    int x;
    int y;
    getValues(x, y);
    cout << "Result: " << x << " " << y << endl;
}

Иногда вы хотите использовать тот же шаблон в Java, но не можете; по крайней мере, не напрямую. Вместо этого вы можете сделать что-то вроде этого:

void getValues(int[] arg1, int[] arg2) {
    arg1[0] = 1;
    arg2[0] = 2;
}
void caller() {
    int[] x = new int[1];
    int[] y = new int[1];
    getValues(x, y);
    System.out.println("Result: " + x[0] + " " + y[0]);
}

Как было объяснено в предыдущих ответах, в Java вы передаете указатель на массив как значение в getValues. Этого достаточно, потому что метод затем модифицирует элемент массива, и по соглашению вы ожидаете, что элемент 0 будет содержать возвращаемое значение. Очевидно, что вы можете сделать это другими способами, такими как структурирование вашего кода, так что в этом нет необходимости, или создание класса, который может содержать возвращаемое значение или разрешить его установку. Но простой шаблон, доступный вам в C ++ выше, недоступен в Java.

49
8.03.2009 06:28:52

Насколько я знаю, Java знает только вызов по значению. Это означает, что для примитивных типов данных вы будете работать с копией, а для объектов - с копией ссылки на объекты. Однако я думаю, что есть некоторые подводные камни; например, это не будет работать:

public static void swap(StringBuffer s1, StringBuffer s2) {
    StringBuffer temp = s1;
    s1 = s2;
    s2 = temp;
}


public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer("Hello");
    StringBuffer s2 = new StringBuffer("World");
    swap(s1, s2);
    System.out.println(s1);
    System.out.println(s2);
}

Это заполнит Hello World, а не World Hello, потому что в функции swap вы используете copys, которые не влияют на ссылки в основном. Но если ваши объекты не являются неизменяемыми, вы можете изменить это, например:

public static void appendWorld(StringBuffer s1) {
    s1.append(" World");
}

public static void main(String[] args) {
    StringBuffer s = new StringBuffer("Hello");
    appendWorld(s);
    System.out.println(s);
}

Это заполнит Hello World в командной строке. Если вы измените StringBuffer на String, он выдаст просто Hello, потому что String неизменен. Например:

public static void appendWorld(String s){
    s = s+" World";
}

public static void main(String[] args) {
    String s = new String("Hello");
    appendWorld(s);
    System.out.println(s);
}

Тем не менее, вы можете создать обёртку для String, такую, которая позволила бы использовать её со строками:

class StringWrapper {
    public String value;

    public StringWrapper(String value) {
        this.value = value;
    }
}

public static void appendWorld(StringWrapper s){
    s.value = s.value +" World";
}

public static void main(String[] args) {
    StringWrapper s = new StringWrapper("Hello");
    appendWorld(s);
    System.out.println(s.value);
}

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

56
2.04.2009 17:58:44
+1 для теста подкачки - вероятно, самый простой и удобный способ различить передачу по ссылке и передачу ссылки по значению . Если вы можете легко написать функцию, swap(a, b)которая (1) меняет местами aи bиз POV вызывающей стороны, (2) не зависит от типа в той степени, в которой это позволяет статическая типизация (то есть использование ее с другим типом не требует ничего, кроме изменения объявленных типов aи b) и (3) не требует, чтобы вызывающая сторона явно передавала указатель или имя, тогда язык поддерживает передачу по ссылке.
cHao 17.12.2013 21:24:26
«... для примитивных типов данных вы будете работать с копией, а для объектов - с копией ссылки на объекты» - отлично написано!
Raúl 24.11.2016 13:16:43

Несколько исправлений к некоторым постам.

C не поддерживает передачу по ссылке. ВСЕГДА передается по значению. C ++ поддерживает передачу по ссылке, но не используется по умолчанию и довольно опасен.

Неважно, какое значение имеет Java: примитив или адрес (грубо) объекта, оно ВСЕГДА передается по значению.

Если объект Java «ведет себя» так, как его передают по ссылке, это свойство изменчивости и не имеет абсолютно никакого отношения к механизмам передачи.

Я не уверен, почему это так сбивает с толку, возможно, потому, что так много «программистов» на Java не обучены формально и поэтому не понимают, что на самом деле происходит в памяти?

25
26.12.2009 20:19:22
+1. Что C делает поддержку, будет обрабатывать ссылки (которые C вызывает указатели ) в качестве значений первого класса, а затем передать их по значению .
Jörg W Mittag 7.09.2010 22:02:17
Да, в C вы можете создавать указатели не только на объекты, но и на произвольные переменные - так что вы можете легко смоделировать «вызов по ссылке» любой переменной. В Java это работает только для переменных, которые имеют окружающий объект, который вы можете дать.
Paŭlo Ebermann 3.02.2011 01:27:19

Java копирует ссылку по значению. Таким образом, если вы измените его на что-то другое (например, используя new), ссылка не изменится вне метода. Для нативных типов оно всегда передается по значению.

19
26.12.2009 21:05:11

Посмотрите на этот код. Этот код не скинет NullPointerException... Он напечатает "Vinay"

public class Main {
    public static void main(String[] args) {
        String temp = "Vinay";
        print(temp);
        System.err.println(temp);
    }

    private static void print(String temp) {
        temp = null;
    }
}

Если Java передается по ссылке, то она должна быть выброшена, NullPointerExceptionпоскольку для ссылки задано значение Null.

13
9.08.2010 12:21:58
Статика отбрасывает это. открытый класс Main {public static void main (String [] args) {Main m = new Main (); m.aaa (); } public void aaa () {String temp = "Vinay"; печать (температура); System.err.println (температура); } private void print (String temp) {temp = null; }} приведенный выше код не является NPE.
Milhous 12.08.2010 21:27:49
Не получил то, что вы пытаетесь сказать ..?
Vinay Lodha 13.08.2010 06:27:20
Не отвечает на вопрос. ОП просит объяснений, а не просто доказательств.
user207421 2.08.2013 10:04:53

Я не могу поверить, что никто еще не упомянул Барбару Лисков. Когда она разработала CLU в 1974 году, она столкнулась с той же проблемой терминологии, и она изобрела термин вызов путем разделения (также известный как вызов посредством совместного использования объекта и вызов по объекту ) для этого конкретного случая «вызов по значению, где значение равно ссылка".

149
7.09.2010 22:07:02
Мне нравится это различие в номенклатуре. К сожалению, Java поддерживает вызов посредством совместного использования для объектов, но не вызов по значению (как это делает C ++). Java поддерживает вызов по значению только для примитивных типов данных, а не для составных типов данных.
Derek Mahar 8.09.2010 14:16:37
Я действительно не думаю, что нам нужен был дополнительный термин - это просто передача по значению для определенного типа значения. Будет ли добавление «вызов по примитиву» добавить какие-либо разъяснения?
Scott Stanchfield 25.10.2010 20:03:18
wulfgarpro 16.09.2011 00:29:43
Таким образом, я могу передать по ссылке, поделившись некоторым глобальным контекстным объектом или даже передавая контекстный объект, который содержит другие ссылки? Я все еще передаю по значению, но, по крайней мере, у меня есть доступ к ссылкам, которые я могу изменить и заставить их указывать на что-то еще.
YoYo 8.01.2016 00:14:02
@Sanjeev: совместное использование вызовов - особый случай передачи по значению. Тем не менее, многие люди яростно утверждают, что Java (и подобные языки, такие как Python, Ruby, ECMAScript, Smalltalk) передаются по ссылке. Я бы предпочел также называть это передачей по значению, но разделение по объектам кажется разумным термином, с которым могут согласиться даже люди, которые утверждают, что это не передача по значению.
Jörg W Mittag 23.05.2019 00:03:21

По моему мнению, «передача по значению» - ужасный способ описать два одинаковых, но разных события. Я думаю, они должны были спросить меня сначала.

С помощью примитивов мы передаем фактическое значение примитива в метод (или конструктор), будь то целое число «5», символ «c» или что у вас есть. Это действительное значение становится его собственным локальным примитивом. Но с объектами все, что мы делаем, это даем тому же объекту дополнительную ссылку (локальную ссылку), так что теперь у нас есть две ссылки, указывающие на один и тот же объект.

Надеюсь, это простое объяснение поможет.

4
24.06.2011 00:13:17
«Pass by value» является стандартным термином в информатике и существует с 1950-х годов. Нет смысла жаловаться на это сейчас.
user207421 7.06.2016 03:59:03

Это даст вам некоторое представление о том, как на самом деле работает 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. }

Что происходит?

  • Переменная person создается в строке # 1 и в начале имеет значение null.
  • Новый объект Person создается в строке # 2, сохраняется в памяти, а переменной person дается ссылка на объект Person. То есть его адрес. Допустим, 3bad086a.
  • Переменная person, содержащая адрес объекта, передается функции в строке # 3.
  • В строке № 4 вы можете прослушать звук тишины
  • Проверьте комментарий в строке № 5
  • Локальная переменная метода - anotherReferenceToTheSamePersonObject - создается, а затем появляется волшебство в строке # 6:
    • Переменная / эталонное лицо копируется побитно и передается в anotherReferenceToTheSamePersonObject внутри функции.
    • Новые экземпляры Person не создаются.
    • И " person ", и " anotherReferenceToTheSamePersonObject " содержат одно и то же значение 3bad086a.
    • Не пытайтесь это сделать, но person == anotherReferenceToTheSamePersonObject будет истинным.
    • Обе переменные имеют ИДЕНТИЧНЫЕ КОПИИ ссылки, и они обе ссылаются на один и тот же объект Person, ОДИН ЖЕ объект в куче и НЕ КОПИЮ.

Одна картинка стоит тысячи слов:

Передать по значению

Обратите внимание, что стрелки anotherReferenceToTheSamePersonObject направлены к объекту, а не к переменной person!

Если вы не получили его, просто поверьте мне и помните, что лучше сказать, что Java передается по значению . Ну, перейдите по ссылке . Ну что ж, еще лучше передать значение переменной! ;)

Теперь не стесняйтесь ненавидеть меня, но обратите внимание, что, учитывая это, нет разницы между передачей примитивных типов данных и объектов при обсуждении аргументов метода.

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

  • Если это примитивный тип данных, эти биты будут содержать значение самого примитивного типа данных.
  • Если это объект, биты будут содержать значение адреса, который сообщает JVM, как добраться до объекта.

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


Приведенная выше функция changeName никогда не сможет изменить фактическое содержимое (значения битов) переданной ссылки. Другими словами, changeName не может заставить человека обратиться к другому объекту.


Конечно, вы можете сократить его и просто сказать, что Java передается по значению!

723
4.01.2018 10:17:22
Вы имеете в виду указатели? .. Если я его правильно, в public void foo(Car car){ ... }, carявляется локальной fooи содержит расположение кучи Объекта? Так что, если я изменю carзначение на car = new Car(), оно будет указывать на другой объект в куче? и если я изменю значение carсвойства на car.Color = "Red", Объект в указанной куче carбудет изменен. Кроме того, то же самое в C #? Ответьте, пожалуйста! Спасибо!
dpp 25.02.2012 03:29:07
@ domanokz Ты убиваешь меня, пожалуйста, не говори это слово снова! ;) Обратите внимание, что я мог бы ответить на этот вопрос, не сказав при этом также и «ссылку». Это проблема терминологии, и с ней дела обстоят хуже. У меня и Скотта разные взгляды на это, к сожалению. Я думаю, что вы поняли, как это работает в Java, теперь вы можете назвать это передачей по значению, разделением объекта, копированием значения переменной или можете свободно предлагать что-то еще! Мне все равно, пока вы понимаете, как это работает и что находится в переменной типа Object: просто адрес почтового ящика! ;)
Marsellus Wallace 25.02.2012 21:25:22
Похоже, вы только что передали ссылку ? Я собираюсь отстаивать тот факт, что Java все еще является скопированным языком передачи по ссылке. Тот факт, что это скопированная ссылка, не меняет терминологию. Обе ссылки все еще указывают на один и тот же объект. Это аргумент пуриста ...
John Strickler 12.03.2014 17:38:45
Так что загляните в теоретическую строку № 9, System.out.println(person.getName());что появится? "Том" или "Джерри"? Это последнее, что поможет мне преодолеть эту путаницу.
TheBrenny 19.10.2014 12:20:23
«Значение ссылки » - вот что мне наконец-то объяснили.
Alexander Shubert 14.02.2020 08:52:18

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

Когда вы передаете объект методу, вы передаете ссылку, и объект может быть изменен реализацией метода.

void bithday(Person p) {
    p.age++;
}

Ссылка на сам объект передается по значению: вы можете переназначить параметр, но изменение не отражается обратно:

void renameToJon(Person p) { 
    p = new Person("Jon"); // this will not work
}

jack = new Person("Jack");
renameToJon(jack);
sysout(jack); // jack is unchanged

По сути, «р» является ссылкой (указатель на объект) и не может быть изменен.

Примитивные типы передаются по значению. Ссылка на объект также может считаться примитивным типом.

Напомним, что все передается по значению.

6
7.06.2013 08:51:16

Это действительно довольно просто:

Для переменной примитивного типа (например. int, boolean, charИ т.д ...), когда вы используете его имя для аргумента метода, вы передаете значение содержатся в нем ( 5, trueили 'c'). Это значение копируется, и переменная сохраняет свое значение даже после вызова метода.

Для переменной ссылочного типа (например. String, ObjectИ т.д ...), когда вы используете его имя для аргумента метода, вы передаете значение , содержащиеся в нем ( на эталонное значение , что «точки» к объекту ). Это ссылочное значение копируется, а переменная сохраняет свое значение даже после вызова метода. Ссылочная переменная продолжает указывать на один и тот же объект.

В любом случае, вы всегда передаете вещи по значению.


Сравните это с C ++, где у вас может быть метод для взятия int&, или в C #, где вы могли бы взять с собой ref int(хотя, в этом случае, вы также должны использовать refмодификатор при передаче имени переменной методу.)

20
1.08.2012 17:31:26
Также помня, что StringS неизменны в Java.
wulfgarpro 1.09.2012 07:58:53
Я считаю полезным думать о переменных типа класса как о «идентификаторах объектов». Строковое представление объектов Java по умолчанию хорошо работает с таким описанием. Если один устанавливает Fooна «Автомобиль # 1234», а также копии Fooс Bar, тогда как Fooи Barбудет держать «Автомобиль # 1234». Поговорка Foo.SetColor(Colors.Blue)покрасит «Автомобиль № 1234» в синий цвет. Другое описание, которое я использую для таких мест хранения, - это «разнородные ссылки на объекты», поскольку код, который передает такую ​​ссылку на объект, не может контролировать, как получатель может поделиться ею.
supercat 25.05.2013 04:12:43
Переменные не влияют на работу Java. Фактические аргументы от оценки выражений.
Tom Blodget 16.07.2018 00:22:58

Java всегда передает аргументы по значению , а не по ссылке.


Позвольте мне объяснить это на примере :

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");

    введите описание изображения здесь

  2. Со стороны метода объявляется ссылка на тип Fooс именем, aи она изначально присваивается null.

    public static void changeReference(Foo a)

    введите описание изображения здесь

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

    changeReference(f);

    введите описание изображения здесь

  4. Объявить ссылку с именем bтипа Fooи назначить ей новый объект типа Fooс атрибутом "b".

    Foo b = new Foo("b");

    введите описание изображения здесь

  5. a = bсоздает новое назначение для ссылки a, а не f для объекта, чьим атрибутом является "b".

    введите описание изображения здесь

  6. При вызове modifyReference(Foo c)метода создается ссылка, cкоторой присваивается объект с атрибутом "f".

    введите описание изображения здесь

  7. c.setAttribute("c");изменит атрибут объекта, на который cуказывает ссылка , и тот же объект, на который fуказывает ссылка .

    введите описание изображения здесь

Надеюсь, теперь вы понимаете, как передача объектов в качестве аргументов работает в Java :)

1714
3.01.2020 09:54:52
+1 Хороший материал. хорошие диаграммы. Я также нашел хорошую лаконичную страницу здесь adp-gmbh.ch/php/pass_by_reference.html Хорошо, я признаю, что она написана на PHP, но это - основа понимания разницы, которую я считаю важной (и как манипулировать этой разницей твои нужды).
DaveM 7.06.2013 08:46:12
@ Eng.Fouad Это хорошее объяснение, но если он aуказывает на тот же объект, что и f(и никогда не получает свою собственную копию объекта, на который fуказывает), любые изменения объекта, сделанные с помощью, aдолжны измениться так же fхорошо (поскольку они оба работают с одним и тем же объектом ), поэтому в какой-то момент aдолжна получить собственную копию объекта, на которую fуказывает.
0x6C38 15.07.2013 19:08:28
@MrD, когда «a» указывает на тот же объект, «f» также указывает на, тогда любое изменение, внесенное в этот объект через «a», также можно наблюдать через «f», НО ЭТО НЕ ИЗМЕНИЛО «f». 'f' все еще указывает на тот же объект. Вы можете полностью изменить объект, но вы никогда не сможете изменить то, на что указывает «f». Это фундаментальный вопрос, который по какой-то причине некоторые люди просто не могут понять.
Mike Braun 1.12.2013 13:02:34
@MikeBraun ... что? Теперь вы меня смутили: S. Разве то, что вы только что написали, противоречит тому, что показывают 6. и 7.?
Evil Washing Machine 18.11.2014 11:34:58
Это лучшее объяснение, которое я нашел. Кстати, а как насчет основной ситуации? Например, аргумент требует тип int, передает ли он копию переменной int в аргумент?
allenwang 21.05.2016 12:38:43

Нет, это не передача по ссылке.

Java передается по значению в соответствии со спецификацией языка Java:

Когда метод или конструктор вызывается (§15.12), значения фактических выражений аргумента инициализируют вновь созданные переменные параметра , каждый из объявленного типа, перед выполнением тела метода или конструктора. Идентификатор, который появляется в DeclaratorId, может использоваться как простое имя в теле метода или конструктора для ссылки на формальный параметр .

55
22.08.2015 05:50:12

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

18
31.12.2012 19:35:46
В Java не существует такого понятия, как «постоянная ссылка», если программист не укажет «наконец».
user207421 2.08.2013 10:05:23
Под константной ссылкой я имел в виду, что нет способа изменить саму ссылку, сказав в функции новый MyClass (). Если я правильно выразился, там ссылки на объекты передаются по значению, что означает, что копия ссылки передается, так что вы можете изменить данные, на которые ссылается эта ссылка, но вы не можете изменить их с помощью нового оператора и выделить новый объект.
fatma.ekici 9.09.2013 13:06:23
Так что исправьте свой ответ. Если это была константа, вы не можете переназначить ее внутри вызываемого метода, и вы можете, если не укажетеfinal.
user207421 25.09.2014 00:41:47

Java всегда передается по значению, параметры являются копиями передаваемых переменных, все объекты определяются с помощью ссылки, а ссылка - это переменная, которая хранит адрес памяти, где находится объект в памяти.

Проверьте комментарии, чтобы понять, что происходит при исполнении; следуйте за числами, поскольку они показывают поток выполнения ..

class Example
{
    public static void test (Cat ref)
    {
        // 3 - <ref> is a copy of the reference <a>
        // both currently reference Grumpy
        System.out.println(ref.getName());

        // 4 - now <ref> references a new <Cat> object named "Nyan"
        ref = new Cat("Nyan");

        // 5 - this should print "Nyan"
        System.out.println( ref.getName() );
    }

    public static void main (String [] args)
    {
        // 1 - a is a <Cat> reference that references a Cat object in memory with name "Grumpy"
        Cat a = new Cat("Grumpy");

        // 2 - call to function test which takes a <Cat> reference
        test (a);

        // 6 - function call ends, and <ref> life-time ends
        // "Nyan" object has no references and the Garbage
        // Collector will remove it from memory when invoked

        // 7 - this should print "Grumpy"
        System.out.println(a.getName());
    }
}
7
17.09.2017 16:38:58
«Передать свою внутреннюю ценность» не имеет смысла.
user207421 17.09.2017 10:09:47
@EJP спасибо за примечание, извините за мой плохой английский с 2013 года, я отредактировал все это, если вы видите лучшую формулировку, вы можете предложить или отредактировать
Khaled.K 17.09.2017 16:41:46
Я хотел бы добавить одну вещь. Если вы изменили имя cat вместо создания нового, оно будет отражаться в памяти даже после возврата метода
user6091735 18.11.2017 16:26:37

В попытке добавить еще больше к этому, я подумал, что включу раздел «Учебное пособие SCJP» по этой теме. Это из руководства, предназначенного для прохождения теста Sun / Oracle на поведение Java, поэтому это хороший источник для использования в этом обсуждении.

Передача переменных в методы (цель 7.3)

7.3. Определите влияние на ссылки на объекты и значения примитивов, когда они передаются в методы, которые выполняют присваивания или другие модифицирующие операции над параметрами.

Методы могут быть объявлены для получения примитивов и / или ссылок на объекты. Вы должны знать, как (или если) переменная вызывающего может быть затронута вызываемым методом. Разница между ссылкой на объект и примитивными переменными при передаче в методы огромна и важна. Чтобы понять этот раздел, вам нужно освоить раздел заданий, описанный в первой части этой главы.

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

Когда вы передаете переменную объекта в метод, вы должны иметь в виду, что вы передаете ссылку на объект, а не на сам объект. Помните, что ссылочная переменная содержит биты, которые представляют (для базовой ВМ) способ добраться до определенного объекта в памяти (в куче). Что еще более важно, вы должны помнить, что вы даже не передаете фактическую ссылочную переменную, а скорее копию ссылочной переменной. Копия переменной означает, что вы получаете копию битов в этой переменной, поэтому, когда вы передаете ссылочную переменную, вы передаете копию битов, представляющих, как добраться до определенного объекта. Другими словами, и вызывающая сторона, и вызываемый метод теперь будут иметь идентичные копии ссылки, и, таким образом, оба будут ссылаться на один и тот же точный (а не копию) объект в куче.

Для этого примера мы будем использовать класс Dimension из пакета java.awt:

1. import java.awt.Dimension;
2. class ReferenceTest {
3.     public static void main (String [] args) {
4.         Dimension d = new Dimension(5,10);
5.         ReferenceTest rt = new ReferenceTest();
6.         System.out.println("Before modify() d.height = " + d.height);
7.         rt.modify(d);
8.         System.out.println("After modify() d.height = "
9.     }
10.
11.
12.
13.   }
14. }

Когда мы запустим этот класс, мы увидим, что метод modify () действительно смог изменить исходный (и единственный) объект Dimension, созданный в строке 4.

C: \ Java Projects \ Reference> java ReferenceTest
Перед изменением () d.height = 10
тусклый = 11
После изменения () d.height = 11

Обратите внимание, что когда объект Dimension в строке 4 передается методу modify (), любые изменения в объекте, происходящие внутри метода, вносятся в объект, ссылка на который была передана. В предыдущем примере ссылочные переменные d и dim указывают на один и тот же объект.

Использует ли Java семантику передачи по значению?

Если Java передает объекты путем передачи ссылочной переменной, означает ли это, что Java использует передачу по ссылке для объектов? Не совсем, хотя вы часто будете слышать и читать, что это так. Java фактически передается по значению для всех переменных, работающих в одной виртуальной машине. Передача по значению означает передачу по переменной. А это значит, передача-копия-переменной! (Это слово снова скопировано!)

Не имеет значения, передаете ли вы примитивные или ссылочные переменные, вы всегда передаете копию битов в переменной. Таким образом, для простой переменной вы передаете копию битов, представляющих значение. Например, если вы передаете переменную int со значением 3, вы передаете копию битов, представляющих 3. Затем вызываемый метод получает свою собственную копию значения, чтобы делать с ним то, что ему нравится.

И если вы передаете переменную ссылки на объект, вы передаете копию битов, представляющих ссылку на объект. Затем вызываемый метод получает свою собственную копию ссылочной переменной, чтобы делать с ней то, что ему нравится. Но поскольку две идентичные ссылочные переменные ссылаются на один и тот же объект, если вызываемый метод изменяет объект (например, путем вызова методов установки), вызывающая сторона увидит, что объект, на который ссылается исходная переменная вызывающей стороны, также изменился. В следующем разделе мы рассмотрим, как меняется картина, когда мы говорим о примитивах.

Суть в передаче по значению: вызываемый метод не может изменить переменную вызывающего, хотя для ссылочных переменных объекта вызываемый метод может изменить объект, на который ссылается переменная. Какая разница между изменением переменной и изменением объекта? Для ссылок на объекты это означает, что вызываемый метод не может переназначить исходную ссылочную переменную вызывающей стороны и заставить ее ссылаться на другой объект или значение NULL. Например, в следующем фрагменте кода

        void bar() {
           Foo f = new Foo();
           doStuff(f);
        }
        void doStuff(Foo g) {
           g.setName("Boo");
           g = new Foo();
        }

переназначение g не переназначает f! В конце метода bar () были созданы два объекта Foo: один ссылается на локальную переменную f, а другой - на локальную (аргументную) переменную g. Поскольку у метода doStuff () есть копия ссылочной переменной, у него есть способ получить исходный объект Foo, например, вызвать метод setName (). Но у метода doStuff () нет способа добраться до ссылочной переменной f. Таким образом, doStuff () может изменять значения внутри объекта, на который ссылается f, но doStuff () не может изменять фактическое содержимое (битовый шаблон) для f. Другими словами, doStuff () может изменить состояние объекта, на который ссылается f, но не может заставить f ссылаться на другой объект!

Передача примитивных переменных

Давайте посмотрим, что происходит, когда примитивная переменная передается методу:

class ReferenceTest {
    public static void main (String [] args) {
      int a = 1;
      ReferenceTest rt = new ReferenceTest();
      System.out.println("Before modify() a = " + a);
      rt.modify(a);
      System.out.println("After modify() a = " + a);
    }
    void modify(int number) {
      number = number + 1;
      System.out.println("number = " + number);
    }
}

В этой простой программе переменная a передается методу modify (), который увеличивает значение переменной на 1. Полученный результат выглядит следующим образом:

  Before modify() a = 1
  number = 2
  After modify() a = 1

Обратите внимание, что a не изменилось после того, как оно было передано методу. Помните, это была копия, которая была передана методу. Когда примитивная переменная передается методу, она передается по значению, что означает «передача-копирование-бит-в-переменной».

9
7.06.2016 04:03:14
С передачей по значению, как в Java, «переменная вызывающего» не имеет смысла. Фактическим аргументом является оценка выражения; переменная не должна быть задействована вообще.
Tom Blodget 16.07.2018 00:28:11

У Java есть только передача по значению. Очень простой пример, чтобы подтвердить это.

public void test() {
    MyClass obj = null;
    init(obj);
    //After calling init method, obj still points to null
    //this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
    objVar = new MyClass();
}
32
29.04.2017 04:31:33
Это самый простой и простой способ увидеть, что Java проходит по значению. Значение obj( null) было передано init, а не ссылка на obj.
David Schwartz 10.06.2017 23:24:59