Я всегда думал, что Java была по ссылке .
Тем не менее, я видел пару постов в блоге (например, этот блог ), которые утверждают, что это не так.
Я не думаю, что понимаю разницу, которую они проводят.
Какое объяснение?
Я всегда думал, что Java была по ссылке .
Тем не менее, я видел пару постов в блоге (например, этот блог ), которые утверждают, что это не так.
Я не думаю, что понимаю разницу, которую они проводят.
Какое объяснение?
call-by-value
иcall-by-reference
отличается очень важным образом . (Лично я предпочитаю использоватьcall-by-object-sharing
эти дни болееcall-by-value[-of-the-reference]
, поскольку это описывает семантику на высоком уровне и не создает конфликтаcall-by-value
, который является базовой реализацией.)swap(x,y)
тест .Ответы:
Java всегда передается по значению . К сожалению, когда мы передаем значение объекта, мы передаем ссылку на него. Это сбивает с толку новичков.
Это выглядит так:
В приведенном выше примере
aDog.getName()
все еще вернется"Max"
. Значение вaDog
пределахmain
не изменяется в функцииfoo
сDog
"Fifi"
как ссылка на объект передается по значению. Если бы он был передан по ссылке, то после вызова к возвращаетсяaDog.getName()
in .main
"Fifi"
foo
Точно так же:
В приведенном выше примере
Fifi
это имя собаки после вызова,foo(aDog)
потому что имя объекта было установлено внутриfoo(...)
. Любые операции , которыеfoo
выполняют наd
таковы , что для всех практических целей, они выполняются наaDog
, но это не возможно изменить значение переменногоaDog
сам.источник
Я только что заметил, что вы ссылались на мою статью .
Спецификация Java говорит, что все в Java передается по значению. В Java нет такого понятия, как «передача по ссылке».
Ключом к пониманию этого является то, что
это не собака; это на самом деле указатель на собаку.
Что это значит, когда у вас есть
вы по сути передаете адрес созданного
Dog
объектаfoo
методу.(Я говорю по существу, потому что указатели Java не являются прямыми адресами, но проще всего так думать о них)
Предположим, что
Dog
объект находится по адресу памяти 42. Это означает, что мы передаем 42 методу.если метод был определен как
давайте посмотрим на то, что происходит.
someDog
установлено значение 42someDog
следует кDog
нему указывает (Dog
объект по адресу 42)Dog
(тот, по адресу 42) попросили изменить его имя на МаксDog
создан. Допустим, он по адресу 74someDog
74Dog
это указывает (Dog
объект по адресу 74)Dog
(тот по адресу 74) попросили изменить его имя на RowlfТеперь давайте подумаем о том, что происходит вне метода:
Изменился
myDog
?Там ключ.
Помня, что
myDog
это указатель , а не фактDog
, ответ НЕТ.myDog
по-прежнему имеет значение 42; он по-прежнему указывает на оригиналDog
(но обратите внимание, что из-за строки «AAA» его имя теперь «Макс» - все тот же Dog;myDog
значение не изменилось.)Совершенно верно следовать за адресом и изменять то, что в конце этого; это не меняет переменную, однако.
Java работает точно так же, как C. Вы можете назначить указатель, передать указатель на метод, следовать указателю в методе и изменить данные, на которые он указывал. Однако вы не можете изменить, куда указывает этот указатель.
В C ++, Ada, Pascal и других языках, которые поддерживают передачу по ссылке, вы действительно можете изменить переданную переменную.
Если бы у Java была семантика передачи по ссылке, то
foo
метод, который мы определили выше, изменился бы там, кудаmyDog
указывал, когда он был назначенsomeDog
в строке BBB.Представьте, что ссылочные параметры являются псевдонимами для передаваемой переменной. Когда этот псевдоним назначен, переменная также передается.
источник
Java всегда передает аргументы по значению , а не по ссылке.
Позвольте мне объяснить это на примере :
Я объясню это по шагам:
Объявить ссылку с именем
f
типаFoo
и назначить ей новый объект типаFoo
с атрибутом"f"
.Со стороны метода объявляется ссылка на тип
Foo
с именем,a
и она изначально присваиваетсяnull
.Когда вы вызываете метод
changeReference
, ссылкеa
будет присвоен объект, который передается в качестве аргумента.Объявить ссылку с именем
b
типаFoo
и назначить ей новый объект типаFoo
с атрибутом"b"
.a = b
создает новое назначение для ссылкиa
, а неf
для объекта, чьим атрибутом является"b"
.Когда вы вызываете
modifyReference(Foo c)
метод, создается ссылкаc
и назначается объект с атрибутом"f"
.c.setAttribute("c");
изменит атрибут объекта, на которыйc
указывает ссылка , и тот же объект, на которыйf
указывает ссылка .Надеюсь, теперь вы понимаете, как передача объектов в качестве аргументов работает в Java :)
источник
a
указывает на тот же объект, что иf
(и никогда не получает свою собственную копию объекта,f
указывает на), любые изменения в объекте, сделанные с использованием,a
должны измениться так жеf
хорошо (так как они оба работают с одним и тем же объектом ), поэтому в какой-то моментa
должна получить собственную копию объекта, на которуюf
указывает.Это даст вам некоторое представление о том, как на самом деле работает Java, до такой степени, что в вашем следующем обсуждении передачи Java по ссылке или по значению вы просто улыбнетесь :-)
Шаг первый, пожалуйста, удалите из памяти это слово, которое начинается с 'p' "_ _ _ _ _ _ _", особенно если вы пришли из других языков программирования. Java и 'p' не могут быть записаны в одной книге, на форуме или даже в txt.
Шаг второй Помните, что когда вы передаете Object в метод, вы передаете ссылку на Object, а не сам объект.
Теперь подумайте о том, что ссылка / переменная объекта делает / является:
В следующем (пожалуйста, не пытайтесь скомпилировать / выполнить это ...):
Что просходит?
Одна картинка стоит тысячи слов:
Обратите внимание, что стрелки anotherReferenceToTheSamePersonObject направлены на объект, а не на переменную person!
Если вы не получили его, просто поверьте мне и помните, что лучше сказать, что Java передается по значению . Ну, перейдите по ссылке . Ну что ж, еще лучше, если передать значение переменной! ;)
Теперь не стесняйтесь ненавидеть меня, но обратите внимание, что с учетом этого нет разницы между передачей примитивных типов данных и объектов при обсуждении аргументов метода.
Вы всегда передаете копию битов значения ссылки!
Конечно, вы можете сократить его и просто сказать, что Java передается по значению!
источник
public void foo(Car car){ ... }
,car
является локальнойfoo
и содержит расположение кучи Объекта? Так что, если я изменюcar
значение наcar = new Car()
, оно будет указывать на другой объект в куче? и если я изменю значениеcar
свойства наcar.Color = "Red"
, объект в указанной кучеcar
будет изменен. Кроме того, то же самое в C #? Ответьте, пожалуйста! Спасибо!System.out.println(person.getName());
что появится? "Том" или "Джерри"? Это последнее, что поможет мне преодолеть эту путаницу.Java всегда передается по значению, без исключений, никогда .
Так почему же это может смущать любого, кто верит, что Java передается по ссылке, или думает, что у него есть пример того, как Java действует как передача по ссылке? Ключевым моментом является то, что Java никогда не обеспечивает прямого доступа к значениям самих объектов ни при каких обстоятельствах. Единственный доступ к объектам - через ссылку на этот объект. Поскольку доступ к объектам Java всегда осуществляется через ссылку, а не напрямую, обычно говорят о полях, переменных и аргументах методов как об объектах , когда педантично они являются только ссылками на объекты .Путаница проистекает из этого (строго говоря, неверного) изменения в номенклатуре.
Итак, при вызове метода
int
,long
и т. Д.) Передача по значению является фактическим значением примитива (например, 3).Так что если у вас есть
doSomething(foo)
иpublic void doSomething(Foo foo) { .. }
два Foos скопировали ссылки, которые указывают на одни и те же объекты.Естественно, передача по значению ссылки на объект очень похожа (и практически не отличается от) передачи объекта по ссылке.
источник
Java передает ссылки по значению.
Таким образом, вы не можете изменить ссылку, которая будет передана.
источник
Я чувствую, что спор о «передача по ссылке против передачи по значению» не является супер-полезным.
Если вы говорите: «Java является передачей по любому (ссылка / значение)», в любом случае вы не дадите полного ответа. Вот некоторая дополнительная информация, которая, надеюсь, поможет понять, что происходит в памяти.
Ускоренный курс по стеку / куче, прежде чем мы перейдем к реализации Java: значения идут и выходят из стека хорошим упорядоченным образом, как стопка тарелок в кафетерии. Память в куче (также известная как динамическая память) является случайной и дезорганизованной. JVM просто находит место, где только может, и освобождает его, поскольку переменные, которые его используют, больше не нужны.
Ладно. Во-первых, локальные примитивы идут в стек. Итак, этот код:
Результаты в этом:
Когда вы объявляете и создаете экземпляр объекта. Фактический объект идет в кучу. Что идет в стек? Адрес объекта в куче. Программисты C ++ назвали бы это указателем, но некоторые Java-разработчики против слова «указатель». Без разницы. Просто знайте, что адрес объекта уходит в стек.
Вот так:
Массив - это объект, поэтому он также попадает в кучу. А как насчет объектов в массиве? Они получают свое собственное пространство кучи, и адрес каждого объекта идет внутри массива.
Итак, что передается, когда вы вызываете метод? Если вы передаете объект, на самом деле вы передаете адрес объекта. Некоторые могут сказать «значение» адреса, а некоторые говорят, что это просто ссылка на объект. Это происхождение священной войны между сторонниками «ссылки» и «ценности». То, что вы называете, не так важно, как то, что вы понимаете, что то, что передается, - это адрес объекта.
Создается одна строка, и место для нее выделяется в куче, а адрес строки сохраняется в стеке и получает идентификатор
hisName
, поскольку адрес второй строки совпадает с первым, новая строка не создается и новое пространство кучи не выделяется, но в стеке создается новый идентификатор. Затем мы вызываемshout()
: создается новый кадр стека и создается новый идентификатор,name
и ему присваивается адрес уже существующей строки.Итак, ценность, ссылка? Вы говорите "картофель".
источник
Просто чтобы показать контраст, сравните следующие фрагменты C ++ и Java :
В C ++: Примечание: плохой код - утечки памяти! Но это демонстрирует суть.
В Java
Java имеет только два типа передачи: по значению для встроенных типов и по значению указателя для типов объектов.
источник
Dog **objPtrPtr
к примеру C ++, чтобы мы могли изменить то, на что «указывает» указатель.Java передает ссылки на объекты по значению.
источник
По сути, переназначение параметров объекта не влияет на аргумент, например,
распечатает
"Hah!"
вместоnull
. Причина, по которой это работает, заключается в том, чтоbar
это копия значенияbaz
, которое является просто ссылкой на"Hah!"
. Если бы это было само собой фактическое задание, тогдаfoo
бы переопределенаbaz
вnull
.источник
Я не могу поверить, что никто еще не упомянул Барбару Лисков. Когда она разработала CLU в 1974 году, она столкнулась с той же проблемой терминологии, и она изобрела термин вызов путем разделения (также известный как вызов посредством разделения объектов и вызов по объекту ) для этого конкретного случая «вызов по значению, где значение равно ссылка".
источник
Суть дела в том, что слово « ссылка» в выражении «передача по ссылке» означает нечто совершенно отличное от обычного значения слова « ссылка» в Java.
Обычно в Java ссылка означает ссылку на объект . Но технические термины, передаваемые по ссылке / значению из теории языка программирования, говорят о ссылке на ячейку памяти, содержащую переменную , что является чем-то совершенно другим.
источник
В Java все ссылки, поэтому, когда у вас есть что-то вроде:
Point pnt1 = new Point(0,0);
Java делает следующее:Java не передает аргументы метода по ссылке; он передает их по значению. Я буду использовать пример с этого сайта :
Ход программы:
Создание двух разных объектов Point с двумя разными ссылками.
Ожидаемый результат будет:
В этой строке «передача по значению» входит в игру ...
Список литература
pnt1
иpnt2
которые передаются по значению к хитрому способу, что означает , что теперь ваши ссылкиpnt1
иpnt2
имеют ихcopies
имениarg1
иarg2
.sopnt1
иarg1
точки на тот же объект. (То же самое дляpnt2
иarg2
)В
tricky
методе:Далее в
tricky
методеЗдесь вы сначала создаете новую
temp
ссылку на точку, которая будет указывать на то же место, что иarg1
ссылка. Затем вы перемещаете ссылку,arg1
чтобы указать на то же место, что иarg2
ссылка. Наконецarg2
будет указывать на то же место , какtemp
.Отсюда сфера
tricky
метода нет , и вы не имеете доступа больше к ссылкам:arg1
,arg2
,temp
. Но важно отметить, что все, что вы делаете с этими ссылками, когда они «в жизни», будет постоянно влиять на объект, на который они указывают .Итак, после выполнения метода
tricky
, когда вы вернетесь кmain
, у вас будет такая ситуация:Итак, теперь полностью выполнение программы будет:
источник
arg1.x = 1; arg1.y = 1; arg2.x = 2; arg2.y = 2;
так, чтобы arg1 теперь содержал pnt2 refrence, а arg2 держал теперь pnt1 ссылку, поэтому его печатьX1: 2 Y1: 2 X2: 1 Y2: 1
Java всегда передается по значению, а не по ссылке
Прежде всего, нам нужно понять, что такое передача по значению и передача по ссылке.
Передача по значению означает, что вы делаете копию в памяти фактического значения параметра, которое передается. Это копия содержимого фактического параметра .
Передача по ссылке (также называемая передачей по адресу) означает, что копия адреса фактического параметра сохраняется .
Иногда Java может дать иллюзию передачи по ссылке. Давайте посмотрим, как это работает, используя пример ниже:
Выход этой программы:
Давайте разберемся шаг за шагом:
Как мы все знаем, он создаст объект в куче и вернет значение ссылки обратно в t. Например, предположим, что значение t равно
0x100234
(мы не знаем фактическое внутреннее значение JVM, это только пример).При передаче ссылки t в функцию она не будет напрямую передавать фактическое значение ссылки объекта test, но создаст копию t и затем передаст ее функции. Поскольку он передается по значению , он передает копию переменной, а не фактическую ссылку на нее. Поскольку мы сказали, что значение t было
0x100234
, и t, и f будут иметь одинаковое значение, и, следовательно, они будут указывать на один и тот же объект.Если вы измените что-либо в функции, используя ссылку f, это изменит существующее содержимое объекта. Вот почему мы получили вывод
changevalue
, который обновляется в функции.Чтобы понять это более четко, рассмотрим следующий пример:
Будет ли это бросить
NullPointerException
? Нет, потому что он передает только копию ссылки. В случае передачи по ссылке он мог бы выброситьNullPointerException
, как показано ниже:Надеюсь, это поможет.
источник
При представлении ссылка всегда является значением, независимо от того, какой язык вы используете.
Получив внешний вид коробки, давайте посмотрим на сборку или некоторое низкоуровневое управление памятью. На уровне ЦП ссылка на что-либо сразу становится значением, если оно записывается в память или в один из регистров ЦП. (Вот почему указатель является хорошим определением. Это значение, которое одновременно имеет цель).
Данные в памяти имеют местоположение, и в этом месте есть значение (байт, слово, что угодно). В Assembly у нас есть удобное решение для присвоения имени определенному местоположению (он же переменная), но при компиляции кода ассемблер просто заменяет имя указанным местоположением, как ваш браузер заменяет доменные имена на IP-адреса.
Вплоть до сути технически невозможно передать ссылку на что-либо на любом языке, не представляя его (когда это сразу становится значением).
Допустим, у нас есть переменная Foo, ее Location находится на 47-м байте в памяти, а ее значение равно 5. У нас есть другая переменная Ref2Foo, которая на 223-м байте в памяти, и ее значение будет 47. Этот Ref2Foo может быть технической переменной , явно не созданный программой. Если вы просто посмотрите на 5 и 47 без какой-либо другой информации, вы увидите только два значения . Если вы используете их в качестве ссылок, чтобы добраться до
5
нас, мы должны путешествовать:Вот как работают таблицы переходов.
Если мы хотим вызвать метод / функцию / процедуру со значением Foo, существует несколько возможных способов передачи переменной в метод, в зависимости от языка и нескольких режимов вызова метода:
В каждом случае выше значения - копия существующего значения - была создана, теперь это должен получить метод обработки. Когда вы пишете «Foo» внутри метода, он либо считывается из EAX, либо автоматически разыменовывается , либо разыменовывается дважды, процесс зависит от того, как работает язык и / или от того, что диктует тип Foo. Это скрыто от разработчика, пока она не обходит процесс разыменования. Таким образом, ссылка - это значение, когда оно представлено, потому что ссылка - это значение, которое должно быть обработано (на уровне языка).
Теперь мы передали Foo методу:
Foo = 9
), это повлияет только на локальную область, поскольку у вас есть копия значения. Изнутри метода мы даже не можем определить, где в памяти находился оригинальный Foo.Foo = 11
), это может изменить Foo глобально (зависит от языка, т. е. Java или типаprocedure findMin(x, y, z: integer;
var m Паскаля: integer);
). Однако, если язык позволяет обойти процесс разыменования, вы можете изменить его47
, скажем, на49
. В этот момент кажется, что Foo изменился, если вы прочитали его, потому что вы изменили локальный указатель на него. И если вы захотите изменить этот Foo внутри метода (Foo = 12
), вы, вероятно, откажетесь от выполнения программы (aka. Segfault), поскольку вы будете писать в память, отличную от ожидаемой, вы даже можете изменить область, предназначенную для хранения исполняемого файла. Программа и запись в нее изменят выполняемый код (Foo сейчас нет47
). НО ценность Фу47
не изменился глобально, только один внутри метода, потому что47
был также копией метода.223
внутри метода, он создает тот же хаос, что и в 3. или 4. (указатель, указывающий на неверное значение, которое снова используется в качестве указателя), но это все еще локальный проблема, так как 223 было скопировано . Однако, если вы сможете разыменоватьRef2Foo
(то есть223
), достигнуть и изменить указанное значение47
, скажем, чтобы49
, это повлияет на Foo глобально , потому что в этом случае методы получили копию,223
но указанная ссылка47
существует только один раз, и изменение этого чтобы49
приведет каждоеRef2Foo
двойное разыменование к неправильному значению.Не обращая внимания на незначительные детали, даже языки, которые передают по ссылке, будут передавать значения в функции, но эти функции знают, что они должны использовать его для разыменования. Эта передача-ссылка-как-значение просто скрыта от программиста, потому что она практически бесполезна, а терминология - только передача по ссылке .
Строгая передача по значению также бесполезна, это будет означать, что 100-мегабайтный массив должен копироваться каждый раз, когда мы вызываем метод с массивом в качестве аргумента, поэтому Java не может строго передаваться по значению. Каждый язык передает ссылку на этот огромный массив (в качестве значения) и использует механизм копирования при записи, если этот массив можно изменить локально внутри метода, или позволяет методу (как это делает Java) изменять массив глобально (из представление вызывающего абонента) и несколько языков позволяет изменять значение самой ссылки.
Короче говоря, и в собственной терминологии Java, Java является передачей по значению, где значением может быть: либо реальное значение, либо значение , представляющее ссылку .
источник
addr
нетипизированной,@
с типом, и вы можете изменить указанную переменную позже (кроме локально скопированных). Но я не вижу смысла, почему вы это сделали. Этот клочок бумаги в вашем примере (объект № 24601) является справочным, его цель - помочь найти массив в памяти, он не содержит в себе никаких данных массива. Если вы перезапустите свою программу, тот же массив может получить другой идентификатор объекта, даже если его содержимое будет таким же, как и в предыдущем запуске.AtomicReference
и не раскрывает ни ссылку, ни ее цель, но вместо этого включает методы для выполнения действий с целью; как только код, на который был передан объект, возвращается,AtomicReference
[на который его создатель хранил прямую ссылку] должен быть признан недействительным и отменен. Это обеспечило бы правильную семантику, но это было бы медленно и неприлично.Java это вызов по значению
Как это работает
Вы всегда передаете копию битов значения ссылки!
Если это примитивный тип данных, эти биты содержат значение самого примитивного типа данных, поэтому, если мы изменим значение заголовка внутри метода, то это не будет отражать изменения снаружи.
Если это тип данных объекта, такой как Foo, foo = new Foo (), то в этом случае копия адреса объекта проходит как ярлык файла, предположим, что у нас есть текстовый файл abc.txt в C: \ desktop, и предположим, что мы сделали ярлык тот же файл и поместите его в C: \ desktop \ abc-ярлык, чтобы при доступе к файлу из C: \ desktop \ abc.txt и записи «Переполнение стека» и закрытии файла снова открывался файл из ярлыка, затем вы напишите «самое большое онлайн-сообщество для изучения программистами», тогда общее изменение файла будет «Stack Overflow - это крупнейшее онлайн-сообщество для обучения программистов»это означает, что не имеет значения, откуда вы открываете файл, каждый раз, когда мы обращались к одному и тому же файлу, здесь мы можем принять Foo в качестве файла и предположить, что foo хранится в 123hd7h (исходный адрес, такой как C: \ desktop \ abc.txt ) address и 234jdid (скопированный адрес, такой как C: \ desktop \ abc-ярлык, который на самом деле содержит оригинальный адрес файла внутри). Так что для лучшего понимания создайте ярлык файла и почувствуйте ..
источник
Уже есть отличные ответы, которые охватывают это. Я хотел внести небольшой вклад, поделившись очень простым примером (который будет скомпилирован), сравнивающим поведение между передачей по ссылке в c ++ и передачей по значению в Java.
Несколько моментов:
C ++ передаем по справочному примеру:
Java передает "Java-ссылку" на примере значения
РЕДАКТИРОВАТЬ
Несколько человек написали комментарии, которые, кажется, указывают, что либо они не смотрят на мои примеры, либо не получают пример c ++. Не уверен, где разъединение, но угадать пример с ++ не ясно. Я выкладываю тот же пример на паскале, потому что думаю, что переход по ссылке выглядит на Паскале чище, но я могу ошибаться. Я мог бы просто запутывать людей больше; Надеюсь нет.
В паскале параметры, передаваемые по ссылке, называются «параметрами вар». В процедуре setToNil ниже обратите внимание на ключевое слово «var», которое предшествует параметру «ptr». Когда указатель передается этой процедуре, он передается по ссылке . Обратите внимание на поведение: когда эта процедура устанавливает для ptr значение nil (это говорит на паскале для NULL), она устанавливает для аргумента значение nil - вы не можете сделать это в Java.
РЕДАКТИРОВАТЬ 2
Некоторые выдержки из «Языка программирования Java» Кена Арнольда, Джеймса Гослинга (парня, который изобрел Java) и Дэвида Холмса, глава 2, раздел 2.6.5
Он продолжает делать то же самое в отношении объектов. , ,
И ближе к концу того же раздела он делает более широкое утверждение о том, что java передается только по значению и никогда не передается по ссылке.
В этом разделе книги дается отличное объяснение передачи параметров в Java, а также различия между передачей по ссылке и передачей по значению, созданной Java. Я бы посоветовал любому прочитать его, особенно если вы все еще не убеждены.
Я думаю, что разница между этими двумя моделями очень тонкая, и если вы не занимались программированием, где фактически использовали переход по ссылке, легко пропустить, где две модели отличаются.
Я надеюсь, что это решит спор, но, вероятно, не будет.
РЕДАКТИРОВАТЬ 3
Я мог бы быть немного одержим этим постом. Вероятно, потому что я чувствую, что создатели Java непреднамеренно распространяют дезинформацию. Если бы вместо использования слова «ссылка» для указателей они использовали что-то еще, скажем, Dingleberry, не было бы никаких проблем. Вы можете сказать: «Java передает dingleberry по значению, а не по ссылке», и никто не будет смущен.
Вот почему только разработчики Java имеют проблемы с этим. Они смотрят на слово «ссылка» и думают, что точно знают, что это значит, поэтому они даже не удосуживаются рассмотреть противоположный аргумент.
Во всяком случае, я заметил комментарий в более старом посте, который сделал аналогию с воздушным шаром, которая мне действительно понравилась. Настолько, что я решил склеить несколько клипов, чтобы сделать набор мультфильмов, чтобы проиллюстрировать это.
Передача ссылки по значению - Изменения в ссылку не отражаются в области видимости вызывающего, но изменения в объекте. Это потому, что ссылка копируется, но и оригинал, и копия ссылаются на один и тот же объект.
Передача по ссылке - Копия ссылки отсутствует. Одиночная ссылка совместно используется как вызывающей, так и вызываемой функцией. Любые изменения в ссылке или данных объекта отражаются в области действия вызывающей стороны.
РЕДАКТИРОВАТЬ 4
Я видел посты на эту тему, которые описывают низкоуровневую реализацию передачи параметров в Java, что я считаю замечательным и очень полезным, потому что оно делает абстрактную идею конкретной. Однако для меня вопрос скорее в поведении, описанном в спецификации языка, чем в технической реализации поведения. Это выдержка из спецификации языка Java, раздел 8.4.1 :
Это означает, что java создает копию переданных параметров перед выполнением метода. Как и большинство людей , которые изучали компилятор в колледже, я использовал «Книгу дракона» , который составители книга. В главе 1 есть хорошее описание «Call-by-value» и «Call-by-Reference». Описание Call-by-value точно соответствует спецификациям Java.
Еще в 90-е годы, когда я изучал компиляторы, я использовал первое издание книги 1986 года, которое предшествовало Java примерно на 9 или 10 лет. Тем не менее, я только что натолкнулся на копию 2- й редакции 2007 года, в которой на самом деле упоминается Java! Раздел 1.6.6, озаглавленный «Механизмы передачи параметров», описывает передачу параметров довольно красиво. Вот выдержка под заголовком «Call-by-value», в которой упоминается Java:
источник
Насколько я знаю, Java знает только вызов по значению. Это означает, что для примитивных типов данных вы будете работать с копией, а для объектов вы будете работать с копией ссылки на объекты. Однако я думаю, что есть некоторые подводные камни; например, это не будет работать:
Это заполнит Hello World, а не World Hello, потому что в функции подкачки вы используете copys, которые не влияют на ссылки в основном. Но если ваши объекты не являются неизменяемыми, вы можете изменить это, например:
Это заполнит Hello World в командной строке. Если вы измените StringBuffer на String, он выдаст только Hello, потому что String неизменен. Например:
Однако вы можете создать оболочку для String, например, такую, которая позволит использовать ее со строками:
редактировать: я считаю, что это также причина для использования StringBuffer, когда дело доходит до «добавления» двух строк, потому что вы можете изменить исходный объект, что вы не можете с неизменными объектами, такими как String.
источник
swap(a, b)
которая (1) меняет местамиa
иb
из POV вызывающей стороны, (2) не зависит от типа в той степени, в которой это позволяет статическая типизация (то есть использование ее с другим типом не требует ничего, кроме изменения объявленных типовa
иb
) и (3) не требует, чтобы вызывающая сторона явно передавала указатель или имя, тогда язык поддерживает передачу по ссылке.Нет, это не передача по ссылке.
Java передается по значению в соответствии со спецификацией языка Java:
источник
Позвольте мне попытаться объяснить мое понимание с помощью четырех примеров. Java передается по значению, а не по ссылке
/ **
Передать по значению
В Java все параметры передаются по значению, т. Е. Назначение аргумента метода не отображается вызывающей стороне.
* /
Пример 1:
Результат
Пример 2:
/ ** * * Передать по значению * * /
Результат
Пример 3:
/ ** Этот «Pass By Value» имеет ощущение «Pass By Reference»
Некоторые люди говорят, что примитивные типы и «String» - это «передача по значению», а объекты - «передача по ссылке».
Но из этого примера мы можем понять, что это только передача по значению, имея в виду, что здесь мы передаем ссылку как значение. т.е. ссылка передается по значению. Вот почему могут измениться, и все же это справедливо после локальной области. Но мы не можем изменить фактическую ссылку за пределы исходной области. что это означает, продемонстрировано на следующем примере PassByValueObjectCase2.
* /
Результат
Пример 4:
/ **
В дополнение к тому, что было упомянуто в примере 3 (PassByValueObjectCase1.java), мы не можем изменить фактическую ссылку за пределы исходной области. "
Примечание: я не вставляю код для
private class Student
. Определение класса дляStudent
такое же, как в примере 3.* /
Результат
источник
Вы никогда не можете перейти по ссылке в Java, и один из очевидных способов - это когда вы хотите вернуть более одного значения из вызова метода. Рассмотрим следующий фрагмент кода на C ++:
Иногда вы хотите использовать тот же шаблон в Java, но не можете; по крайней мере, не напрямую. Вместо этого вы можете сделать что-то вроде этого:
Как было объяснено в предыдущих ответах, в Java вы передаете указатель на массив как значение в
getValues
. Этого достаточно, потому что метод затем модифицирует элемент массива, и по соглашению вы ожидаете, что элемент 0 будет содержать возвращаемое значение. Очевидно, что вы можете сделать это другими способами, такими как структурирование вашего кода, так что в этом нет необходимости, или создание класса, который может содержать возвращаемое значение или разрешить его установку. Но простой шаблон, доступный вам в C ++ выше, недоступен в Java.источник
Я думал, что добавлю этот ответ, чтобы добавить больше деталей из Спецификаций.
Во-первых, в чем разница между передачей по ссылке и передачей по значению?
Или из википедии, на тему передачи по ссылке
И на предмет передачи по стоимости
Во-вторых, нам нужно знать, что Java использует в своих вызовах методов. В языке спецификация Java состояния
Таким образом, он присваивает (или связывает) значение аргумента соответствующей переменной параметра.
Какова ценность аргумента?
Давайте рассмотрим ссылочные типы, в Java Virtual Machine Specification состояния
Спецификация языка Java также говорится ,
Значением аргумента (некоторого ссылочного типа) является указатель на объект. Обратите внимание, что переменная, вызов метода с возвращаемым типом ссылочного типа и выражение создания экземпляра (
new ...
) разрешаются в значение ссылочного типа.Так
все связывают значение ссылки на
String
экземпляр для вновь созданного параметра метода, вparam
. Это именно то, что описывает определение передачи по значению. Таким образом, Java передается по значению .Тот факт, что вы можете следовать по ссылке, чтобы вызвать метод или получить доступ к полю ссылочного объекта, совершенно не имеет отношения к беседе. Определение передачи по ссылке было
В Java изменение переменной означает ее переназначение. В Java, если вы переназначите переменную в методе, она останется незамеченной для вызывающей стороны. Модификация объекта, на который ссылается переменная, совершенно другая концепция.
Примитивные значения также определены в Спецификации виртуальной машины Java, здесь . Значением типа является соответствующее целочисленное значение или значение с плавающей запятой, кодированное соответствующим образом (8, 16, 32, 64 и т. Д. Биты).
источник
В Java только ссылки передаются и передаются по значению:
Все аргументы Java передаются по значению (ссылка используется при использовании метода):
В случае примитивных типов поведение Java простое: значение копируется в другой экземпляр примитивного типа.
В случае объектов это тоже самое: переменные объекта - это указатели (сегменты), содержащие только адрес объекта, который был создан с использованием ключевого слова «new», и копируются как примитивные типы.
Поведение может отличаться от примитивных типов: поскольку скопированная переменная объекта содержит один и тот же адрес (для одного и того же объекта). Содержимое / члены объекта все еще могут быть изменены внутри метода, а затем и доступ снаружи, создавая иллюзию, что сам (содержащий) Объект был передан по ссылке.
«Строковые» объекты кажутся хорошим контрпримером к городской легенде о том, что «объекты передаются по ссылке»:
Фактически, используя метод, вы никогда не сможете обновить значение строки, переданной в качестве аргумента:
String Object, содержит символы в массиве, объявленном как final, который нельзя изменить. Только адрес объекта может быть заменен другим, используя «новый». Использование «new» для обновления переменной не позволит получить доступ к объекту извне, поскольку переменная изначально была передана по значению и скопирована.
источник
strParam.setChar ( i, newValue )
. Тем не менее, строки, как и все остальные, передаются по значению, и, поскольку String является не примитивным типом, это значение является ссылкой на объект, созданный с помощью new, и вы можете проверить это с помощью String.intern (). ,Различие, или, может быть, просто то, что я помню, как у меня было такое же впечатление, как у оригинального плаката, заключается в следующем: Java всегда передается по значению. Все объекты (в Java, все, кроме примитивов) в Java являются ссылками. Эти ссылки передаются по значению.
источник
Как уже упоминали многие люди, Java всегда передается по значению
Вот еще один пример, который поможет вам понять разницу ( классический пример обмена ):
Печать:
Это происходит потому, что iA и iB являются новыми локальными ссылочными переменными, которые имеют одинаковое значение переданных ссылок (они указывают на a и b соответственно). Таким образом, попытка изменить ссылки iA или iB изменится только в локальной области, а не вне этого метода.
источник
У Java есть только передача по значению. Очень простой пример, чтобы подтвердить это.
источник
obj
(null
) было переданоinit
, а не ссылка наobj
.Я всегда думаю об этом как о «проходе копией». Это копия значения, будь то примитив или ссылка. Если это примитив, то это копия битов, являющихся значением, а если это объект, то это копия ссылки.
вывод Java PassByCopy:
Примитивные классы-обертки и строки неизменны, поэтому любой пример, использующий эти типы, не будет работать так же, как другие типы / объекты.
источник
В отличие от некоторых других языков, Java не позволяет выбирать между передачей по значению и передачей по ссылке - все аргументы передаются по значению. Вызов метода может передавать в метод два типа значений - копии примитивных значений (например, значений типа int и double) и копии ссылок на объекты.
Когда метод изменяет параметр типа примитива, изменения параметра не влияют на исходное значение аргумента в вызывающем методе.
Когда дело доходит до объектов, сами объекты не могут быть переданы в методы. Таким образом, мы передаем ссылку (адрес) объекта. Мы можем манипулировать исходным объектом, используя эту ссылку.
Как Java создает и сохраняет объекты: когда мы создаем объект, мы сохраняем адрес объекта в ссылочной переменной. Давайте проанализируем следующее утверждение.
«Account account1» - это тип и имя ссылочной переменной, «=» - оператор присваивания, «new» запрашивает необходимый объем пространства в системе. Конструктор справа от ключевого слова new, который создает объект, неявно вызывается ключевым словом new. Адрес созданного объекта (результат правильного значения, которое является выражением, называемым «выражением создания экземпляра класса») присваивается левому значению (которое является ссылочной переменной с указанным именем и типом) с помощью оператора присвоения.
Хотя ссылка на объект передается по значению, метод все же может взаимодействовать с указанным объектом, вызывая его открытые методы, используя копию ссылки на объект. Поскольку ссылка, сохраненная в параметре, является копией ссылки, переданной в качестве аргумента, параметр в вызываемом методе и аргумент в вызывающем методе ссылаются на один и тот же объект в памяти.
Передача ссылок на массивы вместо самих объектов массива имеет смысл с точки зрения производительности. Поскольку в Java все передается по значению, при передаче объектов массива будет передана копия каждого элемента. Для больших массивов это приведет к потере времени и значительному хранению копий элементов.
На рисунке ниже вы можете видеть, что у нас есть две ссылочные переменные (они называются указателями в C / C ++, и я думаю, что этот термин облегчает понимание этой функции.) В методе main. Примитивные и эталонные переменные хранятся в памяти стека (левая сторона на изображениях ниже). ссылочные переменные array1 и array2 "точка" (как ее называют программисты C / C ++) или ссылки на массивы a и b соответственно, которые являются объектами (значения, которые эти ссылочные переменные содержат адреса объектов) в динамической памяти (справа на изображениях ниже) ,
Если мы передаем значение ссылочной переменной array1 в качестве аргумента методу reverseArray, в методе создается ссылочная переменная, и эта ссылочная переменная начинает указывать на тот же массив (a).
Итак, если мы скажем
в методе reverseArray он внесет изменение в массив a.
У нас есть другая ссылочная переменная в методе reverseArray (array2), которая указывает на массив c. Если бы мы сказали
в методе reverseArray ссылочная переменная array1 в методе reverseArray перестает указывать на массив a и начинает указывать на массив c (пунктирная линия на втором изображении).
Если мы возвращаем значение ссылочной переменной array2 в качестве возвращаемого значения метода reverseArray и присваиваем это значение ссылочной переменной array1 в main методе, array1 в main начнет указывать на массив c.
Итак, давайте напишем все, что мы сделали сразу.
И теперь, когда метод reverseArray завершен, его ссылочные переменные (array1 и array2) исчезли. Это означает, что теперь у нас есть только две ссылочные переменные в основном методе array1 и array2, которые указывают на массивы c и b соответственно. Ссылочная переменная не указывает на объект (массив) a. Так что он имеет право на сборку мусора.
Вы также можете присвоить значение array2 в main для array1. массив1 начнет указывать на б.
источник
Я создал ветку, посвященную этим вопросам для любых языков программирования здесь .
Ява также упоминается . Вот краткое резюме:
источник
Короче говоря, у объектов 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 ++.источник