Fundamentos de Desenvolvimento com Java: Console, Fluxo e Laços

TP2 com Scanner, validação de entrada, faixas, Random, bissexto e laços com condição de parada defensável

Por Fábio Linhares • Instituto Infnet

Como usar esta página

Este TP parece uma lista simples de console, mas cobra disciplina de fronteira: entrada confiável, regra de negócio clara e laço com parada explícita. O valor do material está em mostrar o caminho até a resposta, não só a resposta pronta.

Por isso cada exercício agora segue o mesmo formato: o que ele cobra, passos do algoritmo, um caso normal, uma borda e o código completo. Isso força você a validar a solução, não apenas copiá-la.

Como rodar
  • IntelliJ: crie a classe do exercício, rode com Run e acompanhe a entrada pelo console.
  • Terminal: compile com javac Ex01.java e execute com java Ex01.
  • Se quiser testar várias bordas, altere apenas a entrada. O valor didático aqui está em provar o comportamento, não em trocar o código a cada caso.
BASE
O que este TP realmente testa
Objetivo: enxergar o pacote de competências por trás dos 12 exercícios.
Quatro eixos do roteiro
  • Entrada confiável: Scanner, parse e tratamento de borda.
  • Controle de fluxo preciso: faixas corretas, sem buraco lógico.
  • Validação: data, triângulo, intervalo e incremento não podem ser aceitos no escuro.
  • Repetição com contrato de parada: while só existe quando você sabe exatamente por que ele termina.
Critério de saída

Se você consegue justificar as fronteiras, provar o bissexto, explicar por que o valor que ultrapassa 100 ainda aparece e dizer por que usou equals() em vez de ==, então você passou do gabarito para a compreensão.

EX.01
Cadastro completo e comparação de tamanho de string
Objetivo: ler dados do usuário e comparar o tamanho do nome com mãe e pai.
O que o exercício cobra

Ler nome completo, idade, nome da mãe e nome do pai, imprimir o cadastro e comparar o tamanho das strings com length().

Passos do algoritmo
  • Ler tudo com nextLine() e converter a idade com Integer.parseInt().
  • Exibir os quatro campos em um bloco organizado.
  • Comparar nome do usuário com mãe e pai usando length() e operadores relacionais.
Teste manual
  • Caso normal: nome "Ana Clara", mãe "Maria", pai "João Paulo" deve imprimir o cadastro completo e avaliar os dois booleanos.
  • Borda: se dois nomes tiverem o mesmo tamanho, a comparação com > precisa resultar em false.
Ponto de atenção

Scanner com nextLine() + parse evita o clássico bug de quebrar a leitura ao misturar texto e número.

import java.util.Scanner;

public class Ex01 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Nome completo: ");
        String nome = sc.nextLine();

        System.out.print("Idade: ");
        int idade = Integer.parseInt(sc.nextLine());

        System.out.print("Nome da mãe: ");
        String mae = sc.nextLine();

        System.out.print("Nome do pai: ");
        String pai = sc.nextLine();

        System.out.println("\n=== Cadastro ===");
        System.out.println("Nome: " + nome);
        System.out.println("Idade: " + idade);
        System.out.println("Mãe: " + mae);
        System.out.println("Pai: " + pai);

        System.out.println("\nComparação de tamanho (letras):");
        System.out.println("Nome do usuário > nome da mãe? " + (nome.length() > mae.length()));
        System.out.println("Nome do usuário > nome do pai? " + (nome.length() > pai.length()));

        sc.close();
    }
}
EX.02
Média de notas com fronteiras corretas
Objetivo: calcular média e classificar em aprovado, recuperação ou reprovado.
O que o exercício cobra

Receber quatro notas, calcular a média aritmética e classificar o aluno com base em faixas.

Passos do algoritmo
  • Acumular as quatro notas em um laço e dividir por 4.0.
  • Imprimir a média formatada com duas casas.
  • Avaliar as faixas na ordem correta: >= 7.0, depois >= 5.0, depois o restante.
Teste manual
  • Caso normal: notas 8, 7, 6, 7 geram média 7.00 e devem aprovar.
  • Borda: média 5.0 entra em recuperação; média 4.9 reprova; média 7.0 aprova.
Ponto de atenção

Aqui o aprendizado real é fronteira. 6.9 e 7.0 não podem cair na mesma condição.

import java.util.Scanner;

