O autor selecionou o Fundo Livre e de Código Aberto para receber uma doação como parte do programa Escreva por Doações.
Introdução
O Java é uma linguagem de programação com tipagem estática. Isso significa que ao criar uma variável, você também deve especificar seu tipo de dados, que é o tipo de informação que ela armazena. Isso contrasta com as linguagens de tipagem dinâmica, como o PHP. Com linguagens de tipagem dinâmica, você não precisa especificar o tipo de dados de uma variável, o que pode parecer um alívio.
No entanto, conhecer os tipos de dados e usá-los apropriadamente permite aos desenvolvedores otimizar seu código, pois cada tipo de dados tem requisitos de recursos específicos. Além disso, se você especificar um tipo de dados e tentar armazenar um tipo diferente, como por engano, não conseguirá compilar o código. Assim, com linguagens de tipagem estática, você pode detectar erros mesmo antes de qualquer teste.
Java possui dois 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, aprendendo sobre alguns dos tipos de dados comumente utilizados. Este não é um guia abrangente de todos os tipos de dados, mas ajudará você a se familiarizar com as opções disponíveis 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 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 em Java são os tipos de dados mais simples e básicos. Eles representam valores brutos, como números e caracteres. Os tipos de dados primitivos mais frequentemente utilizados são int
(inteiros), boolean
(valores booleanos) e char
(caracteres). Você pode encontrar o restante no documento oficial sobre tipos de dados em 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 suficientemente grandes para a maioria dos propósitos: de -2.147.483.648
a 2.147.483.647
.
Vamos ver como o int
é utilizado em um exemplo:
int theAnswer = 42;
Os tipos primitivos sempre começam com uma letra minúscula (int
). As regras de sintaxe em Java exigem que você primeiro especifique o tipo de dado (int
) e, em seguida, seu nome (theAnswer
). Depois disso, você atribui o valor 42
à variável com o sinal de igual (=
).
Independentemente do tipo de dados, você usa 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 todas as outras variáveis neste tutorial são escritos em Camel case. Mesmo que não haja uma exigência rigorosa para usá-lo, esta é a convenção de nomenclatura aceita no Java.
Depois de declarar a variável, você pode usá-la referenciando-a em um método da seguinte forma:
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, use a ferramenta Java Shell. Após instalar o Java, abra um terminal ou prompt de comando no seu computador local e digite jshell
:
- jshell
Sua saída 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, você 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á a seguinte saída:
OutputtheAnswer ==> 42
The answer to all questions is 42
Esta saída confirma que você definiu corretamente a variável int
theAnswer
para 42 (theAnswer ==> 42
). Você também usou com sucesso theAnswer
passando-o para um método, e o método produziu o valor esperado da variável.
Boolean
Boolean 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);
Executando essas linhas no jshell
produzirá a seguinte saída:
OutputisJavaFun ==> true
Java is fun: true
Semelhante ao exemplo de int
, o método println
imprimirá o argumento fornecido entre parênteses. O sinal de mais (+
) concatena ou une a string “Java é divertido: ” com a variável isJavaFun
, de modo que na realidade, é apenas um argumento — a string, Java é divertido: true
.
Personagens
Para armazenar um único caractere alfanumérico, você usará char
. Por exemplo:
char firstLetter = 'a';
Observe que a letra a
está entre aspas simples. Aspas simples são usadas apenas para valores de char
. Aspas duplas são usadas para strings, como você aprenderá mais tarde.
char
não parece ser um tipo particularmente útil, pois é pouco provável que você precise de uma variável atribuída a um único caractere. No entanto, char
é utilizado como o 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 de tipo primitivo são diretos, pois representam valores simples como inteiros. Esses valores estão prontos para serem usados e não requerem operações adicionais, como criar objetos, invocar métodos, e assim por diante.
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 se referindo a 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 de tipo primitivo não podem apontar para objetos.
Objetos são poderosos porque possuem propriedades avançadas e podem 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, variáveis do tipo referência são essenciais para o Java e a 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 forma 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. Os mais frequentemente usados são encontrados no pacote principal java.lang
. Você irá revisar alguns deles 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 de tipo de referência, você primeiro especifica seu tipo seguido pelo seu 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. 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
juntamente 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 construtor que é utilizado para criar novos objetos. Você pode invocar este construtor adicionando parênteses (()
) no final do nome da classe. O construtor pode aceitar parâmetros, como no exemplo acima, onde o parâmetro "Hello"
é aplicado ao construtor de String
.
Para confirmar que a variável hello
se comporta conforme o esperado, passe-a novamente para o método println
desta forma:
- String hello = new String("Hello");
- System.out.println(hello);
Executar essas linhas no jshell
resultará na seguinte saída:
Outputhello ==> "Hello"
Hello
Desta vez, a saída confirma que a variável hello
está definida como Hello
. Depois disso, o mesmo Hello
é impresso em uma nova linha, confirmando que o método println()
o processou.
Classes de Invólucro
Na seção anterior, você trabalhou com o tipo de referência String
, que é frequentemente utilizado. Outros tipos de referência populares são os chamados invólucros para tipos primitivos. Uma classe de invólucro envolve ou contém dados primitivos, daí o seu nome. Todos os tipos primitivos têm equivalentes de invólucro e aqui estão alguns exemplos:
Integer
: Para envolver valoresint
.Character
: Para envolver valoreschar
.Boolean
: Para envolver valoresboolean
.
Estes invólucros existem para que você possa atualizar um valor primitivo simples para um objeto poderoso. Cada invólucro tem métodos prontos para uso relacionados aos valores que se destinam a 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
, usar um método especial é principalmente sobre otimização de recursos, mas em outros casos, poderia 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 lhe fornecer um objeto com este valor. Nos bastidores, o Java verificará se já existe um objeto com tal 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
, você ainda poderia criar um objeto com a palavra-chave new
, mas você receberá um aviso de depreciação.
Além de String
e wrappers, também existem outros tipos de referência integrados úteis, que você pode encontrar no resumo do pacote java.lang. Para entender completamente alguns desses tipos de referência mais avançados, é necessária uma explicação adicional ou conhecimento prévio. É por isso que vamos abordar alguns deles em nossos próximos tutoriais da 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, há um literal, como os exemplos: 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 utilizado na criação de um objeto Integer
com o código Integer.valueOf(42)
. Existe também uma forma abreviada 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 a necessidade de declarações adicionais. É comum ver um Integer
declarado dessa forma porque é conveniente.
Esta abordagem abreviada 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
. Existe também um literal false
, que você pode atribuir da mesma maneira.
O Literal de String
Existe também 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!";
O uso de literais é mais simples e mais curto, e é por isso que muitos programadores o preferem. No entanto, ainda é possível declarar uma variável String
com um novo objeto String
, como já foi feito na seção de tipos de referência.
O Literal Null
Há 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 aponte-o 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.
Há 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 do tipo de referência com um valor null
, receberá 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 versão do Java, sua saída pode ser diferente.
O erro java.lang.NullPointerException
é lançado porque você está tentando invocar o método getClass()
(que retorna o nome da classe) na variável initiallyNullString
(que aponta para um objeto nulo).
Nota: Para simplicidade, estamos chamando java.lang.NullPointerException
de erro, embora tecnicamente seja uma exceção. Para mais informações sobre exceções e erros, consulte o tutorial, Tratamento de Exceções em Java.
Para resolver o erro, você precisa reatribuir o valor initiallyNullString
assim:
- 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
, e então 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
é seu pacote. Finalmente, uma mensagem completa e significativa é impressa: "The class name is: class java.lang.String"
.
Tais declarações de valores null
são mais comuns em código legado. Elas foram usadas para criar uma variável primeiro e depois atribuir seu valor real, geralmente passando por alguma lógica que determina isso posteriormente. No entanto, desde a versão 8 do Java, há um novo tipo de referência chamado Optional, que é mais adequado para casos em que null
foi usado anteriormente.
Inferência de Tipo de Variável Local
Até agora, você tem utilizado alguns dos tipos de dados comuns em Java para definir variáveis. No entanto, o Java 10 introduziu uma nova funcionalidade chamada inferência de tipo de variável local, que permite o uso da palavra-chave var
na frente de uma nova variável. Com essa funcionalidade, o Java irá inferir (ou seja, adivinhar automaticamente) o tipo de dado a partir do contexto local. A inferência de tipo é controversa, pois contrasta com a verbosidade previamente explicada na definição de variáveis. As vantagens e desvantagens dessa funcionalidade são discutíveis, mas o fato é que outras linguagens tipadas estaticamente, como C++, também suportam a inferência de tipo.
Em todo caso, a inferência de tipo não pode substituir completamente o uso de tipos de dados, pois funciona apenas com variáveis locais, que são variáveis dentro de um método. Vamos dar uma olhada em 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 o tipo de dado. Depois disso, você a imprime no console da maneira usual para confirmar que está funcionando como esperado:
Ouputhello ==> "Hello"
Hello
Este exemplo funcionará desde que sua instalação do Java (mais especificamente, o JDK) seja da versão 10 ou superior. A palavra-chave var
não é suportada em versões mais antigas.
A inferência de tipo ocorre durante o processo de compilação — isto é, quando você compila o código. O processo de compilação converte 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 de tipo inferido. Assim, o código de máquina que você executa após a compilação 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. As variáveis locais são definidas dentro de métodos e são acessíveis apenas dentro dos métodos, razão pela qual são chamadas de “locais”.
Para mostrar que var
só pode ser usada para variáveis locais, tente colocá-la fora do método principal, assim:
- public class Hello {
- var hello = "Hello";
- public static void main(String[] args) {
- // example code
- }
- }
Ao 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 lá 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
Quando declarando variáveis em Java, há mais uma regra importante a saber. Existem palavras-chave reservadas que não pode usar como nomes de variáveis. Por exemplo, não pode declarar um primitivo do tipo int
e nomeá-lo new
assim:
- int new = 1;
Se tentar este exemplo, terá 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 nesta 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 usa a palavra-chave new
, o Java espera que uma classe siga. Neste ponto, o Java não consegue interpretar a declaração e os outros erros seguem.
O resto das 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 não se lembre de todas as palavras-chave reservadas, pode usar erros de compilação para identificar o problema.
Conclusão
No tutorial, aprendeu sobre tipos de dados primitivos e de referência em Java, que é um tópico complexo, mas essencial. Dedique tempo para praticar e revisar os exemplos mais de uma vez. Experimente alterar alguns dos tipos de dados e valores. Preste atenção em quando os erros são gerados e quando não são, a fim de desenvolver um senso para a execução bem-sucedida do código.
Para mais informações sobre Java, confira nossa série Como Programar em Java.
Source:
https://www.digitalocean.com/community/tutorials/understanding-data-types-in-java