Já existem ótimas respostas que cobrem isso. Eu queria fazer uma pequena contribuição compartilhando um exemplo muito simples (que será compilado) contrastando os comportamentos entre Passagem por referência em c ++ e Passagem por valor em Java.
Alguns pontos:
- O termo "referência" é uma sobrecarga com dois significados separados. Em Java, significa simplesmente um ponteiro, mas no contexto de "Passagem por referência", significa um identificador para a variável original que foi passada.
- Java é Passagem por valor . Java é descendente de C (entre outras linguagens). Antes de C, vários idiomas (mas não todos) anteriores, como FORTRAN e COBOL, suportavam PBR, mas C não. O PBR permitiu que esses outros idiomas fizessem alterações nas variáveis passadas nas sub-rotinas. Para realizar a mesma coisa (ou seja, alterar os valores das variáveis dentro das funções), os programadores C passaram os ponteiros para as variáveis para as funções. Idiomas inspirados em C, como Java, pegaram emprestada essa idéia e continuam passando o ponteiro para métodos como C, exceto que Java chama seus ponteiros de Referências. Novamente, esse é um uso diferente da palavra "Referência" do que em "Passagem por Referência".
- O C ++ permite a passagem por referência declarando um parâmetro de referência usando o caractere "&" (que passa a ser o mesmo caractere usado para indicar "o endereço de uma variável" em C e C ++). Por exemplo, se passarmos um ponteiro por referência, o parâmetro e o argumento não estarão apenas apontando para o mesmo objeto. Em vez disso, eles são a mesma variável. Se um é definido para um endereço diferente ou nulo, o outro também.
- No exemplo C ++ abaixo, estou passando um ponteiro para uma seqüência terminada nula por referência . E no exemplo de Java abaixo, estou passando uma referência de Java para uma String (novamente, o mesmo que um ponteiro para uma String) por valor. Observe a saída nos comentários.
C ++ passa pelo exemplo de referência:
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>
}
Exemplo do Java Pass "a Java Reference" por valor
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().
}
}
EDITAR
Várias pessoas escreveram comentários que parecem indicar que eles não estão vendo meus exemplos ou não recebem o exemplo c ++. Não tenho certeza de onde está a desconexão, mas adivinhar o exemplo de c ++ não está claro. Estou postando o mesmo exemplo em pascal porque acho que a passagem por referência parece mais limpa em pascal, mas posso estar errado. Eu poderia estar confundindo mais as pessoas; Espero que não.
No pascal, os parâmetros passados por referência são chamados "parâmetros var". No procedimento setToNil abaixo, observe a palavra-chave 'var' que precede o parâmetro 'ptr'. Quando um ponteiro é passado para esse procedimento, ele é passado por referência . Observe o comportamento: quando este procedimento define ptr como nulo (isso é pascal speak for NULL), ele define o argumento como nulo - você não pode fazer isso em 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
Alguns trechos de "THE Java Programming Language" de Ken Arnold, James Gosling (o cara que inventou o Java) e David Holmes, capítulo 2, seção 2.6.5
Todos os parâmetros para métodos são passados "por valor" . Em outras palavras, os valores das variáveis de parâmetro em um método são cópias do invocador especificado como argumentos.
Ele continua fazendo o mesmo ponto em relação aos objetos. . .
Você deve observar que, quando o parâmetro é uma referência a objeto, é a referência a objeto - e não o próprio objeto - que é passado "por valor" .
E no final da mesma seção, ele faz uma declaração mais ampla sobre o java sendo apenas passado por valor e nunca por referência.
A linguagem de programação Java não passa objetos por referência; ele
passa referências de objeto por valor . Como duas cópias da mesma referência se referem ao mesmo objeto real, as alterações feitas através de uma variável de referência são visíveis na outra. Existe exatamente um parâmetro que passa de modo - passa por valor - e isso ajuda a manter as coisas simples.
Esta seção do livro tem uma ótima explicação sobre a passagem de parâmetros em Java e a distinção entre passagem por referência e passagem por valor e é do criador do Java. Eu encorajaria qualquer um a ler, especialmente se você ainda não está convencido.
Eu acho que a diferença entre os dois modelos é muito sutil e, a menos que você tenha feito a programação em que realmente utilizou a passagem por referência, é fácil perder a diferença entre dois modelos.
Espero que isso resolva o debate, mas provavelmente não.
EDIT 3
Eu posso estar um pouco obcecado com este post. Provavelmente porque sinto que os fabricantes de Java inadvertidamente espalharam desinformação. Se, em vez de usar a palavra "referência" para ponteiros, eles tivessem usado outra coisa, digamos dingleberry, não haveria problema. Você poderia dizer: "Java transmite os frutos silvestres por valor e não por referência", e ninguém ficaria confuso.
Essa é a razão pela qual apenas os desenvolvedores Java têm problemas com isso. Eles olham para a palavra "referência" e pensam que sabem exatamente o que isso significa, para que nem se preocupem em considerar o argumento oposto.
De qualquer forma, notei um comentário em um post antigo, que fez uma analogia de balão que eu realmente gostei. Tanto que decidi colar alguns clip-art para fazer um conjunto de desenhos animados para ilustrar o ponto.
Passando uma referência por valor - As alterações na referência não são refletidas no escopo do chamador, mas as alterações no objeto. Isso ocorre porque a referência é copiada, mas o original e a cópia se referem ao mesmo objeto.
Passar por referência - Não há cópia da referência. A referência única é compartilhada pelo chamador e pela função que está sendo chamada. Quaisquer alterações na referência ou nos dados do objeto são refletidas no escopo do chamador.
EDIT 4
Eu já vi posts sobre esse tópico que descrevem a implementação de baixo nível da passagem de parâmetros em Java, o que eu acho ótimo e muito útil porque torna concreta uma idéia abstrata. No entanto, para mim, a pergunta é mais sobre o comportamento descrito na especificação da linguagem do que sobre a implementação técnica do comportamento. Este é um esforço da Especificação da Linguagem Java, seção 8.4.1 :
Quando o método ou construtor é chamado (§15.12), os valores das expressões de argumento reais inicializam variáveis de parâmetro recém-criadas, cada uma do tipo declarado, antes da execução do corpo do método ou construtor. O identificador que aparece no DeclaratorId pode ser usado como um nome simples no corpo do método ou construtor para se referir ao parâmetro formal.
O que significa que o java cria uma cópia dos parâmetros passados antes de executar um método. Como a maioria das pessoas que estudaram compiladores na faculdade, eu usei "O Livro Dragon" que é O livro compiladores. Ele possui uma boa descrição de "Chamada por valor" e "Chamada por referência" no Capítulo 1. A descrição da Chamada por valor corresponde exatamente às especificações Java.
Quando eu estudei compiladores - nos anos 90, usei a primeira edição do livro de 1986, que antecedia o Java por cerca de 9 ou 10 anos. No entanto, acabei de encontrar uma cópia da 2ª Edição de 2007, que na verdade menciona Java! A Seção 1.6.6, denominada "Mecanismos de passagem de parâmetros", descreve a passagem de parâmetros muito bem. Aqui está um trecho sob o título "Chamada por valor", que menciona Java:
Na chamada por valor, o parâmetro real é avaliado (se for uma expressão) ou copiado (se for uma variável). O valor é colocado no local pertencente ao parâmetro formal correspondente do procedimento chamado. Este método é usado em C e Java e é uma opção comum em C ++, assim como na maioria das outras linguagens.