Java switch

Quando você precisa comparar uma variável com uma lista de valores exatos (o dia 1 é domingo, o 2 é segunda…; o comando "novo", "ajuda" ou "sair"), uma escada de else if fica comprida e repetitiva. O switch ("chave", no sentido de "comutador") faz isso de forma organizada: você diz qual variável examinar e lista os casos.

Você vai aprender
  • A estrutura do switch clássico: case, break e default.
  • O que é o fall-through (o "cair para o próximo caso") e o que acontece de verdade quando você esquece o break.
  • Como juntar vários case que fazem a mesma coisa.
  • switch com String, char e enum.
  • Os erros reais: duplicate case label, constant expression required, NullPointerException e o switch com double.

Esta aula trata do switch "clássico", que existe desde a primeira versão do Java e que você vai encontrar em todo código antigo. A próxima aula, switch com Setas, mostra a forma moderna (Java 14+), mais curta e mais segura. Vale aprender as duas.


A ideia: um painel de botões

Pense no painel de um elevador: você aperta o número do andar, e o elevador vai direto para ele, sem parar em cada andar perguntando "é este?". O switch funciona assim: olha o valor da variável e salta direto para o case correspondente. Se não houver botão para aquele valor, vai para o default ("padrão").


O switch clássico

public class Main {
    public static void main(String[] args) {
        int dia = 3;
        switch (dia) {
            case 1:
                System.out.println("Domingo");
                break;
            case 2:
                System.out.println("Segunda");
                break;
            case 3:
                System.out.println("Terça");
                break;
            case 4:
                System.out.println("Quarta");
                break;
            case 5:
                System.out.println("Quinta");
                break;
            case 6:
                System.out.println("Sexta");
                break;
            case 7:
                System.out.println("Sábado");
                break;
            default:
                System.out.println("Dia inválido");
        }
        System.out.println("Fim");
    }
}
$ java Main.java
Terça
Fim
switch (dia)
A variável que será examinada (o "seletor"), entre parênteses. Pode ser int, char, short, byte, String ou enum.
{ … }
As chaves envolvem todos os casos.
case 3:
"Caso o valor seja 3, comece aqui". Depois do case vem um valor fixo (um literal ou uma constante) e dois-pontos. Os case funcionam como rótulos, etiquetas que marcam pontos de entrada.
break;
"Quebre", "saia": termina o switch e pula para depois do }. Sem ele, acontece algo surpreendente (a próxima seção).
default:
O "senão": roda quando nenhum case bateu. É opcional; sem ele, se nenhum caso bater, nada acontece. Por costume, fica no fim e não precisa de break.

Com dia = 3, o Java saltou direto para case 3:, mostrou Terça, encontrou o break e saiu. Com dia = 9, iria para o default e mostraria Dia inválido.

O mesmo programa com else if teria sete comparações dia == 1, dia == 2… O switch deixa claro, já na primeira linha, que tudo gira em torno de uma variável.


O fall-through: esqueci o break

Aqui está a característica mais famosa (e mais perigosa) do switch clássico. Os case são só pontos de entrada: o Java entra no case certo e, a partir dali, executa tudo para baixo, atravessando os outros case, até encontrar um break ou o fim do switch. Isso se chama fall-through ("cair através"). Veja o mesmo programa sem os break:

Este código tem um erro de propósito (de lógica: ele compila).

public class Main {
    public static void main(String[] args) {
        int dia = 3;
        switch (dia) {
            case 1:
                System.out.println("Domingo");
            case 2:
                System.out.println("Segunda");
            case 3:
                System.out.println("Terça");
            case 4:
                System.out.println("Quarta");
            case 5:
                System.out.println("Quinta");
            case 6:
                System.out.println("Sexta");
            case 7:
                System.out.println("Sábado");
            default:
                System.out.println("Dia inválido");
        }
        System.out.println("Fim");
    }
}
$ java Main.java
Terça           « entrou no case 3, como deveria…
Quarta          « …e continuou descendo por todos os outros
Quinta
Sexta
Sábado
Dia inválido    « até o default!
Fim

O javac não reclama de nada: o código é válido. Pense no elevador sem freio: você apertou o 3, ele chegou ao 3… e continuou descendo até o térreo. O break é o freio. Repare que os casos acima do 3 (Domingo, Segunda) não rodaram: o Java salta para o ponto de entrada e só desce a partir dali.

Todo case termina com break

No switch clássico, a regra é: cada caso termina com break;, a menos que você queira de propósito que ele continue no próximo (e, nesse caso, deixe um comentário dizendo isso). Esquecer o break é tão comum que o javac tem um aviso para isso: compile com javac -Xlint:fallthrough. O switch com setas acabou com esse problema.

Com o programa acima (sem os break), o aviso fica assim:

$ javac -Xlint:fallthrough Main.java
Main.java:7: warning: [fallthrough] possible fall-through into case
            case 2:
            ^
Main.java:9: warning: [fallthrough] possible fall-through into case
            case 3:
            ^
Main.java:11: warning: [fallthrough] possible fall-through into case
            case 4:
            ^
Main.java:13: warning: [fallthrough] possible fall-through into case
            case 5:
            ^
Main.java:15: warning: [fallthrough] possible fall-through into case
            case 6:
            ^
Main.java:17: warning: [fallthrough] possible fall-through into case
            case 7:
            ^
Main.java:19: warning: [fallthrough] possible fall-through into case
            default:
            ^
7 warnings

Possible fall-through into case = "possível queda para dentro do case": um aviso para cada case em que se pode "cair" vindo do caso de cima. São avisos (warnings), não erros: o .class é gerado do mesmo jeito.

Missão: rode e veja o fall-through mostrar três linhas em vez de uma. Coloque os break que faltam para que a saída seja só Terça e depois Fim.

public class Main {
    public static void main(String[] args) {
        int dia = 3;
        switch (dia) {
            case 1:
                System.out.println("Domingo");
            case 2:
                System.out.println("Segunda");
            case 3:
                System.out.println("Terça");
            case 4:
                System.out.println("Quarta");
            default:
                System.out.println("Outro dia");
        }
        System.out.println("Fim");
    }
}
public class Main {
    public static void main(String[] args) {
        int dia = 3;
        switch (dia) {
            case 1:
                System.out.println("Domingo");
                break;
            case 2:
                System.out.println("Segunda");
                break;
            case 3:
                System.out.println("Terça");
                break;
            case 4:
                System.out.println("Quarta");
                break;
            default:
                System.out.println("Outro dia");
        }
        System.out.println("Fim");
    }
}

Fall-through de propósito

Às vezes o fall-through é exatamente o que você quer: cada nível ganha os bônus dele e os de todos os níveis abaixo. Repare na ordem dos casos, do maior para o menor:

public class Main {
    public static void main(String[] args) {
        int nivel = 2;
        System.out.println("Bônus do nível " + nivel + ":");
        switch (nivel) {
            case 3:
                System.out.println("+ escudo");
            case 2:
                System.out.println("+ espada");
            case 1:
                System.out.println("+ poção");
        }
    }
}
$ java Main.java
Bônus do nível 2:
+ espada        « entrou no 2…
+ poção         « …e caiu no 1, de propósito

Funciona, mas quem lê o código não sabe se a falta do break foi intencional. Se usar esse truque, deixe um comentário como // continua no próximo caso.


Vários case com o mesmo resultado

O uso mais comum e legítimo do fall-through é empilhar case vazios: todos caem no mesmo bloco. Desde o Java 14, há também uma forma mais curta: vários valores separados por vírgula num case só.

public class Main {
    public static void main(String[] args) {
        int mes = 4;
        int dias;
        switch (mes) {
            case 2:
                dias = 28;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                dias = 30;
                break;
            default:
                dias = 31;
        }
        System.out.println("O mês " + mes + " tem " + dias + " dias");

        int dia = 7;
        switch (dia) {
            case 1, 7:
                System.out.println("Fim de semana");
                break;
            case 2, 3, 4, 5, 6:
                System.out.println("Dia útil");
                break;
        }
    }
}
$ java Main.java
O mês 4 tem 30 dias
Fim de semana
case 4: case 6: case 9: case 11:
Quatro pontos de entrada seguidos, sem nada entre eles. Entrando por qualquer um, o Java desce até dias = 30; e para no break.
case 1, 7:
Java 14+ A mesma ideia numa linha: "caso seja 1 ou 7".
default: dias = 31;
Todos os outros meses. Graças ao default, o javac sabe que dias sempre recebe um valor. (Fevereiro com 28 dias é uma simplificação: anos bissextos são assunto de Datas e Horas.)

switch com String

Desde o Java 7, o seletor pode ser uma String. Os case são comparados com equals (o conteúdo), então aqui não existe a pegadinha do ==. A comparação diferencia maiúsculas de minúsculas:

public class Main {
    public static void main(String[] args) {
        String comando = "ajuda";
        switch (comando) {
            case "novo":
                System.out.println("Criando um novo arquivo...");
                break;
            case "ajuda":
                System.out.println("Comandos: novo, ajuda, sair");
                break;
            case "sair":
                System.out.println("Até logo!");
                break;
            default:
                System.out.println("Comando desconhecido: " + comando);
        }
        String outro = "AJUDA";
        switch (outro) {
            case "ajuda":
                System.out.println("achou");
                break;
            default:
                System.out.println("Comando desconhecido: " + outro);
        }
        switch (outro.toLowerCase()) {
            case "ajuda":
                System.out.println("achou depois do toLowerCase");
                break;
        }
    }
}
$ java Main.java
Comandos: novo, ajuda, sair
Comando desconhecido: AJUDA      « "AJUDA" não é "ajuda"
achou depois do toLowerCase      « padronizar antes resolve