public class Ex02 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        double soma = 0.0;
        for (int i = 1; i <= 4; i++) {
            System.out.print("Nota " + i + ": ");
            soma += Double.parseDouble(sc.nextLine());
        }

        double media = soma / 4.0;
        System.out.printf("Média: %.2f%n", media);

        if (media >= 7.0) {
            System.out.println("Aprovado(a).");
        } else if (media >= 5.0) {
            System.out.println("Recuperação.");
        } else {
            System.out.println("Reprovado(a).");
        }

        sc.close();
    }
}
EX.03
Conversor de moedas com taxas explícitas
Objetivo: converter reais para dólar, euro ou libra com taxa definida no código.
O que o exercício cobra

Receber um valor em reais e a moeda de destino, converter usando constantes definidas no próprio programa e exibir com duas casas.

Passos do algoritmo
  • Definir as constantes de câmbio no topo do programa.
  • Normalizar a moeda com trim() e toLowerCase() antes do switch.
  • Converter e imprimir apenas quando a moeda estiver entre as opções aceitas.
Teste manual
  • Caso normal: 100 BRL para dólar com USD = 5.00 deve imprimir 20.00.
  • Borda: entrada "iene" deve cair no default e informar moeda inválida.
Ponto de atenção

O enunciado manda definir as taxas no código. Se o professor exigir outro valor, ajuste apenas as constantes USD, EUR e GBP.

import java.util.Scanner;

public class Ex03 {
    public static void main(String[] args) {
        final double USD = 5.00; // 1 USD = 5.00 BRL (exemplo)
        final double EUR = 5.50; // exemplo
        final double GBP = 6.40; // exemplo

        Scanner sc = new Scanner(System.in);

        System.out.print("Valor em reais (BRL): ");
        double brl = Double.parseDouble(sc.nextLine());

        System.out.print("Moeda destino (dolar/euro/libra): ");
        String moeda = sc.nextLine().trim().toLowerCase();

        if (moeda.equals("dólar")) moeda = "dolar"; // normaliza

        double convertido;
        switch (moeda) {
            case "dolar":
                convertido = brl / USD;
                System.out.printf("USD: %.2f%n", convertido);
                break;
            case "euro":
                convertido = brl / EUR;
                System.out.printf("EUR: %.2f%n", convertido);
                break;
            case "libra":
                convertido = brl / GBP;
                System.out.printf("GBP: %.2f%n", convertido);
                break;
            default:
                System.out.println("Moeda inválida. Use: dolar/euro/libra.");
        }

        sc.close();
    }
}
EX.04
Idade em dias com dias absolutos
Objetivo: calcular idade em dias tratando bissexto e validade da data.
O que o exercício cobra

Ler dia, mês e ano de nascimento, validar a data e calcular a idade em dias usando uma contagem absoluta de dias.

Passos do algoritmo
  • Validar se a data existe antes de qualquer cálculo.
  • Converter nascimento e data atual para dias absolutos desde um ponto comum.
  • Subtrair os totais e imprimir a diferença em dias.
Teste manual
  • Caso normal: uma data válida deve produzir um total positivo de dias.
  • Borda: 28/02/2024 -> 01/03/2024 precisa respeitar o dia extra; 1900 não é bissexto e 2000 é.
Ponto de atenção

Dias absolutos ensinam mais do que subtrair datas no improviso, porque forçam uma lógica verificável para meses e anos bissextos. Aqui o uso de LocalDate.now() serve apenas para obter a data atual; se o professor exigir um caminho 100% sem API, basta ler a data de hoje via Scanner e reutilizar exatamente a mesma conta manual.

import java.util.Scanner;
import java.time.LocalDate;

