Sua dose diária de programação

Conceito, 3 exemplos e 3 exercícios: uma pequena conquista de cada vez.

✦   Lição

Variáveis e tipos básicos

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.

Java Fundamentos Iniciante 3 exemplos + 3 exercícios
42 lições publicadas 10 linguagens novas lições todo dia útil
Java / Fundamentos

Variáveis e tipos básicos

por Beckett Hollis
Iniciante

Entenda 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.

↳ Pontos-chave
  • Declaração: tipo nome = valor;. O tipo é fixo: uma variável int nunca vai guardar texto.
  • Literais têm tipo: 10 é int, 10L é long, 2.5 é double, 2.5f é float, 'A' é char e "A" é String.
  • Entre inteiros, / descarta os decimais e % dá o resto; use (double) antes de dividir para obter o valor exato.
  • int vai até 2.147.483.647; acima disso estoura em silêncio, então use long para valores grandes.
  • var deduz o tipo pelo valor inicial (só em variáveis locais) e final impede reatribuição.

Do conceito à prática

3 exemplos para ver a ideia funcionando.

01Ficha de personagem com os tipos mais usados

Inventario.java
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

Estoque.java
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

Receita.java
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.

↳ Requisitos
  • A classe se chama Acervo e fica no arquivo Acervo.java.
  • Usa pelo menos cinco tipos diferentes: String, int, double, char e boolean, todos declarados com o tipo explícito (sem var).
  • Cada linha é impressa concatenando um rótulo à variável, sem repetir o valor digitado no texto.
  • A saída é idêntica à esperada.
código inicial
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.
    }
}
saída esperada
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

  1. String titulo = "O Cortiço"; guarda o texto entre aspas duplas; String é o tipo para qualquer sequência de caracteres.
  2. anoPublicacao e paginas são int, porque são números inteiros pequenos, bem abaixo do limite de 2,1 bilhões.
  3. multaDiaria é double, já que o valor tem casa decimal; em Java o separador decimal no código é sempre o ponto.
  4. estante é char com aspas simples, porque guarda uma única letra.
  5. disponivel é boolean e só aceita true ou false, sem aspas.
  6. Cada println junta 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"). Use double.
  • 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" é uma String. Escreva true sem 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.

↳ Requisitos
  • As quantidades ficam em variáveis int chamadas moedas e jogadores.
  • A parte de cada jogador usa divisão inteira (/) e a sobra usa o resto (%).
  • A divisão exata é um double obtido com casting, sem digitar 125.0 nem 31.25.
  • A saída é idêntica à esperada.
código inicial
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.
    }
}
saída esperada
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

  1. moedas e jogadores são int, porque não existe meia moeda nem meio jogador.
  2. moedas / jogadores divide dois inteiros, então o Java faz divisão inteira: 125 / 4 dá 31 e a parte decimal é descartada, sem arredondar.
  3. moedas % jogadores devolve o resto dessa divisão: 125 - 4 * 31 é 1.
  4. (double) moedas converte 125 em 125.0 antes da divisão; com um dos lados double, a conta inteira é feita em double e dá 31.25.
  5. As quatro linhas concatenam o rótulo ao valor; o double aparece 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) imprime 31.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 em int antes da atribuição.
  • Esperar arredondamento: a divisão inteira sempre corta os decimais (7 / 2 é 3). Para arredondar, use Math.round sobre o valor double.

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.

↳ Requisitos
  • O faturamento de cada filial fica num int e o número de filiais em outro int.
  • O total correto é um long obtido com casting antes da multiplicação, como (long) porFilial * filiais.
  • Reais e centavos saem do total com / 100 e % 100, e os centavos são impressos com dois dígitos (%02d).
  • A última linha compara o total com Integer.MAX_VALUE.
código inicial
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.
    }
}
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
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

  1. int porFilial = 950_000_075; cabe num int, cujo limite é 2.147.483.647; os _ só separam os dígitos para facilitar a leitura.
  2. porFilial * filiais multiplica dois int, 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.
  3. (long) porFilial * filiais converte o primeiro operando antes da multiplicação, e a conta toda passa a ser feita em long, que vai até cerca de 9,2 quintilhões.
  4. total / 100 é divisão inteira entre long e int, então devolve só a parte em reais: 28500002.
  5. total % 100 devolve os centavos que sobraram: 25.
  6. printf("Em reais: R$ %d,%02d%n", reais, centavos) imprime os reais e, com %02d, os centavos sempre com dois dígitos (5 viraria 05).
  7. (total <= Integer.MAX_VALUE) compara o long com o limite do int e produz um boolean; 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 em int antes de virar long. 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 em int primeiro.
  • Centavos sem zero à esquerda: %d no lugar de %02d imprimiria R$ 10,5 para 1.005 centavos, quando o certo é R$ 10,05.
  • Literal grande sem L: long total = 2850000225; não compila ("integer number too large"). Escreva 2_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

Últimas lições

Ver o arquivo completo →