Variáveis e tipos básicos
por Beckett HollisEntenda o conceito
Uma variável é um nome para um valor guardado na memória. Em Java toda variável tem um tipo fixo, decidido na compilação: int nivel = 3; declara o tipo, o nome e o valor inicial. Depois disso, nivel só aceita outros int; tentar guardar um texto ali é erro de compilação.
Os tipos primitivos são oito: byte, short, int e long para inteiros; float e double para decimais; char para um caractere (entre aspas simples, como 'A'); e boolean para true ou false. Texto fica em String, que não é primitivo, mas é usado o tempo todo. No dia a dia, int, long, double, boolean, char e String resolvem quase tudo.
O tipo muda o resultado das contas: 7 / 2 entre inteiros dá 3, porque a parte decimal é descartada, e um int que passa de Integer.MAX_VALUE dá a volta para um número negativo sem aviso. Para converter, o Java alarga sozinho (int vira double), mas estreitar exige casting explícito, como (int) 7.9, que dá 7.
Desde o Java 10, var deixa o compilador deduzir o tipo de uma variável local pelo valor inicial (var total = 10; é um int). E final impede que a variável receba outro valor depois de atribuída, o jeito de criar constantes.
- Declaração:
tipo nome = valor;. O tipo é fixo: uma variávelintnunca vai guardar texto. - Literais têm tipo:
10éint,10Lélong,2.5édouble,2.5féfloat,'A'échare"A"éString. - Entre inteiros,
/descarta os decimais e%dá o resto; use(double)antes de dividir para obter o valor exato. intvai até 2.147.483.647; acima disso estoura em silêncio, então uselongpara valores grandes.vardeduz o tipo pelo valor inicial (só em variáveis locais) efinalimpede reatribuição.
Do conceito à prática
3 exemplos para ver a ideia funcionando.
01Ficha de personagem com os tipos mais usados
public class Inventario {
public static void main(String[] args) {
// Declaração com tipo e valor inicial: tipo nome = valor;
String heroi = "Lia";
int nivel = 3;
double vida = 87.5;
char classe = 'M'; // char usa aspas simples: um único caractere
boolean temEscudo = false;
System.out.println("Herói: " + heroi);
System.out.println("Nível: " + nivel);
System.out.println("Vida: " + vida);
System.out.println("Classe: " + classe);
System.out.println("Tem escudo? " + temEscudo);
// Uma variável pode receber um novo valor do mesmo tipo.
nivel = nivel + 1;
vida -= 12.5; // atalho para vida = vida - 12.5
temEscudo = true;
System.out.println("--- depois da batalha ---");
System.out.println("Nível: " + nivel);
System.out.println("Vida: " + vida);
System.out.println("Tem escudo? " + temEscudo);
}
}
O exemplo declara uma variável de cada tipo que mais aparece no dia a dia: String para o nome, int para o nível, double para a vida, char para a classe e boolean para saber se o herói tem escudo. Repare que o char usa aspas simples e a String, aspas duplas.
Depois da batalha, as mesmas variáveis recebem novos valores, sempre do tipo declarado. O -= é um atalho para vida = vida - 12.5, e o double imprime 75.0 com a casa decimal, mesmo quando o valor é redondo.
02Estoque com inteiros, decimais e limites
public class Estoque {
public static void main(String[] args) {
// Inteiros: byte, short, int e long, cada um com sua faixa.
byte prateleiras = 12;
short caixas = 1_200; // _ separa os dígitos, só para leitura
int unidades = 48_000;
long vendasNoAno = 3_500_000_000L; // o L marca o literal como long
System.out.println("Prateleiras: " + prateleiras);
System.out.println("Caixas: " + caixas);
System.out.println("Unidades: " + unidades);
System.out.println("Vendas no ano: " + vendasNoAno);
// Decimais: double é o padrão; float precisa do sufixo f
// e tem menos precisão, o que aparece nas casas finais.
double precoCaneta = 4.75;
float desconto = 0.1f;
System.out.println("Preço com desconto: " + precoCaneta * (1 - desconto));
// int dividido por int descarta a parte decimal.
int caixasPorPrateleira = caixas / prateleiras;
int sobra = unidades % 7; // % devolve o resto da divisão
System.out.println("Caixas por prateleira: " + caixasPorPrateleira);
System.out.println("Resto em lotes de 7: " + sobra);
// Os limites de cada tipo ficam nas classes wrapper.
System.out.println("Maior int: " + Integer.MAX_VALUE);
System.out.println("Maior long: " + Long.MAX_VALUE);
}
}
Aqui aparecem os quatro tipos inteiros e os dois decimais. O literal 3_500_000_000L precisa do L, porque sem ele o compilador trata o número como int e acusa que ele é grande demais. O _ entre os dígitos é só para facilitar a leitura e não muda o valor.
A divisão caixas / prateleiras é feita entre inteiros, então 1200 / 12 dá 100 exato, e 48000 % 7 mostra o resto. O preço com desconto expõe a imprecisão do float nas últimas casas, por isso double é a escolha padrão para decimais (e, para dinheiro de verdade, BigDecimal ou centavos em long).
03Receita com var, final e conversões
public class Receita {
// final: o valor não pode mudar depois de atribuído (constante).
static final int PORCOES_BASE = 4;
public static void main(String[] args) {
// var (Java 10+) deduz o tipo pelo valor inicial; o tipo continua fixo.
var prato = "Panqueca"; // String
var convidados = 10; // int
var leiteMl = 250.0; // double
// Conversão automática (alargamento): int vira double sem perda.
double fator = (double) convidados / PORCOES_BASE;
System.out.println(prato + " para " + convidados + " pessoas");
System.out.println("Fator: " + fator);
System.out.println("Leite: " + leiteMl * fator + " ml");
// Casting explícito (estreitamento): double vira int e perde os decimais.
int ovos = (int) (3 * fator);
System.out.println("Ovos (arredondado para baixo): " + ovos);
System.out.println("Ovos (arredondado): " + Math.round(3 * fator));
// char é um número por baixo: somar 1 avança no alfabeto.
char etapa = 'A';
etapa++;
System.out.println("Etapa atual: " + etapa);
// convidados = "dez"; // não compila: convidados é int para sempre
}
}
PORCOES_BASE é uma constante: o final impede que ela receba outro valor, e o nome em maiúsculas é a convenção para constantes. As variáveis com var têm o tipo deduzido pelo valor inicial (String, int e double) e esse tipo continua fixo, como mostra a linha comentada no fim.
O (double) em (double) convidados / PORCOES_BASE converte antes de dividir, e o resultado é 2.5, não 2. Na outra direção, (int) (3 * fator) corta os decimais de 7.5 e dá 7, enquanto Math.round arredonda para 8. O char também é um número por baixo, por isso etapa++ passa de 'A' para 'B'.
Agora é com você
Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.
01Cadastro de um livro no acervoFácil
Escreva um programa que guarde em variáveis os dados de um livro da biblioteca (título, ano, páginas, multa diária, estante e disponibilidade) e imprima cada um em uma linha, como na saída esperada.
- A classe se chama
Acervoe fica no arquivoAcervo.java. - Usa pelo menos cinco tipos diferentes:
String,int,double,chareboolean, todos declarados com o tipo explícito (semvar). - Cada linha é impressa concatenando um rótulo à variável, sem repetir o valor digitado no texto.
- A saída é idêntica à esperada.
public class Acervo {
public static void main(String[] args) {
String titulo = "O Cortiço";
// Declare as outras cinco variáveis e imprima as seis linhas.
}
}
Título: O Cortiço
Ano: 1890
Páginas: 312
Multa por dia: 1.5
Estante: C
Disponível: true
Ver dica
Escolha o tipo pelo dado: número sem vírgula, número com casas decimais, uma única letra ou sim/não.
Ver resposta
public class Acervo {
public static void main(String[] args) {
// Cada variável tem um tipo escolhido pelo tipo de dado que guarda.
String titulo = "O Cortiço";
int anoPublicacao = 1890;
int paginas = 312;
double multaDiaria = 1.5;
char estante = 'C';
boolean disponivel = true;
System.out.println("Título: " + titulo);
System.out.println("Ano: " + anoPublicacao);
System.out.println("Páginas: " + paginas);
System.out.println("Multa por dia: " + multaDiaria);
System.out.println("Estante: " + estante);
System.out.println("Disponível: " + disponivel);
}
}
Passo a passo
String titulo = "O Cortiço";guarda o texto entre aspas duplas;Stringé o tipo para qualquer sequência de caracteres.anoPublicacaoepaginassãoint, porque são números inteiros pequenos, bem abaixo do limite de 2,1 bilhões.multaDiariaédouble, já que o valor tem casa decimal; em Java o separador decimal no código é sempre o ponto.estanteécharcom aspas simples, porque guarda uma única letra.disponivelébooleane só aceitatrueoufalse, sem aspas.- Cada
printlnjunta o rótulo à variável com+; o Java converte o número, o caractere e o booleano em texto automaticamente.
Por que funciona
Cada variável foi declarada com o tipo que combina com o dado que ela guarda, e o compilador confere essa combinação: um texto num int ou um número decimal num int não compilaria. Na hora de imprimir, a concatenação com uma String transforma qualquer tipo em texto, por isso o double aparece como 1.5 e o boolean como true.
Erros comuns
- Decimal em variável int:
int multaDiaria = 1.5;não compila ("possible lossy conversion from double to int"). Usedouble. - Aspas duplas no char:
char estante = "C";não compila ("incompatible types: String cannot be converted to char"). Use aspas simples:'C'. - Booleano entre aspas:
boolean disponivel = "true";também não compila, porque"true"é umaString. Escrevatruesem aspas. - Vírgula decimal:
double multaDiaria = 1,5;gera erro de sintaxe. No código, o decimal usa ponto.
Outra forma de resolver
Com var, o compilador deduz os mesmos tipos a partir dos valores:
var paginas = 312; // int
var multaDiaria = 1.5; // double
var estante = 'C'; // char
var encurta declarações em que o tipo fica óbvio pelo valor; o enunciado pede o tipo explícito justamente para treinar a escolha de cada um.
Saída esperada
Título: O Cortiço
Ano: 1890
Páginas: 312
Multa por dia: 1.5
Estante: C
Disponível: true
02Repartir as moedas do baúMédio
Um baú tem 125 moedas para dividir entre 4 jogadores. Calcule quantas moedas cada um recebe, quantas sobram no baú e qual seria a divisão exata com casas decimais.
- As quantidades ficam em variáveis
intchamadasmoedasejogadores. - A parte de cada jogador usa divisão inteira (
/) e a sobra usa o resto (%). - A divisão exata é um
doubleobtido com casting, sem digitar125.0nem31.25. - A saída é idêntica à esperada.
public class Moedas {
public static void main(String[] args) {
int moedas = 125;
int jogadores = 4;
// Calcule a parte de cada um, a sobra e a divisão exata.
}
}
Moedas no baú: 125
Cada jogador recebe: 31
Sobram no baú: 1
Divisão exata: 31.25
Ver dica
A conversão precisa acontecer antes da divisão; converter o resultado depois é tarde demais.
Ver resposta
public class Moedas {
public static void main(String[] args) {
int moedas = 125;
int jogadores = 4;
// int / int: divisão inteira, a parte decimal é descartada.
int porJogador = moedas / jogadores;
// % devolve o que sobrou da divisão.
int sobra = moedas % jogadores;
// O casting converte moedas para double antes de dividir.
double divisaoExata = (double) moedas / jogadores;
System.out.println("Moedas no baú: " + moedas);
System.out.println("Cada jogador recebe: " + porJogador);
System.out.println("Sobram no baú: " + sobra);
System.out.println("Divisão exata: " + divisaoExata);
}
}
Passo a passo
moedasejogadoressãoint, porque não existe meia moeda nem meio jogador.moedas / jogadoresdivide dois inteiros, então o Java faz divisão inteira:125 / 4dá31e a parte decimal é descartada, sem arredondar.moedas % jogadoresdevolve o resto dessa divisão:125 - 4 * 31é1.(double) moedasconverte125em125.0antes da divisão; com um dos ladosdouble, a conta inteira é feita emdoublee dá31.25.- As quatro linhas concatenam o rótulo ao valor; o
doubleaparece com o ponto decimal.
Por que funciona
Em Java o tipo dos operandos decide o tipo da operação. Dois int produzem um int, e é por isso que / e % andam juntos: um dá o quociente, o outro o que sobrou, e 31 * 4 + 1 volta a 125. O casting tem precedência maior que a divisão, então (double) moedas / jogadores converte primeiro e divide depois.
Erros comuns
- Casting no lugar errado:
(double) (moedas / jogadores)imprime31.0. A divisão inteira já aconteceu dentro dos parênteses; converta sómoedas. - Guardar o resultado num double sem casting:
double divisaoExata = moedas / jogadores;também dá31.0, porque a conta é feita emintantes da atribuição. - Esperar arredondamento: a divisão inteira sempre corta os decimais (
7 / 2é3). Para arredondar, useMath.roundsobre o valordouble.
Outra forma de resolver
Multiplicar por 1.0 também força a conta em double:
double divisaoExata = moedas * 1.0 / jogadores;
Funciona, mas o casting (double) deixa a intenção mais clara para quem lê; o truque do 1.0 aparece em código antigo.
Saída esperada
Moedas no baú: 125
Cada jogador recebe: 31
Sobram no baú: 1
Divisão exata: 31.25
03Faturamento das filiais sem estouroDesafio
Três filiais de uma loja faturaram 950.000.075 centavos cada uma. Mostre o total calculado em int (que estoura), o total correto em long, o valor em reais e se o total caberia num int.
- O faturamento de cada filial fica num
inte o número de filiais em outroint. - O total correto é um
longobtido com casting antes da multiplicação, como(long) porFilial * filiais. - Reais e centavos saem do total com
/ 100e% 100, e os centavos são impressos com dois dígitos (%02d). - A última linha compara o total com
Integer.MAX_VALUE.
public class Faturamento {
public static void main(String[] args) {
int porFilial = 950_000_075;
int filiais = 3;
// Calcule o total em int e em long, converta para reais e compare com o limite.
}
}
Faturamento por filial: 950000075 centavos
Total em int: -1444967071
Total em long: 2850000225
Em reais: R$ 28500002,25
Cabe em int? false
Ver dica
Some de cabeça: três vezes quase um bilhão passa do maior valor que um int consegue guardar.
Ver resposta
public class Faturamento {
public static void main(String[] args) {
int porFilial = 950_000_075; // centavos de cada filial
int filiais = 3;
// int * int é calculado em int e estoura o limite em silêncio.
int totalInt = porFilial * filiais;
// Com um dos lados em long, a conta inteira é feita em long.
long total = (long) porFilial * filiais;
// Centavos viram reais com divisão inteira e resto.
long reais = total / 100;
long centavos = total % 100;
System.out.println("Faturamento por filial: " + porFilial + " centavos");
System.out.println("Total em int: " + totalInt);
System.out.println("Total em long: " + total);
System.out.printf("Em reais: R$ %d,%02d%n", reais, centavos);
System.out.println("Cabe em int? " + (total <= Integer.MAX_VALUE));
}
}
Passo a passo
int porFilial = 950_000_075;cabe numint, cujo limite é 2.147.483.647; os_só separam os dígitos para facilitar a leitura.porFilial * filiaismultiplica doisint, então o resultado também éint. O valor real, 2.850.000.225, passa do limite e dá a volta: sobra-1444967071, sem erro nem aviso.(long) porFilial * filiaisconverte o primeiro operando antes da multiplicação, e a conta toda passa a ser feita emlong, que vai até cerca de 9,2 quintilhões.total / 100é divisão inteira entrelongeint, então devolve só a parte em reais:28500002.total % 100devolve os centavos que sobraram:25.printf("Em reais: R$ %d,%02d%n", reais, centavos)imprime os reais e, com%02d, os centavos sempre com dois dígitos (5viraria05).(total <= Integer.MAX_VALUE)compara olongcom o limite dointe produz umboolean; os parênteses fazem a comparação antes da concatenação.
Por que funciona
O tipo de uma operação aritmética é decidido pelos operandos, não pela variável que recebe o resultado. Por isso o casting precisa estar dentro da expressão, num dos operandos: assim a multiplicação acontece em 64 bits e o valor cabe. A representação em centavos com long evita a imprecisão do double com dinheiro, e / e % separam reais e centavos sem nenhuma conta com vírgula.
O int estourado mostra por que escolher o tipo importa: o Java não lança exceção no overflow de inteiros, ele simplesmente continua a partir do menor valor negativo.
Erros comuns
- Casting no resultado:
long total = porFilial * filiais;imprime-1444967071, porque a multiplicação estourou emintantes de virarlong. Converta um dos operandos:(long) porFilial * filiais. - Casting depois da conta:
(long) (porFilial * filiais)tem o mesmo problema; os parênteses fazem a multiplicação emintprimeiro. - Centavos sem zero à esquerda:
%dno lugar de%02dimprimiriaR$ 10,5para 1.005 centavos, quando o certo éR$ 10,05. - Literal grande sem L:
long total = 2850000225;não compila ("integer number too large"). Escreva2_850_000_225L.
Outra forma de resolver
Math.multiplyExact faz a conta em int e lança ArithmeticException se estourar, em vez de devolver um valor errado:
try {
int total = Math.multiplyExact(porFilial, filiais);
} catch (ArithmeticException e) {
System.out.println("Estourou o int: use long");
}
É a escolha certa quando o valor deveria caber no int e um estouro indica um problema que precisa aparecer na hora.
Saída esperada
Faturamento por filial: 950000075 centavos
Total em int: -1444967071
Total em long: 2850000225
Em reais: R$ 28500002,25
Cabe em int? false