Java Operadores Lógicos

Uma comparação responde uma pergunta simples: "a idade é 18 ou mais?". Mas as decisões reais costumam ter várias condições: "pode dirigir quem tem 18 anos e carteira"; "paga meia quem é estudante ou idoso". Os operadores lógicos juntam vários boolean num só. Nesta aula você vai conhecer os três principais (&&, || e !), o "curto-circuito" que eles fazem e os primos &, | e ^.

Você vai aprender
  • && (e), || (ou) e ! (não), com as tabelas-verdade.
  • Como combinar várias condições numa expressão legível.
  • O curto-circuito: quando o Java nem olha a segunda condição, e por que isso evita erros.
  • A diferença entre && e &, entre || e |, e o ou-exclusivo ^.

A ideia: "e", "ou" e "não"

Você usa esses operadores todos os dias em português:

  • "Vou à praia se fizer sol e eu estiver de folga." Precisa das duas coisas.
  • "Aceito pagamento em dinheiro ou cartão." Basta uma.
  • "Entro na piscina se não estiver chovendo." Inverte a condição.

Em Java, cada um tem um símbolo. Os operandos precisam ser boolean (valores ou comparações) e o resultado também é boolean:

OperadorNomeLê-seDá true quando…
a && bE lógico (AND)"a e b"os dois são true
a || bOu lógico (OR)"a ou b"pelo menos um é true
!aNão lógico (NOT)"não a"a é false (inverte)

O | é a "barra vertical": fica na mesma tecla da barra invertida \, com Shift (no teclado brasileiro ABNT2, é a tecla à esquerda do Z). O & é o "e comercial" (Shift + 7).

public class Main {
    public static void main(String[] args) {
        int idade = 20;
        boolean temCarteira = false;
        System.out.println(idade >= 18 && temCarteira);
        System.out.println(idade >= 18 || temCarteira);
        System.out.println(!temCarteira);
        boolean podeDirigir = idade >= 18 && temCarteira;
        System.out.println("Pode dirigir? " + podeDirigir);
    }
}
$ javac Main.java && java Main
false              « 20 >= 18 é true, mas temCarteira é false: "e" exige os dois
true               « "ou": basta a idade
true               « !false
Pode dirigir? false
idade >= 18
Primeira condição: uma comparação que dá true.
&&
"E": o resultado só é true se as duas condições forem.
temCarteira
Segunda condição: uma variável boolean, que vale false.

Repare que não foi preciso usar parênteses em idade >= 18 && temCarteira: as comparações são feitas antes dos operadores lógicos (Precedência).


Tabelas-verdade

Como cada operando só pode ser true ou false, dá para listar todas as combinações. Essa lista se chama tabela-verdade. Este programa testa todas, no JDK de verdade:

public class Main {
    public static void main(String[] args) {
        System.out.println(true && true);
        System.out.println(true && false);
        System.out.println(false && true);
        System.out.println(false && false);
        System.out.println(true || true);
        System.out.println(true || false);
        System.out.println(false || true);
        System.out.println(false || false);
        System.out.println(!true);
        System.out.println(!false);
        System.out.println(true ^ true);
        System.out.println(true ^ false);
        System.out.println(false ^ true);
        System.out.println(false ^ false);
    }
}
$ javac Main.java && java Main
true
false
false
false
true
true
true
false
false
true
false
true
true
false

Organizando a saída em tabelas (o ^ é explicado mais adiante):

aba && ba || ba ^ b
truetruetruetruefalse
truefalsefalsetruetrue
falsetruefalsetruetrue
falsefalsefalsefalsefalse
a!a
truefalse
falsetrue

Para memorizar: && só dá true numa linha (tudo verdadeiro); || só dá false numa linha (tudo falso).


Combinando condições

Você pode encadear quantas condições quiser. A Ana usa isso em vários lugares:

public class Main {
    public static void main(String[] args) {
        int idade = 70;
        boolean estudante = false;
        boolean meia = idade < 18 || idade >= 60 || estudante;
        System.out.println("Meia-entrada? " + meia);
        int nota = 8;
        int faltas = 3;
        boolean aprovado = nota >= 6 && faltas <= 10;
        System.out.println("Aprovado? " + aprovado);
        boolean fimDeSemana = true;
        boolean feriado = false;
        System.out.println("Dia útil? " + !(fimDeSemana || feriado));
        System.out.println("Dia útil? " + (!fimDeSemana && !feriado));
        boolean temCafe = true;
        boolean temCha = true;
        System.out.println("Só um dos dois? " + (temCafe ^ temCha));
        System.out.println(true || false && false);
        System.out.println((true || false) && false);
    }
}
$ javac Main.java && java Main
Meia-entrada? true         « idade >= 60 bastou
Aprovado? true             « nota e faltas, os dois ok
Dia útil? false            « não (fim de semana ou feriado)
Dia útil? false            « (não fim de semana) e (não feriado): a mesma coisa
Só um dos dois? false      « tem os dois, então não é "só um"
true                       « && é feito antes de ||
false                      « com parênteses, o || é feito antes
ExpressãoEm português
idade < 18 || idade >= 60 || estudantemenor de idade, ou idoso, ou estudante
nota >= 6 && faltas <= 10nota suficiente e poucas faltas
idade >= 18 && idade < 60entre 18 e 59 (a forma Java de "18 ≤ idade < 60", como visto em Operadores de Comparação)
!(fimDeSemana || feriado)não é fim de semana nem feriado

&& vem antes de ||

As duas últimas linhas da saída mostram uma regra importante: assim como a multiplicação vem antes da soma, o && é calculado antes do ||. Por isso true || false && false é lido como true || (false && false) e dá true. O ! vem antes dos dois. Quando misturar && e ||, use parênteses, mesmo que não sejam necessários: quem lê agradece. A tabela completa está em Precedência.

Inverter um "e" ou um "ou"

As duas linhas "Dia útil?" deram o mesmo resultado. Não é coincidência: "não (A ou B)" é o mesmo que "(não A) e (não B)". Essa regra se chama Lei de De Morgan e funciona também ao contrário:

Isto……é o mesmo que
!(a || b)!a && !b
!(a && b)!a || !b

Ela é útil para simplificar condições confusas e para inverter um if. Ao "distribuir" o !, o && vira || e vice-versa.

Missão: paga meia quem é estudante ou professor. A Ana é professora, mas o programa diz que ela paga inteira. Troque o operador lógico errado para mostrar Meia-entrada? true.

public class Main {
    public static void main(String[] args) {
        boolean estudante = false;
        boolean professor = true;
        boolean meia = estudante && professor;
        System.out.println("Meia-entrada? " + meia);
    }
}
public class Main {
    public static void main(String[] args) {
        boolean estudante = false;
        boolean professor = true;
        boolean meia = estudante || professor;
        System.out.println("Meia-entrada? " + meia);
    }
}

Curto-circuito: o Java para quando já sabe a resposta

Pense no &&: se a primeira condição é false, o resultado já é false, não importa a segunda. E no ||: se a primeira é true, o resultado já é true. Nesses casos, o Java nem calcula a segunda condição. Isso se chama avaliação de curto-circuito.

Para ver o Java "pulando" a segunda parte, usamos uma condição com efeito colateral: ++contador > 0 soma 1 ao contador toda vez que é calculada. Se o contador não mudar, é porque a condição não foi calculada:

public class Main {
    public static void main(String[] args) {
        int contador = 0;
        boolean r1 = false && (++contador > 0);
        System.out.println("&&: r1 = " + r1 + ", contador = " + contador);
        boolean r2 = true || (++contador > 0);
        System.out.println("||: r2 = " + r2 + ", contador = " + contador);
        boolean r3 = false & (++contador > 0);
        System.out.println("&:  r3 = " + r3 + ", contador = " + contador);
        boolean r4 = true | (++contador > 0);
        System.out.println("|:  r4 = " + r4 + ", contador = " + contador);
    }
}
$ javac Main.java && java Main
&&: r1 = false, contador = 0      « a segunda parte NÃO rodou
||: r2 = true, contador = 0       « a segunda parte NÃO rodou
&:  r3 = false, contador = 1      « & calcula os dois lados sempre
|:  r4 = true, contador = 2       « | também

