Sem entender a sintaxe Java, é impossível se tornar um desenvolvedor sério, por isso hoje continuamos a aprender a sintaxe. Em um dos artigos anteriores falamos sobre variáveis primitivas, mas como existem dois tipos de variáveis, hoje falaremos sobre o segundo tipo - tipos de referência em Java. Então o que é? Por que os tipos de dados de referência são necessários em Java? Vamos imaginar que temos um objeto TV com algumas características, como número do canal, volume do som e bandeira:
public class TV {
int numberOfChannel;
int soundVolume;
boolean isOn;
}
Como um tipo simples como pode int
armazenar esses dados? Vamos lembrar: uma variável int
tem 4 bytes. Mas dentro existem duas variáveis (4 bytes + 4 bytes) do mesmo tipo, e também boolean
(+1 byte)... Total - 4 a 9, mas via de regra, muito mais informações são armazenadas em um objeto. O que fazer? Você não pode colocar um objeto em uma variável. Neste ponto da nossa história, aparecem variáveis de referência. Variáveis de referência armazenam o endereço do local da memória em que um objeto específico está localizado. Ou seja, trata-se de um “cartão de visita” com um endereço, com o qual podemos encontrar nosso objeto na memória compartilhada e realizar algumas manipulações com ele. Uma referência a qualquer objeto em Java é uma variável de referência. Seria assim com nosso objeto TV:
TV telly = new TV();
Definimos uma variável do tipo TV com um nome telly
para um link para o objeto criado do tipo TV. Ou seja, a JVM aloca memória no heap para o objeto TV, cria-o e o endereço para sua localização na memória, coloca-o na variável telly
, que fica armazenada na pilha. Você pode ler mais sobre memória, ou seja, pilha e muitas outras informações úteis, nesta palestra . Uma variável do tipo TV e um objeto do tipo TV, você percebeu? Isso não é sem razão: objetos de um determinado tipo devem ter variáveis correspondentes do mesmo tipo (sem contar herança e implementações de interface, mas agora não estamos levando isso em consideração). Afinal, não vamos colocar sopa em copos, certo? Acontece que nosso objeto é uma TV e a variável de referência para ele é como um painel de controle. Usando este controle remoto podemos interagir com nosso objeto e seus dados. Por exemplo, defina as características da nossa TV:
telly.isOn = true;
telly.numberOfChannel = 53;
telly.soundVolume = 20;
Aqui usamos o operador ponto .
para acessar e começar a usar os elementos internos do objeto ao qual a variável se refere. Por exemplo, na primeira linha dissemos à variável telly
: “Dê-nos uma variável interna isOn
do objeto que você está referenciando e configure-a como verdadeira” (ligue a TV para nós).
Redefinindo variáveis de referência
Digamos que temos duas variáveis de um tipo de referência e os objetos aos quais elas se referem:TV firstTV = new TV();
TV secondTV = new TV();
Se escrevermos:
firstTV = secondTV;
isso significará que atribuímos à primeira variável como valor uma cópia do endereço (o valor dos bits de endereço) para o segundo objeto, e agora ambas as variáveis se referem ao segundo objeto (em outras palavras, dois controles remotos para o mesmo TELEVISÃO). Ao mesmo tempo, o primeiro objeto ficou sem uma variável que se referisse a ele. Como resultado, temos um objeto que não pode ser acessado, pois a variável era um thread condicional para ele, sem o qual ele vira lixo, apenas fica na memória e ocupa espaço. Este objeto será posteriormente destruído da memória pelo coletor de lixo . Você pode quebrar o fio de conexão com um objeto sem outro link:
secondTV = null;
Como resultado, haverá apenas um link para o objeto - firstTV
, e secondTV
não apontará mais para ninguém (o que não nos impede de atribuir a ele um link para algum objeto como TV no futuro).
Classe de cordas
Separadamente, gostaria de mencionar a classe String . Esta é uma classe base projetada para armazenar e trabalhar com dados armazenados como uma string. Exemplo:String text = new String("This TV is very loud");
Aqui passamos uma string para ser armazenada no construtor do objeto. Mas ninguém faz isso. Afinal, strings podem ser criadas:
String text = "This TV is very loud";
Muito mais conveniente, certo? Em termos de popularidade de uso, String
não é inferior aos tipos primitivos, mas ainda é uma classe, e a variável que se refere a ela não é um tipo primitivo, mas um tipo de referência. Temos String
esta maravilhosa capacidade de concatenar strings:
String text = "This TV" + " is very loud";
Como resultado, obteremos novamente o texto: This TV is very loud
, pois as duas linhas serão combinadas em um todo, e a variável se referirá a este texto completo. Uma nuance importante é que String
esta é uma classe imutável. O que isso significa? Vejamos este exemplo:
String text = "This TV";
text = text + " is very loud";
Parece que tudo é simples: declaramos uma variável, atribuímos um valor a ela. Na próxima linha nós mudamos isso. Mas nós realmente não mudamos. Por se tratar de uma classe imutável, na segunda linha o valor inicial não é alterado, mas é criado um novo, que por sua vez consiste no primeiro + " is very loud"
.
Constantes de referência
No artigo sobre tipos primitivos, abordamos o tópico de constantes. Como uma variável de referência se comportará quando a declararmos final ?final TV telly = new TV();
Você pode pensar que isso tornará o objeto imutável. Mas não, isso não é verdade. Uma variável de referência com um modificador final
será vinculada a um objeto específico sem a capacidade de desvinculá-lo de qualquer forma (redefini-lo ou igualá-lo a null
). Ou seja, após definir o valor de tal variável, codifique como:
telly = new TV();
ou
telly = null;
causará um erro de compilação. Ou seja, final
atua apenas no link e não tem efeito no objeto em si. Se inicialmente o tivermos mutável, podemos alterar seu estado interno sem problemas:
telly.soundVolume = 30;
Às vezes, as variáveis são designadas como finais mesmo nos argumentos do método!
public void enableTV (final TV telly){
telly.isOn = true;
}
Isso é feito para que durante o processo de escrita de um método esses argumentos não possam ser substituídos e, conseqüentemente, criem menos confusão. E se denotarmos final
uma variável de referência que se refere a um objeto imutável? Por exemplo String
:
final String PASSWORD = "password";
Como resultado, obteremos uma constante, um análogo de constantes de tipo primitivo, pois aqui não podemos redefinir a referência nem alterar o estado interno do objeto (dados internos).
Vamos resumir
- Enquanto variáveis simples armazenam bits de valor, variáveis de referência armazenam bits que representam como um objeto é recuperado.
- As referências de objeto são declaradas para apenas um tipo de objeto.
- Qualquer classe em Java é um tipo de referência.
- O valor padrão em Java para qualquer variável de referência é
null
. String
é um exemplo padrão de um tipo de referência. Esta classe também é imutável.- Variáveis de referência com modificador
final
são vinculadas a apenas um objeto sem possibilidade de redefinição.
GO TO FULL VERSION