Projeto Calculadora

O primeiro projeto do guia: uma calculadora que roda no terminal. Ela pergunta a operação e os dois números, mostra o resultado e continua até você digitar sair. Parece simples, e é de propósito: o desafio não é a conta, é fazer o programa não quebrar quando alguém digita "abc", divide por zero ou escolhe uma operação que não existe.

Você vai praticar

O que vamos construir

Esta é a calculadora pronta, rodando na máquina da Ana. Tudo que aparece depois dos : nas perguntas foi digitado por ela:

$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, / ou sair): +
Primeiro número: 10
Segundo número: 5
10.0 + 5.0 = 15.0

Operação (+, -, *, / ou sair): /
Primeiro número: 7
Segundo número: 2
7.0 / 2.0 = 3.5

Operação (+, -, *, / ou sair): %
Operação desconhecida: %                  « operação que não existe
Operação (+, -, *, / ou sair): *
Primeiro número: seis
"seis" não é um número. Tente de novo.    « texto no lugar de número
Primeiro número: 6
Segundo número: 1,5                       « decimal com vírgula (veja a etapa 1)
6.0 * 1.5 = 9.0

Operação (+, -, *, / ou sair): /
Primeiro número: 1
Segundo número: 0
Não dá para dividir por zero.
Operação (+, -, *, / ou sair): sair
Até logo!

Vamos chegar lá em seis etapas:

  1. Ler dois números e somar (Scanner, nextDouble).
  2. Escolher a operação (switch com setas).
  3. Organizar em métodos (calcular, lerNumero, operacaoValida).
  4. Validar o que foi digitado (hasNextDouble).
  5. Não dividir por zero.
  6. Repetir até "sair" (while, break, continue).

Antes de começar

Você precisa saber criar e rodar um programa (Olá, Mundo), usar variáveis double e String e ter visto o básico do Scanner. No seu computador, crie a pasta do projeto; o arquivo vai se chamar Calculadora.java, porque a classe se chama Calculadora:

$ mkdir -p java/calculadora
$ cd java/calculadora

Etapa 1 · Ler dois números e somar

O começo de toda calculadora: pedir dois números e mostrar a soma. Para ler do teclado, o Java usa um Scanner (em inglês, "leitor", "escâner"), um objeto que lê o que você digita e transforma em números ou texto.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Primeiro número: ");
        double a = entrada.nextDouble();
        System.out.print("Segundo número: ");
        double b = entrada.nextDouble();

        double soma = a + b;
        System.out.println(a + " + " + b + " = " + soma);
    }
}
LinhaO que faz
import java.util.Scanner;Avisa que vamos usar a classe Scanner, que mora no pacote java.util. Sem isso, o javac diz cannot find symbol (veja Pacotes e import).
Scanner entrada = new Scanner(System.in);Cria o leitor. System.in é a entrada padrão: o teclado. Criamos um Scanner e usamos o programa inteiro.
System.out.print("Primeiro número: ");print, sem ln: não pula linha, então o que a pessoa digitar aparece logo depois dos dois-pontos.
double a = entrada.nextDouble();Espera a pessoa digitar e apertar Enter, e converte o texto num double (número com casas decimais).
a + " + " + b + " = " + somaJunta números e texto numa frase só (concatenação).
$ java Calculadora.java
=== Calculadora da Ana ===
Primeiro número: 10
Segundo número: 5
10.0 + 5.0 = 15.0

Repare no 10.0: a Ana digitou 10, mas a variável é double, e o Java sempre mostra um double com pelo menos uma casa decimal. Na última etapa você vai ver um desafio para deixar isso mais bonito.

Decimais: no teclado, com vírgula; na tela, com ponto

O Scanner lê os números no formato do idioma do sistema. Na máquina da Ana, em português do Brasil, o separador decimal é a vírgula: 2,5 funciona e 2.5 dá erro. Já o println de um double sempre usa ponto:

$ java Calculadora.java
=== Calculadora da Ana ===
Primeiro número: 2,5
Segundo número: 1,25
2.5 + 1.25 = 3.75
$ java Calculadora.java
=== Calculadora da Ana ===
Primeiro número: 2.5
Exception in thread "main" java.util.InputMismatchException
	at java.base/java.util.Scanner.throwFor(Scanner.java:977)
	at java.base/java.util.Scanner.next(Scanner.java:1632)
	at java.base/java.util.Scanner.nextDouble(Scanner.java:2603)
	at Calculadora.main(Calculadora.java:9)

InputMismatchException quer dizer "a entrada não combina com o que foi pedido": o nextDouble queria um número e encontrou um texto que, em português, não é número. A última linha mostra onde: Calculadora.java, linha 9. Na etapa 4, a calculadora vai aprender a se defender disso.

Missão (etapa 1): o programa já lê o primeiro número. Leia o segundo número na variável b e mostre a soma no formato 10.0 + 5.0 = 15.0. A entrada 10 e 5 já vem preenchida.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Primeiro número: ");
        double a = entrada.nextDouble();
        // peça e leia o segundo número

        // calcule e mostre a soma
    }
}
import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Primeiro número: ");
        double a = entrada.nextDouble();
        System.out.print("Segundo número: ");
        double b = entrada.nextDouble();

        double soma = a + b;
        System.out.println(a + " + " + b + " = " + soma);
    }
}

Etapa 2 · Escolher a operação

Agora a calculadora pergunta qual conta fazer. A operação é um texto ("+", "-"…), lido com entrada.next(), que devolve a próxima "palavra" digitada. Para escolher a conta, um switch com setas: ele compara o texto com cada case e devolve o valor da seta escolhida.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Primeiro número: ");
        double a = entrada.nextDouble();
        System.out.print("Operação (+, -, *, /): ");
        String op = entrada.next();
        System.out.print("Segundo número: ");
        double b = entrada.nextDouble();

        double resultado = switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> {
                System.out.println("Operação desconhecida: " + op);
                yield 0;
            }
        };
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }
}
double resultado =
O switch aqui é uma expressão: ele produz um valor, que vai para resultado.
switch (op)
Olha o valor de op e procura o case igual. Com String, a comparação é pelo conteúdo do texto.
case "+" -> a + b;
Se op for "+", o valor do switch é a + b. A seta -> não "cai" no próximo case: não precisa de break.
default -> { … yield 0; }
Qualquer outro texto. Como aqui há duas instruções, elas vão entre chaves, e yield ("render", "entregar") diz qual valor o switch devolve.
$ java Calculadora.java
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+, -, *, /): *
Segundo número: 4
10.0 * 4.0 = 40.0
$ java Calculadora.java
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+, -, *, /): /
Segundo número: 4
10.0 / 4.0 = 2.5
$ java Calculadora.java
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+, -, *, /): x
Segundo número: 4                         « a operação é inválida, mas ele pede o número mesmo assim
Operação desconhecida: x
10.0 x 4.0 = 0.0                          « e ainda mostra um resultado falso

A divisão 10.0 / 4.0 deu 2.5, e não 2, porque os dois números são double (divisão inteira só acontece entre inteiros; veja Operadores Aritméticos). A terceira execução mostra um defeito: com a operação x, o programa pede o segundo número à toa e mostra 0.0 como se fosse uma resposta. A etapa 3 conserta isso.

Quando dá errado: esqueci o default

Este código tem um erro de propósito.

        double resultado = switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
        };
$ javac Calculadora.java
Calculadora.java:5: error: the switch expression does not cover all possible input values
        double resultado = switch (op) {
                           ^
1 error

"A expressão switch não cobre todos os valores possíveis." Um switch que devolve um valor precisa ter resposta para qualquer texto, e uma String pode ser qualquer coisa. Acrescente um default.

Missão (etapa 2): esta versão só sabe somar e subtrair. Acrescente os case de multiplicação (*) e divisão (/). A entrada da missão é 10, /, 4.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Primeiro número: ");
        double a = entrada.nextDouble();
        System.out.print("Operação (+, -, *, /): ");
        String op = entrada.next();
        System.out.print("Segundo número: ");
        double b = entrada.nextDouble();

        double resultado = switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            default -> {
                System.out.println("Operação desconhecida: " + op);
                yield 0;
            }
        };
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }
}
import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Primeiro número: ");
        double a = entrada.nextDouble();
        System.out.print("Operação (+, -, *, /): ");
        String op = entrada.next();
        System.out.print("Segundo número: ");
        double b = entrada.nextDouble();

        double resultado = switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> {
                System.out.println("Operação desconhecida: " + op);
                yield 0;
            }
        };
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }
}