public class Ex04 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Dia nascimento: ");
        int diaN = Integer.parseInt(sc.nextLine());
        System.out.print("Mês nascimento: ");
        int mesN = Integer.parseInt(sc.nextLine());
        System.out.print("Ano nascimento: ");
        int anoN = Integer.parseInt(sc.nextLine());

        if (!dataValida(diaN, mesN, anoN)) {
            System.out.println("Data de nascimento inválida.");
            sc.close();
            return;
        }

        LocalDate hoje = LocalDate.now();
        int diaH = hoje.getDayOfMonth();
        int mesH = hoje.getMonthValue();
        int anoH = hoje.getYear();

        long absN = diasAbsolutos(diaN, mesN, anoN);
        long absH = diasAbsolutos(diaH, mesH, anoH);

        long idadeDias = absH - absN;
        System.out.println("Idade total em dias: " + idadeDias);

        sc.close();
    }

    static boolean dataValida(int dia, int mes, int ano) {
        if (ano < 1) return false;
        if (mes < 1 || mes > 12) return false;
        int max = diasNoMes(mes, ano);
        return dia >= 1 && dia <= max;
    }

    static int diasNoMes(int mes, int ano) {
        int[] base = {0,31,28,31,30,31,30,31,31,30,31,30,31};
        if (mes == 2 && isBissexto(ano)) return 29;
        return base[mes];
    }

    static long diasAbsolutos(int dia, int mes, int ano) {
        int y = ano - 1;
        long total = 365L * y + qtdBissextosAte(y);

        for (int m = 1; m < mes; m++) {
            total += diasNoMes(m, ano);
        }

        total += dia;
        return total;
    }

    static long qtdBissextosAte(int ano) {
        return (ano / 4L) - (ano / 100L) + (ano / 400L);
    }

    static boolean isBissexto(int ano) {
        return (ano % 400 == 0) || (ano % 4 == 0 && ano % 100 != 0);
    }
}
EX.05
Descontos progressivos por faixa
Objetivo: aplicar 10%, 5% ou 0% conforme o valor da compra.
O que o exercício cobra

Receber o valor de uma compra e calcular desconto e total final conforme a faixa de preço.

Passos do algoritmo
  • Ler o valor e decidir a taxa de desconto pela faixa.
  • Calcular desconto = valor * taxa.
  • Imprimir valor original, desconto e valor final.
Teste manual
  • Caso normal: compra de 800.00 deve aplicar 5% e imprimir os três valores.
  • Borda: 1000.00 ainda está na faixa de 5%; 1000.01 passa para 10%.
Ponto de atenção

Este é um bom exercício para treinar a leitura exata de intervalos. Um >= no lugar errado muda o resultado inteiro.

import java.util.Scanner;

public class Ex05 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Valor da compra: ");
        double valor = Double.parseDouble(sc.nextLine());

        double taxa;
        if (valor > 1000.0) taxa = 0.10;
        else if (valor >= 500.0) taxa = 0.05;
        else taxa = 0.0;

        double desconto = valor * taxa;
        double finalValor = valor - desconto;

        System.out.printf("Original: R$ %.2f%n", valor);
        System.out.printf("Desconto: R$ %.2f%n", desconto);
        System.out.printf("Final:    R$ %.2f%n", finalValor);

        sc.close();
    }
}
EX.06
Ano bissexto com a regra completa
Objetivo: aplicar corretamente a regra de século e múltiplos de quatro.
O que o exercício cobra

Receber um ano e decidir se ele é bissexto segundo a regra completa.

Passos do algoritmo
  • Ler o ano informado pelo usuário.
  • Aplicar a condição: múltiplo de 400 entra, múltiplo de 100 sai, demais múltiplos de 4 entram.
  • Imprimir a mensagem final conforme o booleano.
Teste manual
  • Caso normal: 2024 deve ser classificado como bissexto.
  • Borda: 1900 deve ser falso; 2000 deve ser verdadeiro.
Ponto de atenção

Nem todo múltiplo de 4 é bissexto. O caso de século é exatamente o tipo de borda que costuma ser esquecida.

import java.util.Scanner;

public class Ex06 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Ano: ");
        int ano = Integer.parseInt(sc.nextLine());

        boolean b = (ano % 400 == 0) || (ano % 4 == 0 && ano % 100 != 0);
        System.out.println(b ? "É bissexto." : "Não é bissexto.");

        sc.close();
    }
}
EX.07
Imposto progressivo por faixa, não por taxa única
Objetivo: calcular imposto anual respeitando a mecânica progressiva.
O que o exercício cobra

Receber a renda anual e calcular imposto pela soma das faixas, não pela aplicação de uma única alíquota final.

Passos do algoritmo
  • Ler a renda anual bruta.
  • Calcular o imposto somando o resultado de cada faixa tributável.
  • Subtrair o imposto da renda para obter o salário líquido.
Teste manual
  • Caso normal: renda 90000 deve tributar 30000 a 10% e 30000 a 20%, totalizando 9000 no exemplo.
  • Borda: renda 30000 paga zero; renda 30000.01 tributa apenas a parte acima do limite.