Com && e ||, o ++contador foi pulado (o contador continuou 0). Com & e |, versões sem curto-circuito, os dois lados sempre são calculados, e o contador subiu. O resultado lógico é o mesmo; o que muda é se a segunda parte executa.

Por que isso importa: condições de guarda

O curto-circuito permite escrever uma condição que protege a seguinte. Dividir por zero com inteiros para o programa (Operadores Aritméticos), então primeiro confirmamos que o divisor não é zero:

public class Main {
    public static void main(String[] args) {
        int convidados = 0;
        int bolos = 10;
        if (convidados != 0 && bolos / convidados >= 2) {
            System.out.println("Bolo de sobra!");
        }
        System.out.println("Fim do teste com &&");
        if (convidados != 0 & bolos / convidados >= 2) {
            System.out.println("Bolo de sobra!");
        }
        System.out.println("Fim do teste com &");
    }
}
$ javac Main.java && java Main
Fim do teste com &&
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Main.main(Main.java:9)

Com &&, a primeira condição (convidados != 0) deu false e a divisão nunca aconteceu. Com &, o Java fez a divisão mesmo assim, e o programa parou na linha 9. O if é apresentado em if, else if, else; aqui basta saber que ele executa o bloco entre chaves quando a condição é true.

A ordem das condições importa

A guarda tem de vir primeiro. bolos / convidados >= 2 && convidados != 0 divide antes de conferir e lança a exceção do mesmo jeito. O mesmo padrão aparece com textos: nome != null && nome.length() > 0 confere se a variável aponta para algum texto antes de pedir o tamanho dele (veja Strings).

Missão: o programa para com ArithmeticException. Reorganize a condição para que a guarda (convidados != 0) venha primeiro e o curto-circuito evite a divisão. A saída deve ser Sobra bolo? false.

public class Main {
    public static void main(String[] args) {
        int convidados = 0;
        int bolos = 10;
        boolean sobra = bolos / convidados >= 2 && convidados != 0;
        System.out.println("Sobra bolo? " + sobra);
    }
}
public class Main {
    public static void main(String[] args) {
        int convidados = 0;
        int bolos = 10;
        boolean sobra = convidados != 0 && bolos / convidados >= 2;
        System.out.println("Sobra bolo? " + sobra);
    }
}

&, | e ^

& e |: sem curto-circuito

Com boolean, & e | dão os mesmos resultados que && e || (mesmas tabelas-verdade), mas sempre calculam os dois lados, como você viu. Use && e || no dia a dia; & e | com boolean são raros, para quando a segunda parte precisa executar.

Com números inteiros, &, | e ^ são outra coisa: operadores de bits, que comparam os números bit a bit (5 & 3 dá 1, 5 | 3 dá 7, 5 ^ 3 dá 6). É um assunto avançado, usado em permissões e protocolos; não é preciso para este curso.

^: ou-exclusivo

O ^ (acento circunflexo) é o "ou exclusivo" (XOR): dá true quando exatamente um dos lados é true. É o "ou" do garçom: "acompanha arroz ou fritas" (um dos dois, não ambos). Com dois boolean, a ^ b é o mesmo que a != b.

Missão: no JShell, calcule true ^ false (exatamente um verdadeiro). Depois compare com true ^ true e true || true.

true ^ false

Quando dá errado

!idade > 18: "bad operand type int for unary operator '!'"
public class Main {
    public static void main(String[] args) {
        int idade = 30;
        System.out.println(!idade > 18);
    }
}
$ javac Main.java
Main.java:4: error: bad operand type int for unary operator '!'
        System.out.println(!idade > 18);
                           ^
