1. Variáveis de referência
Na linguagem Java, existem dois tipos de variáveis: variáveis primitivas e tudo mais. Por acaso, vamos falar sobre "todo o resto" agora.
Na verdade, seria mais correto dizer que existem variáveis primitivas e variáveis de referência . Então, quais são essas variáveis de referência?
Ao contrário dos tipos primitivos, cujas variáveis armazenam valores diretamente, as variáveis de referência armazenam referências a objetos. Ou seja, existe um objeto em algum lugar da memória e a variável de referência simplesmente armazena o endereço desse objeto na memória (uma referência ao objeto).
Somente tipos primitivos armazenam valores diretamente dentro de variáveis. Todos os outros tipos armazenam apenas uma referência de objeto . A propósito, você já encontrou dois desses tipos de variáveis — String
variáveis e variáveis de matriz .
Tanto um array quanto uma string são objetos armazenados em algum lugar da memória. String
variáveis e variáveis de matriz armazenam apenas referências a objetos.
int a, int b and double d
são variáveis primitivas que armazenam seus valores dentro de si.
Uma String str
variável é uma referência e armazena o endereço (referência) para um String
objeto na memória.
Ao atribuir um valor primitivo a uma variável de tipo primitivo, seu valor é copiado (duplicado). Ao atribuir uma variável de referência, apenas o endereço do objeto é copiado — o próprio objeto não é copiado .
2. Do que se tratam as referências?
Qual é a diferença fundamental entre variáveis de referência e variáveis primitivas?
Uma variável primitiva é como uma caixa: você pode armazenar algum valor nela. Uma variável de referência é mais como um pedaço de papel com um número de telefone.
Um carro vs chaves do carro
Imagine que você decide dar um carro para seu amigo no aniversário dele. Você não vai embrulhá-lo em uma caixa e carregá-lo com você: o carro é grande demais para isso.
É muito mais conveniente apresentar apenas as chaves do carro em uma caixa grande o suficiente para contê-las. Seu amigo vai entender tudo quando tirar as chaves da caixa. Não há necessidade de carregar o carro inteiro com você quando você pode simplesmente entregar as chaves.
Uma pessoa versus seu número de telefone
Ou aqui está outra comparação: uma pessoa e seu número de telefone. Um número de telefone não é a pessoa, mas um número de telefone pode ser usado para ligar para ela, pedir algumas informações ou fornecer instruções.
Da mesma forma, uma referência é usada para interagir com um objeto. Todos os objetos interagem uns com os outros usando referências. Em vez de "trocar pessoas", simplesmente trocamos números de telefone.
Ao atribuir um valor a uma variável primitiva, seu valor é copiado (duplicado). Ao atribuir um valor a uma variável de referência, apenas o endereço (número de telefone) do objeto é copiado — o próprio objeto não é copiado.
Uma referência oferece mais uma vantagem: você pode passar uma referência de objeto para algum método, e o método poderá modificar (alterar) o objeto usando a referência a ele, chamando seus métodos e acessando dados dentro do objeto.
3. Atribuição de referências
Ao atribuir variáveis de referência, apenas o endereço do objeto na memória é atribuído. Os próprios objetos não aparecem ou desaparecem.
Essa abordagem evita a cópia de grandes quantidades de memória. Se você precisa passar um objeto muito grande para um método, basta passarmos a referência do objeto e pronto. A referência ocupa muito menos espaço.
O tamanho de todas as variáveis de referência (independente de seu tipo) é o mesmo — 4 bytes (como um int). Mas! Se seu aplicativo estiver sendo executado em uma máquina Java de 64 bits, todas as referências terão 8 bytes (64 bits) de tamanho.
Além do mais, as referências só podem ser atribuídas umas às outras. Você não pode alterar referências ou atribuir valores arbitrários a variáveis de referência:
Código | Descrição |
---|---|
|
isso é permitido |
|
Mas isso não é permitido |
|
E isso não é permitido |
4. Uma null
referência
E o que uma variável de referência armazena se nada foi atribuído a ela ainda?
Ele armazena uma referência nula . null
é uma palavra-chave Java especial que significa a ausência de uma referência (uma referência vazia). O null
valor pode ser atribuído a qualquer variável de referência.
Todas as variáveis de referência são, null
a menos que tenham algum tipo de referência atribuída a elas.
Exemplos:
Código | Descrição |
---|---|
|
A String name variável tem um valor padrão: null . A int age variável tem um valor padrão: 0 . |
Variáveis locais que não receberam um valor são consideradas não inicializadas para tipos primitivos e de referência.
Se uma variável armazena uma referência a algum objeto e você deseja limpar o valor da variável, basta atribuir a ela uma referência nula.
Código | Descrição |
---|---|
|
s lojas null . s armazena uma referência a um objeto de cadeia s de caracteres null . |
5. Passando referências a métodos
Se um método tiver parâmetros que são tipos de referência , os valores serão passados para o método da mesma forma que ao trabalhar com variáveis sem referência. O parâmetro é simplesmente atribuído ao valor da outra variável.
Exemplo:
Código | Descrição |
---|---|
|
O fill preenche o array passado ( array ) com o valor passado ( value ). |
Quando o fill
método é chamado, o array
parâmetro recebe uma referência ao data
array. A value
variável recebe uma referência ao objeto string ("Hello").
É assim que a memória se parece antes de chamar o fill
método:
É assim que a memória se parece quando o fill
método está em execução :
As variáveis data
e array
referem-se (armazenam referências) ao mesmo contêiner na memória.
A value
variável armazena uma referência ao objeto string ( "Hello"
).
As células da matriz também armazenam apenas referências ao "Hello"
objeto.
Na verdade, nenhum objeto é duplicado — apenas referências são copiadas.
6. Comparação com a linguagem C/C++
Em entrevistas, às vezes os programadores Java são questionados sobre como os dados são passados para os métodos em Java. E às vezes a dúvida é se os dados são passados por referência ou por valor?
Esta questão vem de C++, mas não é muito significativa em Java . Em Java, os parâmetros sempre recebem simplesmente os valores dos argumentos. Então a resposta correta seria " por valor ".
Mas esteja preparado para explicar sua posição , pois você pode ouvir imediatamente a resposta: "tipos primitivos são passados por valor e tipos de referência são passados por referência."
A origem desse problema decorre do fato de que muitos programadores Java eram programadores C++ no passado. Nessa linguagem de programação, a questão de como os parâmetros são passados para os métodos era muito importante.
Em Java, tudo é inequívoco: tipos primitivos armazenam valores e tipos de referência também armazenam um valor — uma referência. É uma questão de saber se uma variável é considerada um valor .
Em C++, uma variável pode armazenar tanto uma referência a um objeto quanto o próprio objeto. O mesmo acontecia com os tipos primitivos: uma variável primitiva poderia armazenar um valor ou declarar a variável como uma referência a um arquivo int
. Portanto, para evitar confusão, os programadores C++ sempre se referem ao objeto como uma referência como uma referência e o próprio objeto como um valor.
Em C++, você pode facilmente ter a situação em que uma variável contém um objeto, mas a outra contém uma referência a esse objeto. Assim, a questão do que uma variável armazena — o próprio objeto ou apenas uma referência a ele — era muito importante. Quando um objeto foi passado para um método, ele foi copiado (se passado por valor), e não copiado (se passado por referência).
Em Java essa dualidade não existe, então a resposta correta é: os argumentos são passados para os métodos Java por valor . Só que quando estamos falando de variáveis de referência, esse valor é uma referência.
GO TO FULL VERSION