Уже есть отличные ответы, которые охватывают это. Я хотел внести небольшой вклад, поделившись очень простым примером (который будет скомпилирован), сравнивающим поведение между передачей по ссылке в c ++ и передачей по значению в Java.
Несколько моментов:
- Термин «ссылка» перегружен двумя значениями. В Java это просто означает указатель, но в контексте «Передачи по ссылке» это означает дескриптор исходной переменной, которая была передана.
- Java это передача по значению . Java является потомком C (среди других языков). До C некоторые (но не все) более ранние языки, такие как FORTRAN и COBOL, поддерживали PBR, но C - нет. PBR позволил этим другим языкам вносить изменения в передаваемые переменные внутри подпрограмм. Чтобы выполнить то же самое (т.е. изменить значения переменных внутри функций), программисты на Си передавали указатели на переменные в функции. Языки, вдохновленные C, такие как Java, заимствовали эту идею и продолжают передавать указатели на методы, как это делал C, за исключением того, что Java вызывает свои указатели References. Опять же, это слово используется по-другому, чем в «Pass-By-Reference».
- C ++ допускает передачу по ссылке , объявляя ссылочный параметр с помощью символа «&» (который является тем же символом, который используется для обозначения «адреса переменной» как в C, так и в C ++). Например, если мы передаем указатель по ссылке, параметр и аргумент не просто указывают на один и тот же объект. Скорее, это одна и та же переменная. Если один из них настроен на другой адрес или имеет значение NULL, то же самое можно сказать и о другом.
- В приведенном ниже примере C ++ я передаю указатель на завершенную нулем строку по ссылке . И в приведенном ниже примере с Java я передаю ссылку Java на строку (опять же, как указатель на строку) по значению. Обратите внимание на вывод в комментариях.
C ++ передаем по справочному примеру:
using namespace std;
#include <iostream>
void change (char *&str){ // the '&' makes this a reference parameter
str = NULL;
}
int main()
{
char *str = "not Null";
change(str);
cout<<"str is " << str; // ==>str is <null>
}
Java передает "Java-ссылку" на примере значения
public class ValueDemo{
public void change (String str){
str = null;
}
public static void main(String []args){
ValueDemo vd = new ValueDemo();
String str = "not null";
vd.change(str);
System.out.println("str is " + str); // ==> str is not null!!
// Note that if "str" was
// passed-by-reference, it
// WOULD BE NULL after the
// call to change().
}
}
РЕДАКТИРОВАТЬ
Несколько человек написали комментарии, которые, кажется, указывают, что либо они не смотрят на мои примеры, либо не получают пример c ++. Не уверен, где разъединение, но угадать пример с ++ не ясно. Я выкладываю тот же пример на паскале, потому что думаю, что переход по ссылке выглядит на Паскале чище, но я могу ошибаться. Я мог бы просто запутывать людей больше; Надеюсь нет.
В паскале параметры, передаваемые по ссылке, называются «параметрами вар». В процедуре setToNil ниже обратите внимание на ключевое слово «var», которое предшествует параметру «ptr». Когда указатель передается этой процедуре, он передается по ссылке . Обратите внимание на поведение: когда эта процедура устанавливает для ptr значение nil (это говорит на паскале для NULL), она устанавливает для аргумента значение nil - вы не можете сделать это в Java.
program passByRefDemo;
type
iptr = ^integer;
var
ptr: iptr;
procedure setToNil(var ptr : iptr);
begin
ptr := nil;
end;
begin
new(ptr);
ptr^ := 10;
setToNil(ptr);
if (ptr = nil) then
writeln('ptr seems to be nil'); { ptr should be nil, so this line will run. }
end.
РЕДАКТИРОВАТЬ 2
Некоторые выдержки из «Языка программирования Java» Кена Арнольда, Джеймса Гослинга (парня, который изобрел Java) и Дэвида Холмса, глава 2, раздел 2.6.5
Все параметры в методы передаются «по значению» . Другими словами, значения переменных параметров в методе являются копиями инициатора, указанного в качестве аргументов.
Он продолжает делать то же самое в отношении объектов. , ,
Вы должны заметить, что когда параметр является ссылкой на объект, передается «по значению» ссылка на объект, а не на сам объект .
И ближе к концу того же раздела он делает более широкое утверждение о том, что java передается только по значению и никогда не передается по ссылке.
Язык программирования Java не передает объекты по ссылке; он
передает ссылки на объекты по значению . Поскольку две копии одной и той же ссылки ссылаются на один и тот же фактический объект, изменения, внесенные через одну ссылочную переменную, видны через другую. Существует ровно один способ передачи параметров - передача по значению - и это помогает упростить процесс.
В этом разделе книги дается отличное объяснение передачи параметров в Java, а также различия между передачей по ссылке и передачей по значению, созданной Java. Я бы посоветовал любому прочитать его, особенно если вы все еще не убеждены.
Я думаю, что разница между этими двумя моделями очень тонкая, и если вы не занимались программированием, где фактически использовали переход по ссылке, легко пропустить, где две модели отличаются.
Я надеюсь, что это решит спор, но, вероятно, не будет.
РЕДАКТИРОВАТЬ 3
Я мог бы быть немного одержим этим постом. Вероятно, потому что я чувствую, что создатели Java непреднамеренно распространяют дезинформацию. Если бы вместо использования слова «ссылка» для указателей они использовали что-то еще, скажем, Dingleberry, не было бы никаких проблем. Вы можете сказать: «Java передает dingleberry по значению, а не по ссылке», и никто не будет смущен.
Вот почему только разработчики Java имеют проблемы с этим. Они смотрят на слово «ссылка» и думают, что точно знают, что это значит, поэтому они даже не удосуживаются рассмотреть противоположный аргумент.
Во всяком случае, я заметил комментарий в более старом посте, который сделал аналогию с воздушным шаром, которая мне действительно понравилась. Настолько, что я решил склеить несколько клипов, чтобы сделать набор мультфильмов, чтобы проиллюстрировать это.
Передача ссылки по значению - Изменения в ссылку не отражаются в области видимости вызывающего, но изменения в объекте. Это потому, что ссылка копируется, но и оригинал, и копия ссылаются на один и тот же объект.
Передача по ссылке - Копия ссылки отсутствует. Одиночная ссылка совместно используется как вызывающей, так и вызываемой функцией. Любые изменения в ссылке или данных объекта отражаются в области действия вызывающей стороны.
РЕДАКТИРОВАТЬ 4
Я видел посты на эту тему, которые описывают низкоуровневую реализацию передачи параметров в Java, что я считаю замечательным и очень полезным, потому что оно делает абстрактную идею конкретной. Однако для меня вопрос скорее в поведении, описанном в спецификации языка, чем в технической реализации поведения. Это выдержка из спецификации языка Java, раздел 8.4.1 :
Когда метод или конструктор вызывают (§15.12), значения фактических выражений аргумента инициализируют вновь созданные переменные параметра, каждый из объявленного типа, перед выполнением тела метода или конструктора. Идентификатор, который появляется в DeclaratorId, может использоваться как простое имя в теле метода или конструктора для ссылки на формальный параметр.
Это означает, что 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:
В вызове по значению фактический параметр оценивается (если это выражение) или копируется (если это переменная). Значение помещается в местоположение, принадлежащее соответствующему формальному параметру вызываемой процедуры. Этот метод используется в C и Java и является распространенным вариантом в C ++, а также в большинстве других языков.