Etapa 3 · Organizar em métodos

O main está ficando comprido. Vamos separar o programa em métodos: blocos com nome, cada um com uma tarefa. É como numa cozinha: um cozinheiro corta, outro cozinha, outro monta o prato. Ao mesmo tempo, mudamos a ordem: a operação vem primeiro, e se ela for inválida o programa avisa e para, sem pedir os números à toa.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Operação (+, -, *, /): ");
        String op = entrada.next();
        if (!operacaoValida(op)) {
            System.out.println("Operação desconhecida: " + op);
            return;
        }
        double a = lerNumero(entrada, "Primeiro número: ");
        double b = lerNumero(entrada, "Segundo número: ");
        double resultado = calcular(a, op, b);
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}
MétodoRecebeDevolveTarefa
lerNumeroo Scanner e a perguntadoubleMostra a pergunta e lê um número. Assim não repetimos print + nextDouble duas vezes.
operacaoValidaa operaçãobooleantrue se for uma das quatro. case "+", "-", "*", "/" junta vários valores no mesmo case.
calcularos dois números e a operaçãodoubleFaz a conta. Só faz conta: não lê teclado nem escreve na tela. Isso deixa o método fácil de testar e de reaproveitar.
static
O método pertence à classe, e por isso o main (que também é static) pode chamá-lo direto. Veja static.
double
O tipo do valor devolvido (retorno).
calcular(double a, String op, double b)
O nome e os parâmetros: as "entradas" do método.
return switch (op) { … };
Devolve o valor que o switch produzir.
default -> throw new IllegalArgumentException(…)
Se alguém chamar calcular com uma operação inválida, o método lança uma exceção (avisa que recebeu um argumento ilegal) em vez de inventar um resultado. Veja throw e throws.

No main, o ! em !operacaoValida(op) quer dizer "não" (operadores lógicos), e o return sozinho encerra o main, ou seja, o programa.

$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, /): -
Primeiro número: 7
Segundo número: 2
7.0 - 2.0 = 5.0
$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, /): %
Operação desconhecida: %                  « agora ele para antes de pedir os números

Missão (etapa 3): o main chama calcular(a, op, b), mas o método ainda não existe (rode e veja o cannot find symbol). Escreva o método static double calcular(double a, String op, double b) com o switch das quatro operações.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Operação (+, -, *, /): ");
        String op = entrada.next();
        if (!operacaoValida(op)) {
            System.out.println("Operação desconhecida: " + op);
            return;
        }
        double a = lerNumero(entrada, "Primeiro número: ");
        double b = lerNumero(entrada, "Segundo número: ");
        double resultado = calcular(a, op, b);
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    // escreva aqui o método calcular
}
import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Operação (+, -, *, /): ");
        String op = entrada.next();
        if (!operacaoValida(op)) {
            System.out.println("Operação desconhecida: " + op);
            return;
        }
        double a = lerNumero(entrada, "Primeiro número: ");
        double b = lerNumero(entrada, "Segundo número: ");
        double resultado = calcular(a, op, b);
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}

Etapa 4 · Validar o que foi digitado

Hoje, se alguém digita uma palavra no lugar do número, a calculadora "explode":

$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, /): +
Primeiro número: abc
Exception in thread "main" java.util.InputMismatchException
	at java.base/java.util.Scanner.throwFor(Scanner.java:977)
	at java.base/java.util.Scanner.next(Scanner.java:1632)
	at java.base/java.util.Scanner.nextDouble(Scanner.java:2603)
	at Calculadora.lerNumero(Calculadora.java:22)
	at Calculadora.main(Calculadora.java:14)