1 error

O que quer dizer: o ! é aplicado antes da comparação, então o Java tentou fazer !idade, e "não 30" não faz sentido: ! só funciona com boolean.

Como consertar: parênteses em volta da comparação, !(idade > 18), ou, mais simples, inverta o operador: idade <= 18.

Números com &&: "bad operand types for binary operator '&&'"
$ javac Main.java
Main.java:5: error: bad operand types for binary operator '&&'
        System.out.println(a && b);
                             ^
  first type:  int
  second type: int
1 error

O que quer dizer: a e b são int. Em outras linguagens, números valem como verdadeiro ou falso; em Java, os operadores lógicos só aceitam boolean (char e boolean).

Como consertar: escreva as comparações completas: a > 0 && b > 0.

Comparação pela metade: x > 1 && < 10
$ javac Main.java
Main.java:4: error: illegal start of type
        System.out.println(x > 1 && < 10);
                                      ^
1 error

O que quer dizer: em português dizemos "maior que 1 e menor que 10", mas em Java cada lado do && é uma condição completa. < 10 sozinho não é nada.

Como consertar: repita a variável: x > 1 && x < 10.

and, or, not não existem
$ javac Main.java
Main.java:5: error: ')' or ',' expected
        System.out.println(a and b);
                            ^
Main.java:5: error: ';' expected
        System.out.println(a and b);
                                  ^
2 errors

O que quer dizer: quem vem do Python escreve and; para o Java, and é só um nome desconhecido no meio da expressão, e ele se perde.

Como consertar: && no lugar de and, || no lugar de or, ! no lugar de not.

O resultado está "invertido" ou sempre igual

O que aconteceu: normalmente é && no lugar de || (ou o contrário). "Paga meia quem é estudante ou idoso" escrito com && exige as duas coisas. Outro clássico: idade < 18 && idade > 60 é sempre false (ninguém é as duas coisas).

Como consertar: leia a condição em voz alta, trocando os símbolos por "e" e "ou". Em caso de dúvida, imprima cada parte separadamente.


Referência

OperadorNomeResultado true quandoCurto-circuito?
a && bEos dois são trueSim: se a é false, não calcula b
a || bOupelo menos um é trueSim: se a é true, não calcula b
!aNãoa é false—
a & bE (sem curto-circuito)os dois são trueNão
a | bOu (sem curto-circuito)pelo menos um é trueNão
a ^ bOu exclusivoexatamente um é trueNão

Ordem: ! primeiro, depois comparações, depois &, ^, |, && e, por último, ||. Veja Precedência. Para usar essas condições em decisões, continue em Expressões Booleanas e if, else if, else.


Exercícios

1. Os dois

Complete para pode ser true só quando maior e convidado forem verdadeiros:

boolean pode = maior convidado;

2. Inverter

Complete para aberto ser o contrário de fechado:

boolean aberto = fechado;

3. Entre dois valores

Complete a condição "nota de 0 a 10":

nota >= 0 &&

4. Quanto vale true || false && false?

5. Com int c = 0;, quanto vale c depois de boolean r = false && (++c > 0);?

6. Qual condição evita a divisão por zero?

7. "Paga meia quem é estudante ou idoso". Qual operador usar entre as duas condições?


Resumo

  • && (e): true só se os dois forem true. || (ou): true se pelo menos um for. ! (não): inverte.
  • Os operandos têm de ser boolean; números não valem como verdadeiro ou falso.
  • ! vem antes das comparações (!(idade > 18) precisa de parênteses); && vem antes de ||. Na dúvida, parênteses.
  • Curto-circuito: && e || param assim que sabem a resposta. Use isso para guardas: n != 0 && total / n > 5.
  • & e | calculam sempre os dois lados; ^ é o ou-exclusivo (exatamente um).
  • !(a || b) = !a && !b e !(a && b) = !a || !b (De Morgan).