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 ^.
&&(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:
| Operador | Nome | Lê-se | Dá true quando… |
|---|---|---|---|
a && b | E lógico (AND) | "a e b" | os dois são true |
a || b | Ou lógico (OR) | "a ou b" | pelo menos um é true |
!a | Nã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ó é
truese as duas condições forem. - temCarteira
- Segunda condição: uma variável
boolean, que valefalse.
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):
a | b | a && b | a || b | a ^ b |
|---|---|---|---|---|
true | true | true | true | false |
true | false | false | true | true |
false | true | false | true | true |
false | false | false | false | false |
a | !a |
|---|---|
true | false |
false | true |
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ão | Em português |
|---|---|
idade < 18 || idade >= 60 || estudante | menor de idade, ou idoso, ou estudante |
nota >= 6 && faltas <= 10 | nota suficiente e poucas faltas |
idade >= 18 && idade < 60 | entre 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 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.
&&: "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.
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 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
| Operador | Nome | Resultado true quando | Curto-circuito? |
|---|---|---|---|
a && b | E | os dois são true | Sim: se a é false, não calcula b |
a || b | Ou | pelo menos um é true | Sim: se a é true, não calcula b |
!a | Não | a é false | — |
a & b | E (sem curto-circuito) | os dois são true | Não |
a | b | Ou (sem curto-circuito) | pelo menos um é true | Não |
a ^ b | Ou exclusivo | exatamente um é true | Nã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:
2. Inverter
Complete para aberto ser o contrário de fechado:
3. Entre dois valores
Complete a condição "nota de 0 a 10":
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):truesó se os dois foremtrue.||(ou):truese 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 && !be!(a && b)=!a || !b(De Morgan).