Ponto de atenção

O objetivo pedagógico é a mecânica progressiva. Como o enunciado não entrega uma tabela oficial de imposto, as faixas usadas aqui funcionam como placeholder didático: se o professor usar outra tabela, troque as faixas, não o raciocínio.

import java.util.Scanner;

public class Ex07 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Salário bruto anual: ");
        double renda = Double.parseDouble(sc.nextLine());

        // Exemplo de tabela (AJUSTE):
        // até 30000: 0%
        // 30000–60000: 10%
        // 60000–120000: 20%
        // acima: 30%
        double imposto =
                tributaFaixa(renda, 0, 30000, 0.00) +
                tributaFaixa(renda, 30000, 60000, 0.10) +
                tributaFaixa(renda, 60000, 120000, 0.20) +
                tributaFaixa(renda, 120000, Double.POSITIVE_INFINITY, 0.30);

        double liquido = renda - imposto;

        System.out.printf("Imposto a pagar: R$ %.2f%n", imposto);
        System.out.printf("Salário líquido:  R$ %.2f%n", liquido);

        sc.close();
    }

    static double tributaFaixa(double renda, double inicio, double fim, double aliquota) {
        if (renda <= inicio) return 0.0;
        double base = Math.min(renda, fim) - inicio;
        return base * aliquota;
    }
}
EX.08
Classificação de triângulos com validação prévia
Objetivo: validar se os lados formam triângulo e, só então, classificar.
O que o exercício cobra

Receber três lados, validar se existe triângulo e classificar como equilátero, isósceles ou escaleno.

Passos do algoritmo
  • Ler os três lados.
  • Verificar se os lados são positivos e obedecem à desigualdade triangular.
  • Somente depois classificar pelo padrão de igualdade entre lados.
Teste manual
  • Caso normal: 3, 4 e 5 deve resultar em escaleno.
  • Borda: 1, 2 e 3 não formam triângulo; 2, 2 e 2 deve resultar em equilátero.
Ponto de atenção

Em fundamentos isso passa com double, mas vale a precisão didática: comparação exata com == em double pode trair. Se quiser máxima segurança didática, prefira int ou use tolerância.

import java.util.Scanner;

public class Ex08 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Lado A: ");
        double a = Double.parseDouble(sc.nextLine());
        System.out.print("Lado B: ");
        double b = Double.parseDouble(sc.nextLine());
        System.out.print("Lado C: ");
        double c = Double.parseDouble(sc.nextLine());

        boolean valido = a > 0 && b > 0 && c > 0
                && a + b > c && a + c > b && b + c > a;

        if (!valido) {
            System.out.println("Não forma um triângulo válido.");
        } else if (a == b && b == c) {
            System.out.println("Equilátero.");
        } else if (a == b || a == c || b == c) {
            System.out.println("Isósceles.");
        } else {
            System.out.println("Escaleno.");
        }

        sc.close();
    }
}
EX.09
Validador de senha com laço e equals
Objetivo: pedir a senha novamente até que a confirmação esteja correta.
O que o exercício cobra

Cadastrar uma senha e repetir a leitura até que a nova entrada seja idêntica à senha original.

Passos do algoritmo
  • Ler a senha inicial.
  • Entrar em laço pedindo a confirmação.
  • Usar equals() para comparar e sair apenas quando houver coincidência.
Teste manual
  • Caso normal: senha "abc123", tentativa errada e depois correta devem resultar em uma mensagem final de sucesso.
  • Borda: mesmo conteúdo em outro objeto String continua funcionando, porque a comparação é por equals(), não por ==.
Ponto de atenção

Aqui o while existe para cumprir um contrato claro: repetir enquanto a confirmação estiver errada.

import java.util.Scanner;

public class Ex09 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Cadastre uma senha: ");
        String senha = sc.nextLine();

        while (true) {
            System.out.print("Digite novamente: ");
            String tentativa = sc.nextLine();

            if (tentativa.equals(senha)) break;
            System.out.println("Senha incorreta. Tente novamente.");
        }

        System.out.println("Senha confirmada com sucesso.");
        sc.close();
    }
}
EX.10
Jogo de adivinhação com Random e feedback
Objetivo: gerar número entre 1 e 100 e repetir palpites até o acerto.
O que o exercício cobra

Sortear um número com Random, aceitar vários palpites e informar se o próximo palpite deve ser maior ou menor.