Quando o texto vem de uma pessoa, padronize no próprio seletor: switch (comando.strip().toLowerCase()) (veja Métodos de String). E cuidado com null: um switch clássico com String null lança NullPointerException (veja "Quando dá errado").


switch com char

public class Main {
    public static void main(String[] args) {
        char conceito = 'B';
        switch (conceito) {
            case 'A':
                System.out.println("Excelente");
                break;
            case 'B':
                System.out.println("Bom");
                break;
            case 'C':
                System.out.println("Regular");
                break;
            default:
                System.out.println("Insuficiente");
        }
    }
}
$ java Main.java
Bom

Com char, os valores dos case vão entre aspas simples ('A'), como todo char (veja char e boolean).


switch com enum

Um enum (de enumeração) é um tipo com uma lista fechada de valores possíveis, como as cores de um semáforo. Ele tem aula própria em Enums; aqui, basta ver que ele combina perfeitamente com o switch:

public class Main {
    enum Semaforo { VERDE, AMARELO, VERMELHO }

    public static void main(String[] args) {
        Semaforo sinal = Semaforo.AMARELO;
        switch (sinal) {
            case VERDE:
                System.out.println("Pode seguir");
                break;
            case AMARELO:
                System.out.println("Atenção, vai fechar");
                break;
            case VERMELHO:
                System.out.println("Pare!");
                break;
        }
    }
}
$ java Main.java
Atenção, vai fechar
enum Semaforo { VERDE, AMARELO, VERMELHO }
Cria o tipo Semaforo, que só pode valer uma dessas três coisas.
Semaforo sinal = Semaforo.AMARELO;
Fora do switch, o valor é escrito com o nome do tipo na frente.
case AMARELO:
Dentro do case, só o nome (AMARELO), sem Semaforo. na frente: o Java já sabe de que tipo se trata.

Com enums, o switch moderno ainda verifica se você tratou todos os valores; veja switch com Setas.


switch ou if/else if?

SituaçãoUse
Uma variável comparada com vários valores exatosswitch
Faixas de valores (nota >= 7, idade < 18)if/else if (o case clássico não aceita comparações)
Condições sobre variáveis diferentesif/else if
Valores double, long ou booleanif/else if
Escolher um valor conforme o casoswitch com setas (como expressão)
Testar o tipo de um objetoPattern Matching no switch

Quando dá errado

Apareceram várias mensagens em vez de uma

O que aconteceu: fall-through: faltou o break no fim de um ou mais case. O Java entrou no caso certo e continuou descendo.

Como resolver: coloque break; no fim de cada caso. Para o javac avisar, compile com -Xlint:fallthrough. Ou passe para o switch com setas, que não tem fall-through.

error: duplicate case label

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

public class Main {
    public static void main(String[] args) {
        int opcao = 1;
        switch (opcao) {
            case 1:
                System.out.println("Um");
                break;
            case 1:
                System.out.println("Um de novo");
                break;
        }
    }
}
$ javac Main.java
Main.java:8: error: duplicate case label
            case 1:
            ^
1 error

O que aconteceu: "rótulo de case duplicado": dois case com o mesmo valor. O Java não saberia para qual saltar. Costuma acontecer ao copiar e colar um caso e esquecer de trocar o valor.

Como resolver: troque o valor ou junte os dois blocos num só.

error: constant expression required

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

public class Main {
    public static void main(String[] args) {
        int limite = 10;
        int valor = 10;
        switch (valor) {
            case limite:
                System.out.println("No limite");
                break;
        }
    }
}
$ javac Main.java
Main.java:6: error: constant expression required
            case limite:
                 ^
1 error

O que aconteceu: "exige-se uma expressão constante". Os valores dos case precisam ser conhecidos na hora de compilar, e uma variável comum pode mudar.

Como resolver: use um literal (case 10:) ou uma constante: final int LIMITE = 10; (veja Constantes). Se o valor realmente varia, use if.

error: incompatible types: boolean cannot be converted to int (comparação no case)

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

public class Main {
    public static void main(String[] args) {
        int idade = 20;
        switch (idade) {
            case idade >= 18:
                System.out.println("Adulto");
                break;
        }
    }
}
$ javac Main.java
Main.java:5: error: incompatible types: boolean cannot be converted to int
            case idade >= 18:
                       ^
1 error

O que aconteceu: o case compara o seletor (um int) com um valor; idade >= 18 é um boolean.

