Eu sempre pensei que o Java era passado por referência .
No entanto, eu vi algumas postagens de blog (por exemplo, este blog ) que afirmam que não são.
Acho que não entendo a distinção que eles estão fazendo.
Qual a explicação?
Eu sempre pensei que o Java era passado por referência .
No entanto, eu vi algumas postagens de blog (por exemplo, este blog ) que afirmam que não são.
Acho que não entendo a distinção que eles estão fazendo.
Qual a explicação?
call-by-value
e acall-by-reference
diferença são muito importantes . (Pessoalmente eu prefiro usarcall-by-object-sharing
estes dias maiscall-by-value[-of-the-reference]
, que este descreve a semântica em um alto nível e não cria um conflito comcall-by-value
, que é a implementação subjacente.)swap(x,y)
teste .Respostas:
Java é sempre transmitido por valor . Infelizmente, quando passamos o valor de um objeto, estamos passando a referência a ele. Isso é confuso para iniciantes.
É assim:
No exemplo acima
aDog.getName()
ainda retornará"Max"
. O valoraDog
dentromain
não é alterado na funçãofoo
com aDog
"Fifi"
medida que a referência do objeto é passada por valor. Se fosse passado por referência, oaDog.getName()
inmain
retornaria"Fifi"
após a chamada parafoo
.Da mesma forma:
No exemplo acima,
Fifi
é o nome do cão após a chamada para,foo(aDog)
porque o nome do objeto foi definido dentro defoo(...)
. Quaisquer operações quefoo
executa emd
são tais que, para todos os efeitos práticos, eles são realizados emaDog
, mas é não possível alterar o valor da variávelaDog
em si.fonte
Acabei de perceber que você fez referência ao meu artigo .
A especificação Java diz que tudo em Java é transmitido por valor. Não existe "passagem por referência" em Java.
A chave para entender isso é que algo como
não é um cachorro; é na verdade um ponteiro para um cachorro.
O que isso significa é quando você tem
você está basicamente passando o endereço do
Dog
objeto criado para ofoo
método(Digo essencialmente porque os ponteiros Java não são endereços diretos, mas é mais fácil pensar neles dessa maneira)
Suponha que o
Dog
objeto resida no endereço de memória 42. Isso significa que passamos 42 para o método.se o método fosse definido como
vamos ver o que está acontecendo.
someDog
está definido para o valor 42someDog
é seguido para oDog
que aponta (oDog
objeto no endereço 42)Dog
(aquele no endereço 42) é solicitado a mudar seu nome para MaxDog
é criado. Digamos que ele esteja no endereço 74someDog
a 74Dog
que aponta (oDog
objeto no endereço 74)Dog
(aquele no endereço 74) é solicitado a mudar seu nome para RowlfAgora vamos pensar sobre o que acontece fora do método:
Mudou
myDog
?Aí está a chave.
Tendo em mente que
myDog
é um ponteiro , e não um realDog
, a resposta é NÃO.myDog
ainda tem o valor 42; ainda está apontando para o originalDog
(mas observe que, devido à linha "AAA", seu nome é agora "Max" - ainda é o mesmo cão;myDog
o valor de Dog não mudou.)É perfeitamente válido seguir um endereço e alterar o que está no final dele; isso não altera a variável, no entanto.
Java funciona exatamente como C. Você pode atribuir um ponteiro, passar o ponteiro para um método, seguir o ponteiro no método e alterar os dados apontados. No entanto, você não pode alterar para onde esse ponteiro aponta.
Em C ++, Ada, Pascal e outras linguagens que suportam passagem por referência, você pode realmente alterar a variável que foi passada.
Se o Java tivesse semântica de passagem por referência, o
foo
método que definimos acima teria mudado para ondemyDog
estava apontando quando atribuídosomeDog
na linha BBB.Pense nos parâmetros de referência como aliases para a variável transmitida. Quando esse alias é atribuído, o mesmo ocorre com a variável transmitida.
fonte
Java sempre passa argumentos por valor , NÃO por referência.
Deixe-me explicar isso através de um exemplo :
Vou explicar isso em etapas:
Declarando uma referência nomeada
f
do tipoFoo
e atribuir-lhe um novo objeto do tipoFoo
com um atributo"f"
.Do lado do método, uma referência do tipo
Foo
com um nomea
é declarada e é inicialmente atribuídanull
.À medida que você chama o método
changeReference
,a
será atribuído à referência o objeto que é passado como argumento.Declarando uma referência nomeada
b
do tipoFoo
e atribuir-lhe um novo objeto do tipoFoo
com um atributo"b"
.a = b
faz uma nova atribuição à referênciaa
, nãof
ao objeto cujo atributo é"b"
.Como você chamar
modifyReference(Foo c)
método, uma referênciac
é criada e atribuída o objeto com o atributo"f"
.c.setAttribute("c");
mudará o atributo do objeto que faz referênciac
a ele e é o mesmo objeto que faz referênciaf
a ele.Espero que você entenda agora como a passagem de objetos como argumentos funciona em Java :)
fonte
a
apontar para o mesmo objeto quef
(e nunca conseguir sua própria cópia do objetof
apontar para), quaisquer alterações feitas no objeto feitasa
deverão ser modificadasf
também (já que ambos estão trabalhando com o mesmo objeto) ), portanto, em algum momento, éa
necessário obter sua própria cópia do objetof
para o qual aponta.Isso lhe dará algumas idéias de como o Java realmente funciona, a ponto de, na sua próxima discussão sobre o Java passar por referência ou passar por valor, você apenas sorrirá :-)
Primeiro passo, apague sua mente a palavra que começa com 'p' "_ _ _ _ _ _ _", especialmente se você vem de outras linguagens de programação. Java e 'p' não podem ser escritos no mesmo livro, fórum ou mesmo txt.
A segunda etapa lembra que, quando você passa um Objeto para um método, está passando a referência a Objetos e não o próprio Objeto.
Agora pense no que a referência / variável de um Objeto faz / é:
A seguir (por favor, não tente compilar / executar isso ...):
O que acontece?
Uma imagem vale mais que mil palavras:
Observe que as setas anotherReferenceToTheSamePersonObject são direcionadas para o Object e não para a variável person!
Se você não conseguiu, confie em mim e lembre-se de que é melhor dizer que Java é transmitido por valor . Bem, passe pelo valor de referência . Bem, melhor ainda é passar por cópia do valor variável! ;)
Agora, sinta-se à vontade para me odiar, mas observe que, dado isso, não há diferença entre passar tipos de dados primitivos e Objetos ao falar sobre argumentos de método.
Você sempre passa uma cópia dos bits do valor da referência!
Claro que você pode abreviá-lo e apenas dizer que Java é pass-by-value!
fonte
public void foo(Car car){ ... }
,car
é localfoo
e contém a localização da pilha do objeto? Então, se eu alterarcar
o valor decar = new Car()
, ele apontará para um objeto diferente na pilha? e se eu alterarcar
o valor da propriedade porcar.Color = "Red"
, o Objeto no heap apontado porcar
será modificado. Além disso, é o mesmo em c #? Por favor, responda! Obrigado!System.out.println(person.getName());
o que aparecerá? "Tom" ou "Jerry"? Esta é a última coisa que me ajudará a superar essa confusão.Java é sempre transmitido por valor, sem exceções, nunca .
Então, como é que alguém pode ficar confuso com isso e acreditar que o Java é passado por referência, ou acha que eles têm um exemplo de Java agindo como passado por referência? O ponto principal é que o Java nunca fornece acesso direto aos valores dos próprios objetos , em nenhuma circunstância. O único acesso a objetos é através de uma referência a esse objeto. Como os objetos Java são sempre acessados por meio de uma referência, e não diretamente, é comum falar sobre campos e variáveis e argumentos de método como objetos , quando pedanticamente eles são apenas referências a objetos .A confusão decorre dessa mudança (estritamente falando, incorreta) da nomenclatura.
Então, ao chamar um método
int
,long
, etc.), a passagem pelo valor é o valor real da primitiva (por exemplo, 3).Portanto, se você
doSomething(foo)
epublic void doSomething(Foo foo) { .. }
os dois Foos copiaram referências que apontam para os mesmos objetos.Naturalmente, passar por valor uma referência a um objeto se parece muito (e na prática é indistinguível) a passar um objeto por referência.
fonte
Java passa referências por valor.
Portanto, você não pode alterar a referência que é passada.
fonte
Sinto que discutir sobre "passagem por referência versus passagem por valor" não é muito útil.
Se você disser: "Java é aprovado por qualquer que seja (referência / valor)", em ambos os casos, você não fornecerá uma resposta completa. Aqui estão algumas informações adicionais que, com sorte, ajudarão a entender o que está acontecendo na memória.
Faça um curso intensivo sobre a pilha / pilha antes de chegarmos à implementação do Java: Os valores entram e saem da pilha de uma maneira ordenada, como uma pilha de pratos em uma lanchonete. A memória na pilha (também conhecida como memória dinâmica) é aleatória e desorganizada. A JVM encontra o espaço sempre que pode e o libera, pois as variáveis que o utilizam não são mais necessárias.
OK. Primeiro, as primitivas locais vão para a pilha. Portanto, este código:
resulta nisto:
Quando você declara e instancia um objeto. O objeto real continua na pilha. O que se passa na pilha? O endereço do objeto na pilha. Os programadores de C ++ chamariam isso de ponteiro, mas alguns desenvolvedores de Java são contra a palavra "ponteiro". Tanto faz. Apenas saiba que o endereço do objeto está na pilha.
Igual a:
Uma matriz é um objeto e, portanto, também fica no heap. E o que dizer dos objetos na matriz? Eles obtêm seu próprio espaço de heap e o endereço de cada objeto fica dentro da matriz.
Então, o que é passado quando você chama um método? Se você passa um objeto, o que realmente está passando é o endereço do objeto. Alguns podem dizer o "valor" do endereço e outros dizem que é apenas uma referência ao objeto. Esta é a gênese da guerra santa entre os proponentes "referência" e "valor". O que você chama de não é tão importante quanto você entende que o que está sendo passado é o endereço do objeto.
Uma String é criada e o espaço para ela é alocado na pilha, e o endereço da string é armazenado na pilha e recebe o identificador
hisName
, já que o endereço da segunda String é o mesmo que o primeiro, nenhuma nova String é criada e nenhum novo espaço de heap é alocado, mas um novo identificador é criado na pilha. Então chamamosshout()
: um novo quadro de pilha é criado e um novo identificador,name
é criado e atribuído o endereço da String já existente.Então, valor, referência? Você diz "batata".
fonte
Apenas para mostrar o contraste, compare os seguintes trechos de C ++ e Java :
Em C ++: Nota: Código incorreto - vazamentos de memória! Mas isso demonstra o ponto.
Em Java,
Java possui apenas os dois tipos de passagem: por valor para tipos internos e por valor do ponteiro para tipos de objetos.
fonte
Dog **objPtrPtr
ao exemplo C ++, dessa forma, podemos modificar o que o ponteiro "aponta".Java passa referências a objetos por valor.
fonte
Basicamente, a reatribuição de parâmetros de Objeto não afeta o argumento, por exemplo,
será impresso em
"Hah!"
vez denull
. A razão pela qual isso funciona é porquebar
é uma cópia do valor debaz
, que é apenas uma referência"Hah!"
. Se fosse a própria referência real,foo
teria redefinidobaz
paranull
.fonte
Não acredito que ninguém tenha mencionado Barbara Liskov ainda. Quando ela projetou a CLU, em 1974, encontrou o mesmo problema de terminologia e inventou o termo chamada por compartilhamento (também conhecido como chamada por compartilhamento de objeto e chamada por objeto ) para este caso específico de "chamada por valor em que o valor é uma referência".
fonte
O cerne da questão é que a palavra referência na expressão "passagem por referência" significa algo completamente diferente do significado usual da palavra referência em Java.
Normalmente, em referência a Java significa uma referência a um objeto . Mas os termos técnicos transmitidos por referência / valor da teoria da linguagem de programação estão falando de uma referência à célula de memória que contém a variável , que é algo completamente diferente.
fonte
Em java, tudo é referência; portanto, quando você tem algo como:
Point pnt1 = new Point(0,0);
Java faz o seguinte:Java não passa argumentos de método por referência; passa-os pelo valor. Vou usar o exemplo deste site :
Fluxo do programa:
Criando dois objetos Point diferentes com duas referências diferentes associadas.
Como o resultado esperado será:
Nesta linha, 'passagem por valor' entra em cena ...
Referências
pnt1
epnt2
são passados por valor ao método complicado, o que significa que agora o seu faz referênciapnt1
epnt2
tem ocopies
nomearg1
earg2
.Sopnt1
earg1
aponta para o mesmo objeto. (O mesmo parapnt2
earg2
)No
tricky
método:Avançar no
tricky
métodoAqui, você primeiro cria uma nova
temp
referência de ponto que apontará no mesmo local comoarg1
referência. Então você move a referênciaarg1
para apontar para o mesmo local comoarg2
referência. Finalmentearg2
irá apontar para o mesmo lugar comotemp
.A partir daqui âmbito do
tricky
método é ido e você não tem acesso mais às referências:arg1
,arg2
,temp
. Mas nota importante é que tudo o que você faz com essas referências quando elas estão "na vida" afetará permanentemente o objeto para o qual elas apontam .Então, depois de executar o método
tricky
, quando você retornar aomain
, você terá esta situação:Então agora, a execução completa do programa será:
fonte
arg1.x = 1; arg1.y = 1; arg2.x = 2; arg2.y = 2;
assim, como arg1 agora segurando de refrence PNT2 e arg2 segurando referência agora pnt1, por isso, sua impressãoX1: 2 Y1: 2 X2: 1 Y2: 1
Java é sempre passado por valor, não por referência
Antes de tudo, precisamos entender o que passam por valor e passam por referência.
Passagem por valor significa que você está fazendo uma cópia na memória do valor do parâmetro real que é passado. Esta é uma cópia do conteúdo do parâmetro real .
Passagem por referência (também chamada de passagem por endereço) significa que uma cópia do endereço do parâmetro real é armazenada .
Às vezes, o Java pode dar a ilusão de passar por referência. Vamos ver como funciona usando o exemplo abaixo:
A saída deste programa é:
Vamos entender passo a passo:
Como todos sabemos, ele criará um objeto na pilha e retornará o valor de referência para t. Por exemplo, suponha que o valor de t seja
0x100234
(não sabemos o valor interno real da JVM, este é apenas um exemplo).Ao passar a referência t para a função, ela não passará diretamente o valor de referência real do teste do objeto, mas criará uma cópia de t e a passará para a função. Como está passando por valor , passa uma cópia da variável em vez de sua referência real. Como dissemos que o valor de t era
0x100234
, t e f terão o mesmo valor e, portanto, apontarão para o mesmo objeto.Se você alterar alguma coisa na função usando a referência f, ela modificará o conteúdo existente do objeto. É por isso que obtivemos a saída
changevalue
, que é atualizada na função.Para entender isso mais claramente, considere o seguinte exemplo:
Isso vai dar um
NullPointerException
? Não, porque apenas passa uma cópia da referência. No caso de passar por referência, poderia ter lançado umNullPointerException
, como visto abaixo:Espero que isso ajude.
fonte
Uma referência é sempre um valor quando representada, independentemente do idioma que você usa.
Para obter uma visualização fora da caixa, vejamos o Assembly ou algum gerenciamento de memória de baixo nível. No nível da CPU, uma referência a qualquer coisa imediatamente se torna um valor se for gravada na memória ou em um dos registradores da CPU. (É por isso que ponteiro é uma boa definição. É um valor que tem um objetivo ao mesmo tempo).
Os dados na memória têm um local e nesse local há um valor (byte, palavra, qualquer que seja). No Assembly, temos uma solução conveniente para atribuir um nome a um determinado local (também conhecido como variável), mas ao compilar o código, o assembler simplesmente substitui o Name pelo local designado, assim como o navegador substitui os nomes de domínio pelos endereços IP.
Até o âmago, é tecnicamente impossível passar uma referência a qualquer coisa em qualquer idioma sem representá-lo (quando ele imediatamente se torna um valor).
Digamos que tenhamos uma variável Foo, sua localização está no 47º byte na memória e seu valor é 5. Temos outra variável Ref2Foo que está no 223rd byte na memória e seu valor será 47. Esse Ref2Foo pode ser uma variável técnica , não criado explicitamente pelo programa. Se você apenas observar 5 e 47 sem nenhuma outra informação, verá apenas dois Valores . Se você usá-las como referências, para chegar a
5
nós, temos que viajar:É assim que as tabelas de salto funcionam.
Se quisermos chamar um método / função / procedimento com o valor de Foo, existem algumas maneiras possíveis de passar a variável para o método, dependendo do idioma e de seus vários modos de chamada de método:
Em todos os casos acima de um valor - uma cópia de um valor existente - foi criado, agora cabe ao método de recebimento lidar com ele. Quando você escreve "Foo" dentro do método, ele é lido no EAX ou automaticamente desreferenciado ou duplamente referenciado, o processo depende de como o idioma funciona e / ou de que tipo o Foo determina. Isso fica oculto para o desenvolvedor até que ele contorne o processo de cancelamento de referência. Portanto, uma referência é um valor quando representado, porque uma referência é um valor que deve ser processado (no nível do idioma).
Agora passamos o Foo para o método:
Foo = 9
), isso afeta apenas o escopo local, pois você possui uma cópia do Valor. De dentro do método, não podemos nem determinar onde na memória o Foo original estava localizado.Foo = 11
), ele poderá mudar Foo globalmente (depende da linguagem, por exemplo, Java ou como oprocedure findMin(x, y, z: integer;
var m de Pascal: integer);
). No entanto, se o idioma permitir contornar o processo de desreferência, você poderá alterar47
, digamos49
. Nesse ponto, Foo parece ter sido alterado se você o ler, porque você mudou o ponteiro local para ele. E se você modificar este Foo dentro do método (Foo = 12
), provavelmente irá FUBAR a execução do programa (também conhecido como segfault) porque gravará em uma memória diferente da esperada, podendo até modificar uma área destinada a manter executável programa e escrever nele modificará o código em execução (o Foo agora não está em47
). MAS o valor de Foo de47
não mudou globalmente, apenas aquele dentro do método, porque47
também era uma cópia do método.223
dentro do método, ele cria o mesmo caos que em 3. ou 4. (um ponteiro, apontando para um valor agora ruim, que é novamente usado como ponteiro), mas ainda é um local problema, como 223 foi copiado . No entanto, se você puder desreferenciarRef2Foo
(ou seja223
), alcançar e modificar o valor apontado47
, digamos, para49
, isso afetará o Foo globalmente , porque nesse caso os métodos obtiveram uma cópia,223
mas o referenciado47
existe apenas uma vez e alterando esse valor. to49
levará cadaRef2Foo
referência dupla a um valor errado.Ao clicar em detalhes insignificantes, mesmo os idiomas que passam por referência transmitem valores para funções, mas essas funções sabem que precisam usá-lo para fins de desreferenciação. Essa passagem como referência como valor está apenas oculta do programador porque é praticamente inútil e a terminologia é apenas passagem por referência .
A passagem estrita por valor também é inútil, significaria que uma matriz de 100 Mbyte deve ser copiada toda vez que chamarmos um método com a matriz como argumento; portanto, o Java não pode ser estritamente transmitido por valor. Toda linguagem passaria uma referência a essa enorme matriz (como um valor) e empregaria o mecanismo de copiar na gravação se essa matriz pudesse ser alterada localmente dentro do método ou permitir que o método (como Java faz) modifique a matriz globalmente (de visualização do chamador) e alguns idiomas permitem modificar o valor da própria referência.
Portanto, resumidamente, e na própria terminologia de Java, Java é uma passagem por valor em que o valor pode ser: um valor real ou um valor que é uma representação de uma referência .
fonte
addr
não são tipadas,@
fazem com o tipo, e você pode modificar a variável referenciada posteriormente (exceto as copiadas localmente). Mas não vejo por que você faria isso. Esse pedaço de papel no seu exemplo (Objeto nº 24601) é uma referência, seu objetivo é ajudar a encontrar a matriz na memória, não contém dados da matriz em si. Se você reiniciar o programa, a mesma matriz poderá obter um ID de objeto diferente, mesmo que seu conteúdo seja o mesmo da execução anterior.AtomicReference
e nem expõe a referência nem seu destino, mas inclui métodos para fazer coisas com o destino; uma vez que o código para o qual o objeto foi passado retorne, oAtomicReference
[para o qual seu criador manteve uma referência direta] deve ser invalidado e abandonado. Isso forneceria a semântica adequada, mas seria lenta e nojenta.Java é uma chamada por valor
Como funciona
Você sempre passa uma cópia dos bits do valor da referência!
Se for um tipo de dados primitivo, esses bits contêm o valor do próprio tipo de dado primitivo. Por isso, se alterarmos o valor do cabeçalho dentro do método, ele não refletirá as alterações externas.
Se for um tipo de dado de objeto como Foo foo = new Foo () , nesse caso, a cópia do endereço do objeto passa como um atalho de arquivo, suponha que tenhamos um arquivo de texto abc.txt em C: \ desktop e suponha que façamos um atalho de o mesmo arquivo e coloque-o dentro de C: \ desktop \ abc-shortcut; assim, quando você acessar o arquivo em C: \ desktop \ abc.txt e escrever 'Stack Overflow', feche o arquivo e, novamente, abra o arquivo a partir do atalho; escreva 'é a maior comunidade online para programadores aprender', então a alteração total do arquivo será 'Stack Overflow é a maior comunidade online para programadores aprender'o que significa que não importa de onde você abre o arquivo, sempre que acessamos o mesmo arquivo, podemos assumir oFoo como um arquivo e suponha que foo esteja armazenado no 123hd7h (endereço original como C: \ desktop \ abc.txt ) e 234jdid (endereço copiado comoC: \ desktop \ abc-atalho que realmente contém o endereço original do arquivo)
fonte
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:
C ++ passa pelo exemplo de referência:
Exemplo do Java Pass "a Java Reference" por valor
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.
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
Ele continua fazendo o mesmo ponto em relação aos objetos. . .
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.
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 :
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:
fonte
Tanto quanto eu sei, Java só sabe chamar por valor. Isso significa que, para tipos de dados primitivos, você trabalhará com uma cópia e para objetos, com uma cópia da referência aos objetos. No entanto, acho que existem algumas armadilhas; por exemplo, isso não funcionará:
Isso preencherá o Hello World e não o World Hello, porque na função de troca você usa cópias que não têm impacto nas referências principais. Mas se seus objetos não são imutáveis, você pode alterá-lo por exemplo:
Isso preencherá o Hello World na linha de comando. Se você alterar StringBuffer para String, ele produzirá apenas Olá porque String é imutável. Por exemplo:
No entanto, você pode criar um invólucro para String como este, capaz de usá-lo com Strings:
edit: Eu acredito que este também é o motivo para usar o StringBuffer quando se trata de "adicionar" duas Strings porque você pode modificar o objeto original que você não pode com objetos imutáveis como o String.
fonte
swap(a, b)
que (1) trocaa
eb
do ponto de vista do chamador, (2) é independente de tipo na medida em que a digitação estática permite (o que significa que usá-la com outro tipo não requer nada além de alterar os tipos declarados dea
eb
) , e (3) não exige que o chamador passe explicitamente um ponteiro ou nome, então o idioma suporta a passagem por referência.Não, não é passar por referência.
Java é transmitido por valor de acordo com a Java Language Specification:
fonte
Deixe-me tentar explicar meu entendimento com a ajuda de quatro exemplos. Java é passagem por valor, e não passagem por referência
/ **
Passagem por valor
Em Java, todos os parâmetros são passados por valor, ou seja, atribuir um argumento de método não é visível para o chamador.
* /
Exemplo 1:
Resultado
Exemplo 2:
/ ** * * Passagem por valor * * /
Resultado
Exemplo 3:
/ ** Este 'passe por valor' tem a sensação de 'passar por referência'
Algumas pessoas dizem que tipos primitivos e 'String' são 'passados por valor' e objetos são 'passados por referência'.
Mas, a partir deste exemplo, podemos entender que é apenas uma passagem de valor por valor, tendo em mente que aqui estamos passando a referência como valor. ou seja: a referência é passada por valor. É por isso que é capaz de mudar e ainda é válido após o escopo local. Mas não podemos alterar a referência real fora do escopo original. o que isso significa é demonstrado no próximo exemplo de PassByValueObjectCase2.
* /
Resultado
Exemplo 4:
/ **
Além do mencionado no Exemplo3 (PassByValueObjectCase1.java), não podemos alterar a referência real fora do escopo original. "
Nota: Não estou colando o código para
private class Student
. A definição de classe paraStudent
é igual ao Exemplo3.* /
Resultado
fonte
Você nunca pode passar por referência em Java, e uma das maneiras óbvias é quando você deseja retornar mais de um valor de uma chamada de método. Considere o seguinte bit de código em C ++:
Às vezes você deseja usar o mesmo padrão em Java, mas não pode; pelo menos não diretamente. Em vez disso, você poderia fazer algo assim:
Como foi explicado nas respostas anteriores, em Java você está passando um ponteiro para a matriz como um valor
getValues
. Isso é suficiente, porque o método modifica o elemento da matriz e, por convenção, você espera que o elemento 0 contenha o valor de retorno. Obviamente, você pode fazer isso de outras maneiras, como estruturar seu código, para que isso não seja necessário, ou construir uma classe que possa conter o valor de retorno ou permitir que ele seja definido. Mas o padrão simples disponível para você no C ++ acima não está disponível em Java.fonte
Pensei em contribuir com esta resposta para adicionar mais detalhes das especificações.
Primeiro, qual é a diferença entre passar por referência e passar por valor?
Ou da wikipedia, sobre o assunto de passagem por referência
E em matéria de passagem por valor
Segundo, precisamos saber o que Java usa em suas invocações de métodos. Os estados da especificação de linguagem Java
Portanto, atribui (ou vincula) o valor do argumento à variável de parâmetro correspondente.
Qual é o valor do argumento?
Vamos considerar os tipos de referência, os estados da Java Virtual Machine Specification
A especificação da linguagem Java também declara
O valor de um argumento (de algum tipo de referência) é um ponteiro para um objeto. Observe que uma variável, uma chamada de um método com um tipo de referência retorna um tipo e uma expressão de criação de instância (
new ...
) são resolvidas para um valor de tipo de referência.assim
todos vinculam o valor de uma referência a uma
String
instância ao parâmetro recém-criado do métodoparam
,. É exatamente isso que a definição de passagem por valor descreve. Como tal, Java é transmitido por valor .O fato de você poder seguir a referência para chamar um método ou acessar um campo do objeto referenciado é completamente irrelevante para a conversa. A definição de passagem por referência foi
Em Java, modificar a variável significa reatribuí-la. Em Java, se você redesignasse a variável dentro do método, ela passaria despercebida ao chamador. Modificar o objeto referenciado pela variável é um conceito completamente diferente.
Os valores primitivos também são definidos na Java Virtual Machine Specification, aqui . O valor do tipo é o valor integral ou ponto flutuante correspondente, codificado adequadamente (8, 16, 32, 64, etc. bits).
fonte
Em Java, apenas as referências são passadas e são passadas por valor:
Todos os argumentos Java são passados por valor (a referência é copiada quando usada pelo método):
No caso de tipos primitivos, o comportamento Java é simples: o valor é copiado em outra instância do tipo primitivo.
No caso de objetos, é o mesmo: variáveis de objeto são ponteiros (baldes) contendo apenas o endereço do objeto que foi criado usando a palavra-chave "new" e são copiados como tipos primitivos.
O comportamento pode parecer diferente dos tipos primitivos: Como a variável-objeto copiada contém o mesmo endereço (para o mesmo Objeto). Conteúdo / membros do objeto ainda podem ser modificados dentro de um método e posteriormente acessados externamente, dando a ilusão de que o próprio objeto (contendo) foi passado por referência.
Os objetos "String" parecem ser um bom contra-exemplo para a lenda urbana, dizendo que "os objetos são passados por referência":
De fato, usando um método, você nunca poderá atualizar o valor de uma String passada como argumento:
Um String Object, mantém caracteres por uma matriz declarada final que não pode ser modificada. Somente o endereço do objeto pode ser substituído por outro usando "new". O uso de "new" para atualizar a variável não permitirá que o objeto seja acessado de fora, pois a variável foi inicialmente passada por valor e copiada.
fonte
strParam.setChar ( i, newValue )
. Dito isso, as strings, como qualquer outra coisa, são passadas por valor e, como String é um tipo não primitivo, esse valor é uma referência à coisa que foi criada com new, e você pode verificar isso usando String.intern () .A distinção, ou talvez a maneira como me lembro de ter a mesma impressão que o pôster original, é esta: Java é sempre transmitido por valor. Todos os objetos (em Java, qualquer coisa, exceto primitivos) em Java são referências. Essas referências são passadas por valor.
fonte
Como muitas pessoas mencionaram antes, Java sempre passa por valor
Aqui está outro exemplo que o ajudará a entender a diferença ( o exemplo clássico de troca ):
Impressões:
Isso acontece porque iA e iB são novas variáveis de referência local que têm o mesmo valor das referências passadas (elas apontam para aeb respectivamente). Portanto, tentar alterar as referências de iA ou iB mudará apenas no escopo local e não fora deste método.
fonte
Java só passa por valor. Um exemplo muito simples para validar isso.
fonte
obj
(null
) foi passado parainit
, e não uma referência paraobj
.Eu sempre penso nisso como "passagem por cópia". É uma cópia do valor, seja primitivo ou de referência. Se for um primitivo, é uma cópia dos bits que são o valor e, se for um Objeto, é uma cópia da referência.
saída do java PassByCopy:
As classes primitivas de wrapper e Strings são imutáveis, portanto, qualquer exemplo usando esses tipos não funcionará da mesma forma que outros tipos / objetos.
fonte
Diferentemente de outras linguagens, o Java não permite escolher entre passagem por valor e passagem por referência - todos os argumentos são passados por valor. Uma chamada de método pode passar dois tipos de valores para um método - cópias de valores primitivos (por exemplo, valores de int e double) e cópias de referências a objetos.
Quando um método modifica um parâmetro do tipo primitivo, as alterações no parâmetro não têm efeito no valor do argumento original no método de chamada.
Quando se trata de objetos, os próprios objetos não podem ser passados para métodos. Então passamos a referência (endereço) do objeto. Podemos manipular o objeto original usando essa referência.
Como Java cria e armazena objetos: Quando criamos um objeto, armazenamos o endereço do objeto em uma variável de referência. Vamos analisar a seguinte declaração.
"Conta da conta1" é o tipo e o nome da variável de referência, "=" é o operador da atribuição, "novo" solicita a quantidade de espaço necessária do sistema. O construtor à direita da palavra-chave new, que cria o objeto, é chamado implicitamente pela palavra-chave new. O endereço do objeto criado (resultado do valor correto, que é uma expressão chamada "expressão de criação de instância de classe") é atribuído ao valor esquerdo (que é uma variável de referência com um nome e um tipo especificado) usando o operador de atribuição.
Embora a referência de um objeto seja passada por valor, um método ainda pode interagir com o objeto referenciado chamando seus métodos públicos usando a cópia da referência do objeto. Como a referência armazenada no parâmetro é uma cópia da referência que foi passada como argumento, o parâmetro no método chamado e o argumento no método de chamada se referem ao mesmo objeto na memória.
Passar referências para matrizes, em vez dos próprios objetos da matriz, faz sentido por razões de desempenho. Como tudo em Java é passado por valor, se objetos de matriz fossem passados, uma cópia de cada elemento seria passada. Para matrizes grandes, isso desperdiçaria tempo e consumiria armazenamento considerável para as cópias dos elementos.
Na imagem abaixo, você pode ver que temos duas variáveis de referência (chamadas de ponteiros em C / C ++, e acho que esse termo facilita a compreensão desse recurso.) No método principal. As variáveis primitivas e de referência são mantidas na memória da pilha (lado esquerdo nas imagens abaixo). variáveis de referência array1 e array2 "point" (como os programadores C / C ++ o chamam) ou referência a matrizes aeb, respectivamente, que são objetos (valores que essas variáveis de referência mantêm são endereços de objetos) na memória heap (lado direito nas imagens abaixo) .
Se passarmos o valor da variável de referência array1 como argumento para o método reverseArray, uma variável de referência será criada no método e essa variável de referência começará a apontar para a mesma matriz (a).
Então, se dissermos
no método reverseArray, ele fará uma alteração na matriz a.
Temos outra variável de referência no método reverseArray (array2) que aponta para um array c. Se disséssemos
no método reverseArray, a variável de referência array1 no método reverseArray para de apontar para a matriz a e começa a apontar para a matriz c (linha pontilhada na segunda imagem).
Se retornarmos o valor da variável de referência array2 como o valor de retorno do método reverseArray e atribuir esse valor à variável de referência array1 no método principal, o array1 no main começará a apontar para o array c.
Então, vamos escrever todas as coisas que fizemos ao mesmo tempo agora.
E agora que o método reverseArray acabou, suas variáveis de referência (array1 e array2) desapareceram. O que significa que agora temos apenas as duas variáveis de referência no método principal array1 e array2, que apontam para arrays ce respectivamente. Nenhuma variável de referência está apontando para o objeto (matriz) a. Portanto, é elegível para coleta de lixo.
Você também pode atribuir o valor do array2 no main ao array1. array1 começaria a apontar para b.
fonte
Eu criei um tópico dedicado a esse tipo de pergunta para qualquer linguagem de programação aqui .
Java também é mencionado . Aqui está o breve resumo:
fonte
Para resumir uma longa história, os objetos Java têm propriedades muito peculiares.
Em geral, Java tem tipos de primitivas (
int
,bool
,char
,double
, etc.) que são passados directamente por valor. Então o Java tem objetos (tudo o que deriva dejava.lang.Object
). Na verdade, os objetos sempre são manipulados por uma referência (uma referência sendo um ponteiro que você não pode tocar). Isso significa que, na verdade, os objetos são passados por referência, pois as referências normalmente não são interessantes. No entanto, isso significa que você não pode alterar para qual objeto está apontado, pois a própria referência é passada por valor.Isso soa estranho e confuso? Vamos considerar como C implementa passar por referência e passar por valor. Em C, a convenção padrão é passar por valor.
void foo(int x)
passa um int por valor.void foo(int *x)
é uma função que não quer umint a
, mas um ponteiro para um int:foo(&a)
. Alguém poderia usar isso com o&
operador para passar um endereço variável.Leve isso para C ++, e temos referências. As referências são basicamente (neste contexto) açúcar sintático que oculta a parte indicadora da equação:
void foo(int &x)
é chamada porfoo(a)
, onde o próprio compilador sabe que é uma referência e o endereço da não referênciaa
deve ser passado. Em Java, todas as variáveis referentes a objetos são, na verdade, do tipo referência, forçando a chamada por referência para a maioria das intenções e propósitos, sem o controle (e a complexidade) de baixa granularidade proporcionados, por exemplo, pelo C ++.fonte