Leia o stack trace (a "pilha de chamadas") de baixo para cima: o main, na linha 14, chamou o lerNumero, que na linha 22 chamou o nextDouble, que lançou a exceção. As linhas com java.base/ são do próprio Java; as que importam para você são as do seu arquivo.

A solução é perguntar antes de ler. O método hasNextDouble() ("tem um próximo double?") olha o que foi digitado sem consumir e devolve true se aquilo é um número válido. Só mudamos o método lerNumero; o resto do programa fica igual:

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        while (!entrada.hasNextDouble()) {
            String lixo = entrada.next();
            System.out.println("\"" + lixo + "\" não é um número. Tente de novo.");
            System.out.print(pergunta);
        }
        return entrada.nextDouble();
    }
while (!entrada.hasNextDouble())
"Enquanto o que foi digitado não for um número…" O hasNextDouble espera a pessoa digitar, se for preciso, mas não tira nada da entrada.
String lixo = entrada.next();
Tira a palavra inválida da entrada. Esta linha é essencial: sem ela, o hasNextDouble olharia para o mesmo "abc" para sempre, num laço infinito.
"\"" + lixo + "\" …"
\" é uma aspa dentro do texto (caracteres especiais).
return entrada.nextDouble();
Quando o laço termina, com certeza há um número esperando: agora é seguro ler.
$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, /): +
Primeiro número: abc
"abc" não é um número. Tente de novo.
Primeiro número: dez
"dez" não é um número. Tente de novo.
Primeiro número: 10
Segundo número: 2.5                       « com ponto: em pt_BR, não é número
"2.5" não é um número. Tente de novo.
Segundo número: 2,5
10.0 + 2.5 = 12.5
Uma família de métodos "has"

Para cada nextXxx existe um hasNextXxx: hasNextInt(), hasNextLine(), hasNext()… Use-os sempre que a entrada vier de uma pessoa. A tabela completa está em Ler do Teclado (Scanner).

Missão (etapa 4): rode primeiro como está: a entrada da missão tem abc no lugar do primeiro número e o programa termina com InputMismatchException. Depois mude o lerNumero para usar hasNextDouble e mostrar "abc" não é um número. Tente de novo.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Operação (+, -, *, /): ");
        String op = entrada.next();
        if (!operacaoValida(op)) {
            System.out.println("Operação desconhecida: " + op);
            return;
        }
        double a = lerNumero(entrada, "Primeiro número: ");
        double b = lerNumero(entrada, "Segundo número: ");
        double resultado = calcular(a, op, b);
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}
import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Operação (+, -, *, /): ");
        String op = entrada.next();
        if (!operacaoValida(op)) {
            System.out.println("Operação desconhecida: " + op);
            return;
        }
        double a = lerNumero(entrada, "Primeiro número: ");
        double b = lerNumero(entrada, "Segundo número: ");
        double resultado = calcular(a, op, b);
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        while (!entrada.hasNextDouble()) {
            String lixo = entrada.next();
            System.out.println("\"" + lixo + "\" não é um número. Tente de novo.");
            System.out.print(pergunta);
        }
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}

Etapa 5 · Não dividir por zero

Na escola você aprendeu que não existe divisão por zero. O Java, com double, não reclama: ele responde com valores especiais.

$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, /): /
Primeiro número: 10
Segundo número: 0
10.0 / 0.0 = Infinity                     « "infinito"
$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, /): /
Primeiro número: -10
Segundo número: 0
-10.0 / 0.0 = -Infinity
$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, /): /
Primeiro número: 0
Segundo número: 0
0.0 / 0.0 = NaN                           « Not a Number: "não é um número"

Com números inteiros (int), a mesma conta não dá infinito: dá erro. Compare no JShell:

$ jshell
|  Welcome to JShell -- Version 25.0.4.1
|  For an introduction type: /help intro