Passos do algoritmo
  • Sortear um inteiro entre 1 e 100.
  • Ler palpites em laço contínuo.
  • Rejeitar palpites fora do intervalo e dar dica maior/menor até acertar.
Teste manual
  • Caso normal: se o segredo for 70 e o palpite for 50, o programa deve responder "Maior.".
  • Borda: 0 ou 101 devem ser rejeitados antes de comparar com o segredo.
Ponto de atenção

Random aqui não serve para “efeito especial”; serve para exercitar laço com estado e feedback progressivo.

import java.util.Random;
import java.util.Scanner;

public class Ex10 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Random rnd = new Random();

        int secreto = rnd.nextInt(100) + 1; // 1..100
        int palpite;

        while (true) {
            System.out.print("Palpite (1 a 100): ");
            palpite = Integer.parseInt(sc.nextLine());

            if (palpite < 1 || palpite > 100) {
                System.out.println("Fora do intervalo. Digite de 1 a 100.");
                continue;
            }

            if (palpite < secreto) System.out.println("Maior.");
            else if (palpite > secreto) System.out.println("Menor.");
            else break;
        }

        System.out.println("Acertou!");
        sc.close();
    }
}
EX.11
Sequência numérica até ultrapassar 100
Objetivo: gerar sequência a partir de um valor inicial com incremento definido pelo usuário.
O que o exercício cobra

Ler um valor inicial e um incremento, imprimir a sequência até o primeiro valor que ultrapassar 100.

Passos do algoritmo
  • Ler valor inicial e incremento.
  • Validar se o incremento é positivo antes do laço.
  • Imprimir cada termo e encerrar quando o valor atual ultrapassar 100.
Teste manual
  • Caso normal: 98 e 3 deve imprimir 98, 101.
  • Borda: 101 e 3 imprime só 101; incremento 0 deve ser recusado para evitar loop infinito.
Ponto de atenção

O detalhe realmente importante é interpretar “até ultrapassar 100” ao pé da letra: o valor que rompe a barreira ainda precisa aparecer.

import java.util.Scanner;

public class Ex11 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Valor inicial: ");
        int atual = Integer.parseInt(sc.nextLine());

        System.out.print("Incremento: ");
        int inc = Integer.parseInt(sc.nextLine());

        if (inc <= 0) {
            System.out.println("Incremento deve ser > 0 para a sequência terminar.");
            sc.close();
            return;
        }

        boolean primeiro = true;
        while (true) {
            if (!primeiro) System.out.print(", ");
            System.out.print(atual);
            primeiro = false;

            if (atual > 100) break;   // garante que o “cara que passou” aparece

            atual += inc;
        }

        System.out.println();
        sc.close();
    }
}
EX.12
Contagem de palavras com split e trim
Objetivo: contar palavras da frase sem ser enganado por espaços extras.
O que o exercício cobra

Ler uma frase, limpar as bordas, separar por espaços em branco e contar quantas palavras existem.

Passos do algoritmo
  • Ler a frase e aplicar trim().
  • Se a frase ficar vazia, retornar 0.
  • Separar com split("\\s+") e contar os elementos.
Teste manual
  • Caso normal: "um dois tres" deve resultar em 3.
  • Borda: frase vazia retorna 0; múltiplos espaços entre palavras não podem inflar a contagem.
Ponto de atenção

Esse exercício ensina uma forma simples de impedir que “espaço sobrando” vire palavra fantasma.

import java.util.Scanner;

public class Ex12 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Digite uma frase: ");
        String frase = sc.nextLine().trim();

        if (frase.isEmpty()) {
            System.out.println("Total de palavras: 0");
            sc.close();
            return;
        }

        String[] palavras = frase.split("\\s+");

        int contador = 0;
        for (int i = 0; i < palavras.length; i++) {
            contador++;
        }

        System.out.println("Total de palavras: " + contador);
        sc.close();
    }
}
CHECK
Como provar que você entendeu
Objetivo: sair do gabarito e chegar na explicação técnica defensável.
Perguntas que você precisa conseguir responder
  • Por que nextLine() + parse é mais previsível quando há texto e número?
  • Por que dias absolutos deixam o EX04 mais confiável do que subtração improvisada de datas?
  • Por que imposto progressivo precisa ser calculado por faixa?
  • Por que o EX11 valida o incremento antes de entrar no laço?