Como resolver: faixas de valores pedem if/else if. (No Java 21+, o switch com padrões aceita condições com when; veja Pattern Matching.)

error: primitive patterns are a preview feature (switch com double ou boolean)

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

public class Main {
    public static void main(String[] args) {
        double nota = 7.0;
        switch (nota) {
            case 7.0:
                System.out.println("Sete");
                break;
        }
    }
}
$ javac Main.java
Main.java:4: error: primitive patterns are a preview feature and are disabled by default.
        switch (nota) {
               ^
  (use --enable-preview to enable primitive patterns)
1 error

O que aconteceu: o switch aceita int, char, short, byte, String e enum (e objetos, com padrões). double, float, long e boolean ficam de fora. A mensagem do JDK 25 conta uma novidade: um recurso em teste (preview feature, "recurso em pré-estreia") permitirá esses tipos no futuro, mas ele vem desligado e não deve ser usado em código normal.

Como resolver: use if/else if. Comparar double com == já é arriscado (veja double e float).

NullPointerException num switch com String

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

public class Main {
    public static void main(String[] args) {
        String comando = null;
        switch (comando) {
            case "sair":
                System.out.println("Até logo!");
                break;
            default:
                System.out.println("Outro comando");
        }
    }
}
$ java Main.java
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.hashCode()" because "<local2>" is null
	at Main.main(Main.java:4)

O que aconteceu: o switch clássico não aceita null, nem mesmo no default. A mensagem fala de hashCode() porque, por dentro, o switch com String calcula um "código" do texto para saltar mais rápido, e não dá para calcular o código de null. O <local2> é uma variável auxiliar que o javac criou para isso.

Como resolver: teste antes: if (comando != null) { switch … }. No Java 21+, o switch pode ter um case null (veja Pattern Matching).

error: variable msg is already defined in method main(String[])

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

public class Main {
    public static void main(String[] args) {
        int opcao = 2;
        switch (opcao) {
            case 1:
                String msg = "um";
                System.out.println(msg);
                break;
            case 2:
                String msg = "dois";
                System.out.println(msg);
                break;
        }
    }
}
$ javac Main.java
Main.java:10: error: variable msg is already defined in method main(String[])
                String msg = "dois";
                       ^
1 error

O que aconteceu: no switch clássico, todos os case dividem um único bloco (as chaves do switch). Para o Java, as duas msg estão no mesmo lugar: "a variável msg já foi definida".

Como resolver: declare a variável uma vez, antes do switch, ou coloque cada caso entre chaves próprias: case 1: { String msg = "um"; … break; }. No switch com setas, cada caso já tem o seu bloco.


Referência

ParteO que faz
switch (x) { … }Examina o valor de x (int, char, short, byte, String, enum)
case valor:Ponto de entrada quando x é igual a valor (constante)
case a, b, c:Vários valores no mesmo caso (Java 14+)
case a: case b:Casos empilhados (fall-through intencional)
break;Sai do switch
default:Quando nenhum case bate (opcional)
Sem breakContinua executando os casos de baixo (fall-through)

Exercícios

Missão final: na feira da Ana, maçã custa R$ 3; banana e pera custam R$ 2 (use dois case juntos); qualquer outra fruta mostra Fruta em falta. Escreva o switch. Com fruta = "banana", a saída deve ser Preço: R$ 2.

public class Main {
    public static void main(String[] args) {
        String fruta = "banana";
        // escreva o switch
    }
}
public class Main {
    public static void main(String[] args) {
        String fruta = "banana";
        switch (fruta) {
            case "maçã":
                System.out.println("Preço: R$ 3");
                break;
            case "banana":
            case "pera":
                System.out.println("Preço: R$ 2");
                break;
            default:
                System.out.println("Fruta em falta");
        }
    }
}

1. Sair do switch

Qual palavra encerra um case e sai do switch?

2. O caso que sobra

Qual palavra marca o bloco que roda quando nenhum case bate?

3. Com int x = 2;, o que este código mostra?

switch (x) {
    case 1: System.out.println("um");
    case 2: System.out.println("dois");
    case 3: System.out.println("três");
}

4. Qual destes não pode ser o seletor de um switch clássico?

5. Para classificar uma nota em faixas (≥ 9, ≥ 7, ≥ 5), o melhor é…


Resumo

  • switch (x) salta direto para o case com o valor de x; default pega o resto.
  • Os case são pontos de entrada: sem break, a execução continua nos casos de baixo (fall-through). Termine cada caso com break;.
  • Casos empilhados (case 4: case 6:) ou com vírgula (case 4, 6:, Java 14+) compartilham o mesmo bloco.
  • Funciona com int, char, short, byte, String (comparada com equals) e enum; não com double, long, boolean.
  • Os valores dos case são constantes; faixas e condições pedem if.
  • String null no seletor dá NullPointerException.