jshell> 10 / 0
|  Exception java.lang.ArithmeticException: / by zero
|        at (#1:1)

jshell> 10.0 / 0
$2 ==> Infinity

Para uma calculadora de gente, "Infinity" não serve. Vamos conferir o divisor antes de calcular. No main, logo depois de ler o segundo número:

        double b = lerNumero(entrada, "Segundo número: ");
        if (op.equals("/") && b == 0) {
            System.out.println("Não dá para dividir por zero.");
            return;
        }
        double resultado = calcular(a, op, b);
op.equals("/")
Compara o conteúdo do texto. Nunca use == para comparar String (veja Strings).
&&
"E": as duas condições precisam ser verdadeiras.
b == 0
Para números, == está certo. 0 e 0.0 são iguais aqui.
return;
Encerra o programa sem calcular. Na próxima etapa, ele vai virar continue.
$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, /): /
Primeiro número: 10
Segundo número: 0
Não dá para dividir por zero.

Missão (etapa 5): com a entrada /, 10, 0, o programa mostra Infinity. Acrescente o if que mostra Não dá para dividir por zero. e encerra.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Operação (+, -, *, /): ");
        String op = entrada.next();
        if (!operacaoValida(op)) {
            System.out.println("Operação desconhecida: " + op);
            return;
        }
        double a = lerNumero(entrada, "Primeiro número: ");
        double b = lerNumero(entrada, "Segundo número: ");
        double resultado = calcular(a, op, b);
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        while (!entrada.hasNextDouble()) {
            String lixo = entrada.next();
            System.out.println("\"" + lixo + "\" não é um número. Tente de novo.");
            System.out.print(pergunta);
        }
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}
import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Operação (+, -, *, /): ");
        String op = entrada.next();
        if (!operacaoValida(op)) {
            System.out.println("Operação desconhecida: " + op);
            return;
        }
        double a = lerNumero(entrada, "Primeiro número: ");
        double b = lerNumero(entrada, "Segundo número: ");
        if (op.equals("/") && b == 0) {
            System.out.println("Não dá para dividir por zero.");
            return;
        }
        double resultado = calcular(a, op, b);
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        while (!entrada.hasNextDouble()) {
            String lixo = entrada.next();
            System.out.println("\"" + lixo + "\" não é um número. Tente de novo.");
            System.out.print(pergunta);
        }
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}

Etapa 6 · Repetir até "sair"

Uma calculadora que faz uma conta e fecha não ajuda muito. A última etapa coloca o trabalho dentro de um laço while (true) ("enquanto verdadeiro", ou seja, para sempre) e dá duas saídas de emergência: break e continue. Só o main muda:

    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        while (true) {
            System.out.print("Operação (+, -, *, / ou sair): ");
            String op = entrada.next();
            if (op.equals("sair")) {
                break;
            }
            if (!operacaoValida(op)) {
                System.out.println("Operação desconhecida: " + op);
                continue;
            }
            double a = lerNumero(entrada, "Primeiro número: ");
            double b = lerNumero(entrada, "Segundo número: ");
            if (op.equals("/") && b == 0) {
                System.out.println("Não dá para dividir por zero.");
                continue;
            }
            double resultado = calcular(a, op, b);
            System.out.println(a + " " + op + " " + b + " = " + resultado);
            System.out.println();
        }
        System.out.println("Até logo!");
    }
InstruçãoO que aconteceUsada quando
while (true) { … }Repete o bloco sem parar, até um break.Sempre: é o "programa ligado".
break;Sai do laço. O programa continua depois da } do while e mostra "Até logo!".A pessoa digitou sair.
continue;Pula o resto desta volta e começa a próxima: pergunta a operação de novo.Operação inválida ou divisão por zero.
System.out.println();Escreve uma linha vazia, para separar uma conta da outra.Depois de cada resultado.

Os dois return da etapa anterior viraram continue: agora um erro não fecha o programa, só pula para a próxima conta. A sessão completa está em "O que vamos construir", no começo da página; aqui, uma bem curta:

$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, / ou sair): +
Primeiro número: 10
Segundo número: 5
10.0 + 5.0 = 15.0

Operação (+, -, *, / ou sair): sair
Até logo!
Quando dá errado: Ctrl+D no lugar de "sair"

No terminal do Linux, Ctrl+D significa "acabou a entrada". Se a Ana apertar isso na pergunta da operação, o next() não tem mais nada para ler:

$ java Calculadora.java
=== Calculadora da Ana ===
Operação (+, -, *, / ou sair): +
Primeiro número: 10
Segundo número: 5
10.0 + 5.0 = 15.0

Operação (+, -, *, / ou sair): Exception in thread "main" java.util.NoSuchElementException
	at java.base/java.util.Scanner.throwFor(Scanner.java:975)
	at java.base/java.util.Scanner.next(Scanner.java:1516)
	at Calculadora.main(Calculadora.java:10)

NoSuchElementException = "não existe tal elemento". Para tratar, troque a condição do laço: while (entrada.hasNext()) só entra se ainda houver algo para ler. Fica como desafio.

Missão (etapa 6): coloque a parte de "perguntar e calcular" dentro de um while (true), saia com break quando a operação for sair e mostre Até logo! no fim. Troque os return por continue. A entrada da missão faz duas contas e sai.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        System.out.print("Operação (+, -, *, / ou sair): ");
        String op = entrada.next();
        if (!operacaoValida(op)) {
            System.out.println("Operação desconhecida: " + op);
            return;
        }
        double a = lerNumero(entrada, "Primeiro número: ");
        double b = lerNumero(entrada, "Segundo número: ");
        if (op.equals("/") && b == 0) {
            System.out.println("Não dá para dividir por zero.");
            return;
        }
        double resultado = calcular(a, op, b);
        System.out.println(a + " " + op + " " + b + " = " + resultado);
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        while (!entrada.hasNextDouble()) {
            String lixo = entrada.next();
            System.out.println("\"" + lixo + "\" não é um número. Tente de novo.");
            System.out.print(pergunta);
        }
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}
import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        while (true) {
            System.out.print("Operação (+, -, *, / ou sair): ");
            String op = entrada.next();
            if (op.equals("sair")) {
                break;
            }
            if (!operacaoValida(op)) {
                System.out.println("Operação desconhecida: " + op);
                continue;
            }
            double a = lerNumero(entrada, "Primeiro número: ");
            double b = lerNumero(entrada, "Segundo número: ");
            if (op.equals("/") && b == 0) {
                System.out.println("Não dá para dividir por zero.");
                continue;
            }
            double resultado = calcular(a, op, b);
            System.out.println(a + " " + op + " " + b + " = " + resultado);
            System.out.println();
        }
        System.out.println("Até logo!");
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        while (!entrada.hasNextDouble()) {
            String lixo = entrada.next();
            System.out.println("\"" + lixo + "\" não é um número. Tente de novo.");
            System.out.print(pergunta);
        }
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}

O código completo

A calculadora final, com as seis etapas juntas. Ela tem 57 linhas, quatro métodos e nenhum jeito fácil de quebrar.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        while (true) {
            System.out.print("Operação (+, -, *, / ou sair): ");
            String op = entrada.next();
            if (op.equals("sair")) {
                break;
            }
            if (!operacaoValida(op)) {
                System.out.println("Operação desconhecida: " + op);
                continue;
            }
            double a = lerNumero(entrada, "Primeiro número: ");
            double b = lerNumero(entrada, "Segundo número: ");
            if (op.equals("/") && b == 0) {
                System.out.println("Não dá para dividir por zero.");
                continue;
            }
            double resultado = calcular(a, op, b);
            System.out.println(a + " " + op + " " + b + " = " + resultado);
            System.out.println();
        }
        System.out.println("Até logo!");
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        while (!entrada.hasNextDouble()) {
            String lixo = entrada.next();
            System.out.println("\"" + lixo + "\" não é um número. Tente de novo.");
            System.out.print(pergunta);
        }
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}

Desafios extras

  1. Resto da divisão. Acrescente a operação % (resto; veja Operadores Aritméticos). Lembre-se de mudar os dois métodos e o texto da pergunta. Resto por zero também deve ser recusado.
  2. Potência. Acrescente ^ usando Math.pow(a, b) (Math).
  3. Resultado bonito. Mostre o resultado com duas casas decimais usando System.out.printf("%.2f%n", resultado). Em pt_BR, ele usa vírgula: 3,50 (veja Formatar Texto).
  4. Sair sem exceção. Troque while (true) por while (entrada.hasNext()) para que Ctrl+D encerre o programa sem erro.
  5. Memória. Guarde o último resultado numa variável e deixe a pessoa digitar m no lugar de um número para usá-lo.

