Вже є чудові відповіді, які висвітлюють це. Я хотів зробити невеликий внесок, поділившись дуже простим прикладом (який складе), протиставляючи поведінку між Pass-by-reference у c ++ та Pass-by-value на Java.
Кілька пунктів:
- Термін "посилання" - це перевантажене двома окремими значеннями. У Java це просто означає вказівник, але в контексті "Pass-by-reference" це означає ручку до оригінальної змінної, яка була передана в.
- Java є Pass-by-value . Java - нащадок С (серед інших мов). До C декілька (але не всі) попередніх мов, як FORTRAN та COBOL підтримували PBR, але C - не. PBR дозволив цим іншим мовам вносити зміни до переданих змінних всередині підпрограм. Для того, щоб виконати те саме (тобто змінити значення змінних всередині функцій), програмісти C передавали покажчики на змінні у функції. Мови, натхнені C, наприклад, Java, запозичили цю ідею і продовжують передавати покажчик методам, як це робив C, за винятком того, що Java називає свої вказівники Посиланнями. Знову ж таки, це слово вживання "Посилання" по-іншому, ніж у "Pass-By-Reference".
- C ++ дозволяє пропускати посилання шляхом оголошення опорного параметра за допомогою символу "&" (який трапляється тим самим символом, який використовується для позначення "адреси змінної" як у C, так і в C ++). Наприклад, якщо ми передаємо вказівник за посиланням, параметр і аргумент не просто вказують на один і той же об'єкт. Скоріше, вони однакові. Якщо один встановлюється на іншу адресу або на нуль, це робить і інший.
- У наведеному нижче прикладі C ++ я передаю вказівник на нульову закінчену рядок за посиланням . І в наведеному нижче прикладі Java я передаю посилання Java на String (знову ж, те саме, що вказівник на String) за значенням. Помітьте результат у коментарях.
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().
}
}
EDIT
Кілька людей написали коментарі, які, схоже, вказують на те, що або вони не дивляться на мої приклади, або не отримують приклад c ++. Не впевнений, де знаходиться відключення, але вгадати приклад c ++ не зрозуміло. Я розміщую той самий приклад у мові pascal, тому що я думаю, що пропуск посилання виглядає чистішим у Pascal, але я можу помилитися. Я можу просто більше бентежити людей; Я сподіваюся, що не.
У паскалі параметри, передані посиланням, називаються "параметрами var". У нижченаведеній процедурі setToNil зверніть увагу на ключове слово "var", яке передує параметру "ptr". Коли вказівник передається на цю процедуру, він передається за посиланням . Зверніть увагу на поведінку: коли ця процедура встановлює ptr до нуля (це паскаль говорить для NULL), вона встановить аргумент на нуль - цього не можна робити на 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.
EDIT 2
Деякі уривки «Мова програмування Java» Кен Арнольд, Джеймс Гослінг (хлопець, який винайшов Java) та Девід Холмс, глава 2, розділ 2.6.5
Усі параметри методам передаються "за значенням" . Іншими словами, значення змінних параметрів у методі є копіями збудника, вказаними як аргументи.
Він продовжує робити те саме, що стосується предметів. . .
Ви повинні зауважити, що коли параметр є посиланням на об'єкт, "значення" передається саме посиланням на об'єкт, а не самим об'єктом .
І наприкінці цього ж розділу він робить більш широке твердження про те, що java є лише передачею за значенням і ніколи не проходить через посилання.
Мова програмування Java не передає об'єкти за посиланням; він
передає посилання на об'єкт за значенням . Оскільки дві копії одного і того ж посилання стосуються одного і того ж фактичного об'єкта, зміни, зроблені через одну опорну змінну, видно через іншу. Існує рівно один параметр, що проходить режим переходу за значенням, і це допомагає робити прості речі.
У цьому розділі книги є велике пояснення проходження параметрів на Java та різниці між прохідними посиланнями та прохідними значеннями, а також розробниками Java. Я б закликав когось прочитати його, особливо якщо ви все ще не впевнені.
Я думаю, що різниця між цими двома моделями дуже тонка, і якщо ви не програмували там, де ви фактично використовували пропускний посилання, легко не помітити, де дві моделі відрізняються.
Я сподіваюся, що це врегулює дискусію, але, мабуть, не буде.
EDIT 3
Я, можливо, трохи одержимий цією посадою. Можливо, тому, що я відчуваю, що виробники Java ненавмисно поширюють дезінформацію. Якби замість того, щоб використовувати вказівники слово "посилання", вони використовували щось інше, скажімо, брусниця, не було б жодних проблем. Можна сказати: "Java передає чорницю за значенням, а не за посиланням", і ніхто не буде плутати.
Ось чому лише розробники Java мають проблеми з цим. Вони дивляться на слово "посилання" і думають, що точно знають, що це означає, тому навіть не намагаються розглянути протилежний аргумент.
У будь-якому випадку я помітив коментар у старшому дописі, який зробив аналогію на повітряній кулі, яка мені дуже сподобалась. Настільки, що я вирішив склеїти разом деякі картинки, щоб зробити набір мультфільмів, щоб проілюструвати суть.
Передача посилання за значенням - Зміни до посилання не відображаються в області виклику, але зміни в об'єкті є. Це тому, що посилання скопійовано, але і оригінал, і копія посилаються на один і той же об'єкт.
Перейти за посиланням - копії посилання немає. Єдина посилання поділяється як абонентом, так і функцією, що викликається. Будь-які зміни в посиланні або на дані об'єкта відображаються в області дії абонента.
РЕДАКТ 4
Я бачив публікації на цю тему, в яких описується низький рівень реалізації параметрів, що проходять через Java, що, на мою думку, є великим і дуже корисним, оскільки це робить конкретну абстрактну ідею. Однак для мене питання стосується скоріше поведінки, описаної в мовній специфікації, ніж про технічну реалізацію поведінки. Це вказівка зі специфікації мови Java, розділ 8.4.1 :
Коли метод або конструктор викликається (§15.12), значення фактичних виразів аргументу ініціалізують щойно створені змінні параметра, кожне із заявленого типу, перед виконанням тіла методу чи конструктора. Ідентифікатор, який з'являється в DeclaratorId, може використовуватися як просте ім'я в тілі методу чи конструктора для позначення формального параметра.
Що означає, java створює копію переданих параметрів перед виконанням методу. Як і більшість людей, які вивчали компіляторів у коледжі, я використав "Книгу Драконів", яка є THE THE компіляторами. У ній є хороший опис "Дзвінок за вартістю" та "Заклик за посиланням" у Розділі 1. Опис за замовчуванням за значенням точно відповідає Java Specs.
Ще коли я вивчав компіляторів - у 90-х я використав перше видання книги з 1986 року, яке раніше датувало Яву приблизно на 9 або 10 років. Однак я просто наткнувся на копію другого видання з 2007 року, де фактично згадується Java! Розділ 1.6.6 з позначкою "Механізми передачі параметрів" описує параметри проходження досить добре. Ось уривок під заголовком "Call-by-value", де згадується Java:
У значенні виклику фактичний параметр оцінюється (якщо це вираз) або копіюється (якщо він є змінною). Значення розміщується у розташуванні, що належить відповідному формальному параметру викликаної процедури. Цей метод використовується в C та Java, і є поширеним варіантом у C ++, а також у більшості інших мов.