O autor selecionou o Fundo Livre e de Código Aberto para receber uma doação como parte do programa Write for DOnations.
Introdução
Java é uma linguagem de programação estaticamente tipada. Isso significa que, ao criar uma variável, você também deve especificar o seu tipo de dado, que é o tipo de informação que ela armazena. Isso é em contraste com linguagens dinamicamente tipadas, como PHP. Com linguagens dinamicamente tipadas, você não precisa especificar o tipo de dado de uma variável, o que pode parecer um alívio.
No entanto, conhecer os tipos de dados e usá-los apropriadamente permite que os desenvolvedores otimizem seu código, pois cada tipo de dado possui requisitos específicos de recursos. Além disso, se você especificar um tipo de dado e tentar armazenar um tipo diferente, como por engano, você não conseguirá compilar o código. Assim, com linguagens estaticamente tipadas, é possível detectar erros mesmo antes de qualquer teste.
Java tem dois tipos de tipos de dados: primitivos e de referência (também conhecidos como não primitivos). Neste tutorial, você usará variáveis para armazenar e usar informações em um programa Java para aprender sobre alguns dos tipos de dados comumente usados em Java. Este não é uma visão abrangente de todos os tipos de dados, mas este guia ajudará você a se familiarizar com as opções disponíveis para você em Java.
Pré-requisitos
Para seguir este tutorial, você precisará:
-
Um ambiente no qual você pode executar programas Java para acompanhar os exemplos. Para configurar isso em sua máquina local, você precisará do seguinte:
- Java (versão 11 ou superior) instalado em sua máquina, com o compilador fornecido pelo Kit de Desenvolvimento Java (JDK). Para Ubuntu e Debian, siga as etapas da Opção 1 em nosso tutorial, Como Instalar o Java com Apt no Ubuntu 22.04. Para outros sistemas operacionais, incluindo Mac e Windows, consulte as opções de download para instalação do Java.
- Para compilar e executar os exemplos de código, este tutorial utiliza o Java Shell, um Loop de Leitura-Avaliação-Impressão (REPL) executado a partir da linha de comando. Para começar com o JShell, confira o Guia de Introdução ao JShell.
-
Familiaridade com Java e programação orientada a objetos, que você pode encontrar em nosso tutorial, Como Escrever Seu Primeiro Programa em Java.
Tipos Primitivos
Os tipos primitivos do Java são os tipos de dados mais simples e básicos em Java. Eles representam valores brutos como números e caracteres. Os tipos de dados primitivos mais frequentemente usados são int
(inteiros), boolean
(valores booleanos) e char
(caracteres). Você pode encontrar o restante na documentação oficial de tipos de dados do Java.
Inteiros
Os inteiros podem ser tanto números inteiros negativos quanto positivos. Em Java, você usará int
para armazená-los. O int
pode acomodar números grandes o suficiente para a maioria dos propósitos: de -2.147.483.648
a 2.147.483.647
.
Vamos ver como o int
é usado em um exemplo:
int theAnswer = 42;
Os tipos primitivos sempre começam com uma letra minúscula (int
). As regras de sintaxe do Java exigem que você primeiro especifique o tipo de dados (int
) e depois o nome (theAnswer
). Depois disso, você atribui o valor 42
com o sinal de igual (=
) à variável.
Independentemente do tipo de dados, você utiliza uma variável especificando diretamente seu nome sem adicionar nenhum caractere especial. Isso ocorre porque o Java pode reconhecê-lo como uma variável.
Nota: O nome da variável theAnswer
e de todas as outras variáveis neste tutorial estão escritos em Camel case. Mesmo que não haja uma exigência rigorosa para utilizá-lo, essa é a convenção de nomenclatura aceita no Java.
Uma vez que você tenha declarado a variável, pode usá-la referenciando-a em um método da seguinte maneira:
int theAnswer = 42;
System.out.println("The answer to all questions is " + theAnswer);
Na segunda linha, você imprime theAnswer
no console usando o método integrado println
do pacote System.out
. Esta é a maneira mais simples de testar uma variável para garantir que ela seja declarada conforme o esperado.
Para ver este código em ação, utilize a ferramenta Java Shell. Após instalar o Java, abra um terminal ou prompt de comando em seu computador local e digite jshell
:
- jshell
Seu resultado será semelhante ao seguinte:
Output| Welcome to JShell -- Version 11.0.16
| For an introduction type: /help intro
jshell>
Você pode colar os exemplos de código deste tutorial no console. Quando terminar, pode sair do jshell
digitando /exit
.
Para declarar e usar int
, cole as seguintes linhas no console do jshell
:
- int theAnswer = 42;
- System.out.println("The answer to all questions is " + theAnswer);
Você verá o seguinte resultado:
OutputtheAnswer ==> 42
The answer to all questions is 42
Este resultado confirma que você definiu corretamente a variável int
theAnswer
como 42 (theAnswer ==> 42
). Você também usou com sucesso theAnswer
ao passá-lo para um método, e o método produziu o valor da variável esperado.
Booleano
Booleano valores são true
ou false
. Em Java, você usará boolean
para armazená-los. Por exemplo, vamos criar uma variável boolean
definindo se Java é divertido ou não:
boolean isJavaFun = true;
Você define a variável isJavaFun
como true
. O valor alternativo de boolean
é false
.
Usando a variável acima, você pode imprimir a frase Java é divertido: true
assim:
- boolean isJavaFun = true;
- System.out.println("Java is fun: " + isJavaFun);
Executar essas linhas no jshell
produzirá a seguinte saída:
OutputisJavaFun ==> true
Java is fun: true
Similar ao exemplo de int
, o método println
imprimirá o argumento fornecido entre parênteses. O sinal de mais (+
) concatena ou junta a string “Java é divertido: ” com a variável isJavaFun
, de modo que, na realidade, é apenas um argumento – a string, Java é divertido: true
.
Caracteres
Para armazenar um único caractere alfanumérico, você usará char
. Por exemplo:
char firstLetter = 'a';
Observe que a letra a
está entre aspas simples. As aspas simples são usadas apenas para valores de char
. As aspas duplas são usadas para strings, como você aprenderá mais tarde.
char
não parece ser um tipo particularmente útil, porque é pouco provável que você precise de uma variável atribuída a um único caractere. No entanto, char
é utilizado como bloco de construção para classes de cadeias de caracteres, como String
, que são basicamente uma coleção de valores char
.
Como visto nesta seção, a declaração e o uso de variáveis do tipo primitivo são diretos, pois representam valores simples, como inteiros. Esses valores estão prontos para serem usados e não exigem operações adicionais, como a criação de objetos, invocação de métodos, etc.
Tipos de Referência
No primeiro tutorial desta série, Como Escrever Seu Primeiro Programa em Java, você aprendeu que o código Java é organizado em classes e que essas classes são usadas como modelos para criar objetos. Quando esses objetos são atribuídos a variáveis, você está apontando ou referenciando esses objetos. Nesses casos, as variáveis são classificadas como tipos de referência. Essas variáveis também são conhecidas como não primitivas, porque variáveis do tipo primitivo não podem apontar para objetos.
Objetos são poderosos porque possuem propriedades avançadas e conseguem agir quando você aciona seus métodos. No entanto, sem variáveis apontando para eles, esses objetos são inacessíveis e praticamente inutilizáveis. É por isso que variáveis do tipo referência são essenciais para o Java e programação orientada a objetos como um todo.
Nota: Tipos de referência apontam para objetos criados a partir de classes. Para evitar confusão, o tipo de referência e o objeto criado serão da mesma classe nos exemplos a seguir.
No entanto, em programas complexos, isso raramente é o caso. Em Java, uma interface é um grupo de requisitos para um comportamento específico, e esses requisitos podem ser atendidos por uma ou mais classes. Uma classe que atende aos requisitos de uma interface é considerada como implementando essa interface. Assim, em programas complexos, é comum declarar uma variável com o tipo de referência de uma interface. Dessa forma, você especifica o comportamento que sua variável deve exibir sem vinculá-la a uma implementação concreta desse comportamento. Isso permite que você altere facilmente para qual implementação sua variável aponta sem precisar alterar a maneira como a variável é usada. Esse conceito complexo faz parte de um tópico mais avançado sobre herança e polimorfismo, que será um tutorial separado em nossa série Java.
Embora existam apenas alguns tipos primitivos, os tipos de referência são praticamente ilimitados porque não há limite para o número de classes (e interfaces), e cada classe representa um tipo de referência. Existem muitas classes integradas em Java que fornecem funcionalidades essenciais. As mais utilizadas são encontradas no pacote central java.lang
. Você revisará algumas delas nesta seção.
A Classe String
A classe String
representa uma combinação de caracteres que formam uma string. Para declarar uma String
, ou qualquer outra variável do tipo de referência, você primeiro especifica seu tipo seguido pelo nome. Depois disso, você atribui um valor a ela com o sinal de igual. Até agora, é semelhante ao trabalho com tipos primitivos. No entanto, os tipos de referência apontam para objetos, então você precisa criar um objeto se ainda não houver sido criado um. Aqui está um exemplo:
String hello = new String("Hello");
hello
é o nome da variável com tipo de referência String
. Você a atribui a um novo objeto String
. O novo objeto String
é criado com a palavra-chave new
junto com o nome da classe — String
neste caso. A classe String
começa com uma letra maiúscula. Por convenção, todas as classes e, portanto, tipos de referência começam com uma letra maiúscula.
Cada classe possui um método especial chamado de construtor que é utilizado para criar novos objetos. Você pode invocar este construtor adicionando parênteses (()
) ao final do nome da classe. O construtor pode aceitar parâmetros, como no exemplo acima, onde o parâmetro "Hello"
é aplicado ao construtor para String
.
Para confirmar que a variável hello
se comporta conforme esperado, passe-a novamente para o método println
assim:
- String hello = new String("Hello");
- System.out.println(hello);
Executar estas linhas no jshell
produzirá a seguinte saída:
Outputhello ==> "Hello"
Hello
Desta vez, a saída confirma que a variável hello
está definida para Hello
. Depois disso, o mesmo Hello
é impresso em uma nova linha, confirmando que o método println()
processou-o.
Classes Wrapper
Na seção anterior, você trabalhou com o tipo de referência String
, que é frequentemente utilizado. Outros tipos de referência populares são as chamadas wrappers para tipos primitivos. Uma classe wrapper envolve ou contém dados primitivos, daí o seu nome. Todos os tipos primitivos têm contrapartes wrapper, e aqui estão alguns exemplos:
Integer
: Para envolver valoresint
.Character
: Para envolver valoreschar
.Boolean
: Para envolver valoresboolean
.
Esses invólucros existem para que você possa elevar um valor primitivo simples a um objeto poderoso. Cada invólucro possui métodos prontos para uso relacionados aos valores que ele foi projetado para armazenar.
Como exemplo, você explorará Integer
. Na seção anterior, você criou um objeto String
com a palavra-chave new
. No entanto, algumas classes fornecem, e até incentivam, o uso de métodos especiais para adquirir objetos delas, e Integer
é uma delas. No caso de Integer
, o uso de um método especial é principalmente sobre otimização de recursos, mas em outros casos, pode ser sobre simplificar a construção de objetos complexos.
No exemplo a seguir, você cria uma variável Integer
chamada theAnswer
com o valor 42
usando o método valueOf
:
- Integer theAnswer = Integer.valueOf(42);
- System.out.println(theAnswer);
No jshell
, você obterá a seguinte saída:
OutputtheAnswer ==> 42
42
Ao invocar o método valueOf(42)
da classe Integer
, você instrui o Java a fornecer um objeto com esse valor. Nos bastidores, o Java verificará se já existe um objeto com esse valor em seu cache. Se houver, o objeto será vinculado à variável theAnswer
. Se não houver, um novo objeto será criado para a variável theAnswer
.
Muitas classes integradas fornecem tais métodos por razões de desempenho, e seu uso é recomendado, se não obrigatório. No caso de Integer
, ainda é possível criar um objeto com a palavra-chave new
, mas você receberá um aviso de depreciação.
Além de String
e wrappers, existem outros tipos de referência incorporados úteis, que você pode encontrar no resumo do pacote java.lang. Para entender completamente alguns desses tipos de referência mais avançados, é necessário uma explicação adicional ou conhecimento prévio. É por isso que abordaremos alguns deles nos próximos tutoriais da nossa série Java.
Literais
Os literais representam valores fixos que podem ser usados diretamente no código e, portanto, podem ser atribuídos tanto a tipos primitivos quanto a tipos de referência. Existem alguns tipos de literais, que podem ser categorizados da seguinte forma.
Literais de Tipo Primitivo
Você já usou alguns literais na seção sobre tipos primitivos. Para cada tipo primitivo, existe um literal, como os exemplos que usamos: 42
, 'a'
e true
. Inteiros como 42
são literais inteiros. Da mesma forma, caracteres como 'a'
são literais de caractere, e true
e false
são literais booleanos.
Tipos primitivos literais também podem ser usados para criar valores para tipos de referência. O literal int
foi usado para criar um objeto Integer
com o código Integer.valueOf(42)
. Também há uma forma mais simplificada para isso, e você pode atribuir o valor diretamente assim:
Integer theAnswer = 42;
42
é um literal inteiro, assim como qualquer número inteiro, e você pode atribuí-lo diretamente à variável theAnswer
sem precisar de instruções adicionais. É comum ver um Integer
declarado dessa forma porque é conveniente.
Essa abordagem simplificada também funciona para outros tipos primitivos literais e seus tipos de referência correspondentes, como Boolean
, por exemplo:
Boolean isFun = true;
true
é o literal, que é atribuído diretamente à variável isFun
do tipo Boolean
. Também há um literal false
, que pode ser atribuído da mesma forma.
O Literal de String
Também há um literal especial para o tipo de referência String
, e ele é reconhecido pelas aspas duplas que cercam seu valor. Neste exemplo, é "Olá, Mundo!"
:
String helloWorld = "Hello, World!";
Usar literais é mais simples e mais curto, e é por isso que muitos programadores preferem. No entanto, ainda é possível declarar uma variável String
com um novo objeto String
, como você já fez na seção para tipos de referência.
O Literal Null
Existe mais um literal importante: null
, que representa a ausência de um valor ou a não existência de um objeto. Null
permite que você crie um tipo de referência e o aponte para null
em vez de apontá-lo para um objeto. null
pode ser usado para todos os tipos de referência, mas não para nenhum tipo primitivo.
Existe uma ressalva com o literal null
: você pode declarar variáveis com ele, mas não pode usar essas variáveis até atribuir um valor adequado e não nulo. Se você tentar usar uma variável de tipo de referência com valor null
, ocorrerá um erro. Aqui está um exemplo:
- String initiallyNullString = null;
- System.out.println("The class name is: " + initiallyNullString.getClass());
Ao tentar executar este código no jshell
, você verá um erro semelhante ao seguinte:
OutputinitiallyNullString ==> null
| Exception java.lang.NullPointerException
| at (#4:1)
Dependendo do seu sistema operacional e da versão do Java, a saída pode ser diferente.
O erro java.lang.NullPointerException
é lançado porque você está tentando invocar o método getClass()
da classe String
(que retorna o nome da classe) na variável initiallyNullString
(que aponta para um objeto nulo).
Nota: Por simplicidade, estamos chamando o java.lang.NullPointerException
de erro, mesmo que tecnicamente seja uma exceção. Para mais informações sobre exceções e erros, consulte o tutorial, Tratamento de Exceções em Java.
Para corrigir o erro, é necessário reatribuir o valor de initiallyNullString
desta forma:
- String initiallyNullString = null;
- initiallyNullString = "not null any longer";
- System.out.println("The class name is: " + initiallyNullString.getClass());
O novo código corrigido imprimirá a seguinte saída:
OutputinitiallyNullString ==> null
initiallyNullString ==> "not null any longer"
The class name is: class java.lang.String
A saída acima mostra como initiallyNullString
é inicialmente null
, depois se torna um novo objeto String
contendo "not null any longer"
. Em seguida, quando o método getClass()
é invocado no objeto instanciado, você obtém java.lang.String
, onde String
é o nome da classe e java.lang
é o pacote. Finalmente, uma mensagem completa e significativa é impressa: "O nome da classe é: class java.lang.String"
.
Declarações de valores null
como essa são mais comuns em código legado. Elas foram usadas para criar uma variável primeiro e, em seguida, atribuir seu valor real posteriormente, geralmente passando por alguma lógica que determina este último. No entanto, desde a versão 8 do Java, existe um novo tipo de referência chamado Optional, que é mais adequado para casos em que o null
era utilizado anteriormente.
Inferência de Tipo de Variável Local
Até agora, você tem usado alguns dos tipos de dados comuns em Java para definir variáveis. No entanto, o Java 10 introduziu um novo recurso chamado inferência de tipo de variável local, que permite que você use a palavra-chave var
na frente de uma nova variável. Com este recurso, o Java irá inferir (ou seja, adivinhar automaticamente) o tipo de dados a partir do contexto local. A inferência de tipo é controversa, pois contrasta com a verbosidade previamente explicada de definir variáveis. As vantagens e desvantagens de tal recurso são discutíveis, mas o fato é que outras linguagens tipadas estaticamente, como C++, suportam inferência de tipo.
De qualquer forma, a inferência de tipo não pode substituir completamente o uso de tipos de dados, porque funciona apenas com variáveis locais, que são variáveis dentro de um método. Vamos ver um exemplo com var
:
- var hello = "Hello";
- System.out.println(hello);
Você declara a variável hello
com a palavra-chave var
para instruir o Java a detectar seu tipo de dados. Depois disso, você a imprime no console da maneira usual para confirmar que funciona conforme o esperado:
Ouputhello ==> "Hello"
Hello
Este exemplo funcionará desde que sua instalação do Java (mais especificamente, o JDK) esteja acima da versão 10. A palavra-chave var
não é suportada em versões mais antigas.
O processo de inferência de tipo ocorre durante o processo de compilação — ou seja, quando você compila o código. O processo de compilação transforma o código fonte em texto simples em código de máquina e aplica várias otimizações, incluindo a inferência de tipo. Isso garante que a quantidade correta de memória do sistema esteja disponível para as variáveis com tipo inferido. Assim, o código de máquina que você executa após compilar está totalmente otimizado, como se você tivesse especificado manualmente todos os tipos de dados.
Neste exemplo, a palavra-chave var
funciona porque a variável é local, e o tipo de dados var
funciona apenas com variáveis locais. Variáveis locais são definidas dentro de métodos e são acessíveis apenas dentro dos métodos, é por isso que são chamadas de “locais”.
Para mostrar que var
só pode ser usado para variáveis locais, tente colocá-lo fora do método principal, assim:
- public class Hello {
- var hello = "Hello";
- public static void main(String[] args) {
- // example code
- }
- }
Quando você colar o código acima no jshell
, você receberá o seguinte erro:
Output| Error:
| 'var' is not allowed here
| var hello = "Hello";
| ^-^
var
não é permitido aqui porque hello
está fora de um método e não é mais considerado local. Assim, a inferência de tipo não funciona para variáveis não locais porque o contexto não pode ser usado de forma confiável para detectar o tipo de dados.
Embora usar var
possa ser desafiador e não seja obrigatório, você provavelmente encontrará isso, então é útil conhecê-lo.
Palavras-chave Reservadas
Ao declarar variáveis em Java, há uma regra importante a mais para saber. Existem palavras-chave reservadas que você não pode usar como nomes de variáveis. Por exemplo, você não pode declarar um primitivo do tipo int
e nomeá-lo new
assim:
- int new = 1;
Se você tentar este exemplo, você receberá erros de compilação porque new
é uma palavra-chave reservada.
Output| Error:
| '.class' expected
| int new = 1;
| ^
| Error:
| <identifier> expected
| int new = 1;
| ^
| Error:
| '(' or '[' expected
| int new = 1;
| ^
| Error:
| unexpected type
| required: value
| found: class
| int new = 1;
| ^--^
| Error:
| missing return statement
| int new = 1;
| ^----------^
A palavra-chave new
é usada para criar novos objetos e o Java não espera isso nessa posição. Na lista de erros na saída anterior, a primeira parte é a mais importante:
Output| Error:
| '.class' expected
| int new = 1;
| ^
O erro '.class' esperado
significa que quando você usa a palavra-chave new
, o Java espera que uma classe siga. Neste ponto, o Java não é capaz de interpretar a declaração e os demais erros seguem.
As demais palavras-chave reservadas, como abstract
, continue
, default
, for
e break
, também têm significados específicos em Java e não podem ser usadas como nomes de variáveis. A lista completa das palavras-chave reservadas pode ser encontrada na página de Palavras-chave da Linguagem Java. Mesmo que você não se lembre de todas as palavras-chave reservadas, você pode usar erros de compilação para identificar o problema.
Conclusão
Neste tutorial, você aprendeu sobre tipos de dados primitivos e de referência em Java, que é um tópico complexo, mas essencial. Dedique um tempo para praticá-lo e passe pelos exemplos mais de uma vez. Experimente alterar alguns dos tipos de dados e valores. Preste atenção em quando os erros são lançados e quando não são para desenvolver um senso de execução de código bem-sucedida.
Para mais sobre Java, confira nossa série Como Programar em Java.
Source:
https://www.digitalocean.com/community/tutorials/understanding-data-types-in-java