Desafio 1 no editor: acrescente a operação %. Com a entrada %, 10, 3, a calculadora deve mostrar 10.0 % 3.0 = 1.0.

import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        while (true) {
            System.out.print("Operação (+, -, *, / ou sair): ");
            String op = entrada.next();
            if (op.equals("sair")) {
                break;
            }
            if (!operacaoValida(op)) {
                System.out.println("Operação desconhecida: " + op);
                continue;
            }
            double a = lerNumero(entrada, "Primeiro número: ");
            double b = lerNumero(entrada, "Segundo número: ");
            if (op.equals("/") && b == 0) {
                System.out.println("Não dá para dividir por zero.");
                continue;
            }
            double resultado = calcular(a, op, b);
            System.out.println(a + " " + op + " " + b + " = " + resultado);
            System.out.println();
        }
        System.out.println("Até logo!");
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        while (!entrada.hasNextDouble()) {
            String lixo = entrada.next();
            System.out.println("\"" + lixo + "\" não é um número. Tente de novo.");
            System.out.print(pergunta);
        }
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}
import java.util.Scanner;

public class Calculadora {
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);
        System.out.println("=== Calculadora da Ana ===");

        while (true) {
            System.out.print("Operação (+, -, *, /, % ou sair): ");
            String op = entrada.next();
            if (op.equals("sair")) {
                break;
            }
            if (!operacaoValida(op)) {
                System.out.println("Operação desconhecida: " + op);
                continue;
            }
            double a = lerNumero(entrada, "Primeiro número: ");
            double b = lerNumero(entrada, "Segundo número: ");
            if ((op.equals("/") || op.equals("%")) && b == 0) {
                System.out.println("Não dá para dividir por zero.");
                continue;
            }
            double resultado = calcular(a, op, b);
            System.out.println(a + " " + op + " " + b + " = " + resultado);
            System.out.println();
        }
        System.out.println("Até logo!");
    }

    static double lerNumero(Scanner entrada, String pergunta) {
        System.out.print(pergunta);
        while (!entrada.hasNextDouble()) {
            String lixo = entrada.next();
            System.out.println("\"" + lixo + "\" não é um número. Tente de novo.");
            System.out.print(pergunta);
        }
        return entrada.nextDouble();
    }

    static boolean operacaoValida(String op) {
        return switch (op) {
            case "+", "-", "*", "/", "%" -> true;
            default -> false;
        };
    }

    static double calcular(double a, String op, double b) {
        return switch (op) {
            case "+" -> a + b;
            case "-" -> a - b;
            case "*" -> a * b;
            case "/" -> a / b;
            case "%" -> a % b;
            default -> throw new IllegalArgumentException("Operação desconhecida: " + op);
        };
    }
}

Exercícios

1. Na máquina da Ana (pt_BR), o que acontece se ela digitar 2.5 num nextDouble()?

2. No lerNumero, o que acontece se você apagar a linha String lixo = entrada.next(); e alguém digitar "abc"?

3. O que continue faz dentro do while da calculadora?

4. Quanto vale 10.0 / 0 em Java?

Complete o método para que ele só leia quando houver um número esperando:

while (!entrada.) {

Resumo

  • Um Scanner(System.in) lê o teclado; nextDouble() lê números (com vírgula em pt_BR) e next() lê uma palavra.
  • O switch com setas devolve um valor e precisa de default quando o tipo é String.
  • Métodos pequenos, cada um com uma tarefa, deixam o main fácil de ler.
  • hasNextDouble() confere antes de ler; o next() descarta o que não serve.
  • Divisão de double por zero dá Infinity ou NaN; de int, dá ArithmeticException. Confira o divisor antes.
  • while (true) + break + continue fazem o "menu que repete".

Próximo projeto: Jogo da Adivinhação, com números aleatórios e enums.