Operadores
por Sutton AshfordEntenda o conceito
Operadores são os símbolos que fazem contas, comparam e combinam valores. Os aritméticos (+, -, *, /, %) seguem a matemática: *, / e % têm precedência sobre + e -, e operadores do mesmo nível são avaliados da esquerda para a direita. Por isso 2 + 3 * 4 dá 14, e 120 / 6 * 2 dá 40. Quando a ordem natural não serve, os parênteses mandam.
Os operadores de atribuição composta (+=, -=, *=, /=, %=) encurtam x = x + y para x += y, e têm um detalhe: fazem um casting escondido para o tipo da variável, então, com pontos declarado como int, pontos += 38.4; compila e descarta os decimais. Já ++ e -- somam ou subtraem 1, e a posição importa quando o valor é usado na mesma expressão: n++ devolve o valor antigo e depois incrementa; ++n incrementa e devolve o novo.
Os relacionais (==, !=, <, <=, >, >=) produzem um boolean, que pode ser guardado numa variável, e os lógicos combinam esses valores. Além de && e ||, que fazem curto-circuito, existem & e |, que avaliam sempre os dois lados, e ^, o ou exclusivo, verdadeiro quando só um dos lados é true.
Duas armadilhas comuns: o + com uma String vira concatenação, então "Dias: " + 17 + 14 imprime Dias: 1714; e o % mantém o sinal do dividendo (-15 % 7 é -1). Para um resto sempre positivo, como em contas circulares, use Math.floorMod.
*,/e%vêm antes de+e-; no mesmo nível, a conta vai da esquerda para a direita. Na dúvida, use parênteses.x += yequivale ax = (tipo de x) (x + y): o casting escondido fazintaceitar umdouble, descartando os decimais.n++devolve o valor antigo e++no novo; sozinhos numa linha, os dois têm o mesmo efeito.- Com uma
Stringà esquerda,+concatena:"a" + 1 + 2é"a12", mas"a" + (1 + 2)é"a3". &e|avaliam os dois lados,&&e||param cedo,^é o ou exclusivo; para resto sem sinal negativo,Math.floorMod.
Do conceito à prática
3 exemplos para ver a ideia funcionando.
01Placar de partida com precedência e parênteses
public class Placar {
public static void main(String[] args) {
int abates = 9;
int assistencias = 4;
int mortes = 4;
// * e / vêm antes de + e -: as multiplicações são feitas primeiro.
int pontos = abates * 100 + assistencias * 50 - mortes * 30;
System.out.println("Pontos: " + pontos);
// Parênteses mudam a ordem: a soma acontece antes da multiplicação.
int bonusEquipe = (abates + assistencias) * 10;
int semParenteses = abates + assistencias * 10;
System.out.println("Bônus da equipe: " + bonusEquipe);
System.out.println("Sem parênteses daria: " + semParenteses);
// Operadores de mesma precedência são avaliados da esquerda para a direita.
System.out.println("100 - 30 - 20 = " + (100 - 30 - 20));
System.out.println("120 / 6 * 2 = " + (120 / 6 * 2));
// O casting vem antes da divisão, então a conta é feita em double.
double razao = (double) abates / mortes;
System.out.println("Razão abates/mortes: " + razao);
// Relacionais produzem boolean, que pode ir para uma variável.
boolean recorde = pontos > 1000;
boolean empate = pontos == 1000;
System.out.println("Bateu o recorde? " + recorde);
System.out.println("Empatou com o recorde? " + empate);
}
}
Em abates * 100 + assistencias * 50 - mortes * 30, as três multiplicações são feitas antes da soma e da subtração, como na matemática, e o resultado é 980. Já em bonusEquipe, os parênteses obrigam a somar primeiro: (9 + 4) * 10 dá 130, enquanto sem eles a multiplicação vence e sobra 49. As linhas 100 - 30 - 20 e 120 / 6 * 2 mostram que operadores de mesmo nível vão da esquerda para a direita.
No fim, pontos > 1000 e pontos == 1000 não estão dentro de um if: eles produzem um boolean que fica guardado numa variável e pode ser impresso ou reutilizado depois.
02Carrinho com atribuição composta e incremento
public class Carrinho {
public static void main(String[] args) {
int itens = 0;
int totalCentavos = 0;
// total += x é um atalho para total = total + x.
totalCentavos += 2_590; // uma camiseta
itens++; // ++ soma 1
totalCentavos += 1_250 * 2; // duas canecas
itens += 2;
System.out.println("Itens: " + itens + " | Total: " + totalCentavos + " centavos");
// -= e -- desfazem: a cliente tirou uma caneca.
totalCentavos -= 1_250;
itens--;
System.out.println("Itens: " + itens + " | Total: " + totalCentavos + " centavos");
// Pós-incremento devolve o valor antigo; pré-incremento, o novo.
int numeroPedido = 41;
int atual = numeroPedido++; // atual recebe 41, depois numeroPedido vira 42
int seguinte = ++numeroPedido; // numeroPedido vira 43, depois seguinte recebe 43
System.out.println("atual=" + atual + " seguinte=" + seguinte + " numeroPedido=" + numeroPedido);
// *=, /= e %= seguem a mesma ideia.
int canecasNoEstoque = 30;
canecasNoEstoque *= 2; // chegou outro lote igual: 60
int caixasCheias = canecasNoEstoque / 8;
canecasNoEstoque %= 8; // o que não fecha uma caixa de 8
System.out.println("Caixas cheias: " + caixasCheias + " | Avulsas: " + canecasNoEstoque);
// A atribuição composta faz um casting escondido para o tipo da variável.
int pontosFidelidade = 100;
pontosFidelidade += totalCentavos * 0.01; // 100 + 38.4 vira 138
// pontosFidelidade = pontosFidelidade + totalCentavos * 0.01; // não compila
System.out.println("Pontos de fidelidade: " + pontosFidelidade);
}
}
O carrinho é atualizado só com atalhos: += e -= mexem no total, ++ e -- na quantidade, e *=, / e %= repartem o estoque em caixas de 8. O trecho do número do pedido mostra a diferença entre pós e pré-incremento: numeroPedido++ entrega 41 e só depois incrementa; ++numeroPedido incrementa antes e entrega 43.
A última conta revela o casting escondido da atribuição composta: totalCentavos * 0.01 é 38.4, um double, e mesmo assim pontosFidelidade += ... compila e guarda 138. A forma longa, comentada logo abaixo, não compila, porque somar int com double dá double e o Java não estreita sozinho numa atribuição comum.
03Devolução na biblioteca: concatenação, &, ^ e floorMod
public class Devolucao {
static int consultasReserva = 0;
// Conta as chamadas para mostrar quando o lado direito é avaliado.
static boolean semReserva() {
consultasReserva++;
return true;
}
public static void main(String[] args) {
int diasEmprestado = 17;
int prazo = 14;
boolean atrasado = diasEmprestado > prazo;
// Com String, o + é avaliado da esquerda para a direita.
System.out.println("Dias: " + diasEmprestado + prazo); // concatena: 1714
System.out.println("Atraso: " + (diasEmprestado - prazo)); // parênteses: 3
// && não avalia o lado direito se o esquerdo já é false; & sempre avalia.
boolean renovaCurto = !atrasado && semReserva();
System.out.println("Renova (&&)? " + renovaCurto + " | consultas: " + consultasReserva);
boolean renovaCompleto = !atrasado & semReserva();
System.out.println("Renova (&)? " + renovaCompleto + " | consultas: " + consultasReserva);
// ^ (ou exclusivo) é true quando exatamente um dos lados é true.
boolean trouxeCarteirinha = true;
boolean trouxeDocumento = true;
System.out.println("Só um documento? " + (trouxeCarteirinha ^ trouxeDocumento));
// Dias da semana de 0 (domingo) a 6 (sábado); hoje é terça (2).
int hoje = 2;
System.out.println("Prazo para pagar a multa: dia " + (hoje + 5) % 7);
// Voltar no tempo dá número negativo: % mantém o sinal, floorMod não.
System.out.println("Pegou no dia (com %): " + (hoje - diasEmprestado) % 7);
System.out.println("Pegou no dia (floorMod): " + Math.floorMod(hoje - diasEmprestado, 7));
}
}
A primeira linha imprime 1714 porque o + é avaliado da esquerda para a direita: "Dias: " + 17 já é uma String, e o 14 é colado no fim. Com parênteses, a subtração acontece antes e o atraso sai certo. O método semReserva conta as próprias chamadas: com &&, o lado esquerdo já é false e ele nem roda; com &, roda mesmo assim, o que só é útil quando o lado direito precisa executar sempre.
O ^ dá false porque os dois documentos foram trazidos, e a pergunta é se veio só um. Por fim, (2 - 17) % 7 dá -1, porque o % do Java mantém o sinal do dividendo, enquanto Math.floorMod devolve 6, o sábado, que é o dia certo numa semana circular.
Agora é com você
Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.
01Dobrar a receita de boloFácil
Uma receita de bolo rende 4 porções com 200 g de farinha, 120 g de açúcar e 3 ovos. Dobre a receita, tire um quarto do açúcar já dobrado e acrescente um ovo, usando operadores de atribuição composta e incremento.
- A classe se chama
Ingredientes, e as quatro quantidades são variáveisint. - A receita é dobrada com
*=em cada variável, sem reescrever os valores na mão. - O açúcar é reduzido com
-=e o ovo extra é somado com++. - A saída é idêntica à esperada.
public class Ingredientes {
public static void main(String[] args) {
int porcoes = 4;
int farinha = 200;
int acucar = 120;
int ovos = 3;
// Dobre a receita, reduza o açúcar, some um ovo e imprima.
}
}
Porções: 8
Farinha: 400 g
Açúcar: 180 g
Ovos: 7
Ver dica
Um quarto do açúcar é o próprio açúcar dividido por 4, e a redução só pode acontecer depois de dobrar.
Ver resposta
public class Ingredientes {
public static void main(String[] args) {
int porcoes = 4;
int farinha = 200; // gramas
int acucar = 120; // gramas
int ovos = 3;
// Dobra a receita com atribuição composta.
porcoes *= 2;
farinha *= 2;
acucar *= 2;
ovos *= 2;
// Menos doce: tira um quarto do açúcar atual.
acucar -= acucar / 4;
// Um ovo a mais para dar liga.
ovos++;
System.out.println("Porções: " + porcoes);
System.out.println("Farinha: " + farinha + " g");
System.out.println("Açúcar: " + acucar + " g");
System.out.println("Ovos: " + ovos);
}
}
Passo a passo
- As quatro quantidades começam como
int, porque são porções, gramas e ovos inteiros. porcoes *= 2;é o atalho deporcoes = porcoes * 2;, e a mesma linha se repete para farinha, açúcar e ovos.acucar -= acucar / 4;calcula primeiro o lado direito (240 / 4dá60) e depois subtrai do valor atual, deixando180.ovos++;soma 1 à variável; sozinho numa linha, tanto faz escreverovos++ou++ovos.- Os
printlnjuntam o rótulo, o valor e a unidade com+, já que a primeira parte é umaString.
Por que funciona
Cada operador composto lê o valor atual da variável, aplica a operação com o lado direito e guarda o resultado na própria variável. Como a redução do açúcar vem depois do *=, ela usa o valor já dobrado: 240 - 60 dá 180. A ordem das linhas é a ordem das mudanças, e é por isso que o resultado bate com a receita.
Erros comuns
- Dividir em vez de subtrair:
acucar /= 4;deixa só um quarto do açúcar (60g) em vez de tirar um quarto. O pedido éacucar -= acucar / 4;, que mantém três quartos. - Inverter o operador:
acucar =- acucar / 4;compila, mas é lido comoacucar = -acucar / 4e guarda-60. O símbolo é-=, com o sinal antes do igual. - Trocar o operador por um valor fixo:
farinha = 400;imprime o mesmo resultado, mas quebra quando a receita base mudar e não usa o operador pedido.
Outra forma de resolver
Escrever acucar = acucar * 3 / 4; também funciona e deixa claro que sobram três quartos:
acucar = acucar * 3 / 4; // 240 * 3 / 4 = 180
Repare que a multiplicação vem antes da divisão; com acucar * (3 / 4), o 3 / 4 inteiro daria 0. É preferível quando a ideia é "fica uma fração", e não "tira uma parte".
Saída esperada
Porções: 8
Farinha: 400 g
Açúcar: 180 g
Ovos: 7
02Média ponderada e frequência no boletimMédio
Um aluno tirou 7, 8 e 5 nas provas, com pesos 2, 3 e 5, e faltou a 6 de 40 aulas. Mostre a conta escrita sem parênteses, a média ponderada correta, a frequência em porcentagem inteira e se ele está aprovado e se é destaque, guardando as respostas em variáveis boolean.
- A linha "Sem parênteses" usa a mesma expressão da média, só que sem os parênteses, para mostrar o efeito da precedência.
- A frequência é um
intcalculado multiplicando por 100 antes de dividir pelo total de aulas. aprovadoexige média de pelo menos 6 e frequência de pelo menos 75;destaqueexige média de pelo menos 9 ou nenhuma falta. Nada deif.- A saída é idêntica à esperada.
public class Boletim {
public static void main(String[] args) {
int nota1 = 7, nota2 = 8, nota3 = 5;
int peso1 = 2, peso2 = 3, peso3 = 5;
int aulas = 40;
int faltas = 6;
// Calcule as contas e guarde as respostas em variáveis boolean.
}
}
Sem parênteses: 40.5
Média ponderada: 6.3
Frequência: 85%
Aprovado? true
Destaque? false
Ver dica
Na frequência, a ordem da multiplicação e da divisão muda o resultado quando tudo é int.
Ver resposta
public class Boletim {
public static void main(String[] args) {
int nota1 = 7, nota2 = 8, nota3 = 5;
int peso1 = 2, peso2 = 3, peso3 = 5;
int aulas = 40;
int faltas = 6;
// Sem parênteses, só o último produto é dividido.
double errada = nota1 * peso1 + nota2 * peso2 + nota3 * peso3 / 10.0;
// Com parênteses, a soma inteira é dividida pela soma dos pesos.
double media = (nota1 * peso1 + nota2 * peso2 + nota3 * peso3) / 10.0;
// Multiplicar antes de dividir evita perder a parte decimal no int.
int frequencia = (aulas - faltas) * 100 / aulas;
// Relacionais e lógicos guardam a resposta em boolean.
boolean aprovado = media >= 6 && frequencia >= 75;
boolean destaque = media >= 9 || faltas == 0;
System.out.println("Sem parênteses: " + errada);
System.out.println("Média ponderada: " + media);
System.out.println("Frequência: " + frequencia + "%");
System.out.println("Aprovado? " + aprovado);
System.out.println("Destaque? " + destaque);
}
}
Passo a passo
- Notas, pesos, aulas e faltas são
int; a vírgula na declaração (int nota1 = 7, nota2 = 8) cria várias variáveis do mesmo tipo numa linha. - Em
errada, a precedência faz sónota3 * peso3 / 10.0virar2.5, que é somado a14 + 24: o resultado absurdo40.5mostra o problema. - Em
media, os parênteses somam os três produtos (63) antes de dividir por10.0; como10.0édouble, a divisão não descarta o.3. (aulas - faltas) * 100 / aulasfaz34 * 100primeiro e só depois divide, dando85; dividir antes daria0.media >= 6 && frequencia >= 75junta duas comparações com o "e" lógico, e o resultado vai direto para a variávelaprovado.media >= 9 || faltas == 0éfalse, porque nenhum dos dois lados é verdadeiro.
Por que funciona
A precedência decide a ordem das operações sem olhar para a intenção de quem escreveu, por isso os parênteses na média são obrigatórios. Na frequência, a ordem da esquerda para a direita é aproveitada: multiplicar primeiro mantém a precisão antes da divisão inteira. Comparações são expressões como quaisquer outras, então seu resultado boolean pode ser guardado e impresso sem passar por um if.
Erros comuns
- Dividir por um int:
(...) / 10faz divisão inteira e imprime6.0. Um dos lados precisa serdouble, como10.0. - Dividir antes de multiplicar:
(aulas - faltas) / aulas * 100dá0, porque34 / 40entre inteiros é0. - Concatenar a comparação sem parênteses:
"Aprovado? " + media >= 6não compila ("bad operand types for binary operator '>='"), porque o+vem antes e compara umaStringcom um número. Guarde em variável ou use parênteses. - Usar
=no lugar de==:faltas = 0é atribuição e não compila dentro do||, já que produz umint.
Outra forma de resolver
Os pesos podem ser somados por uma expressão, em vez do 10.0 fixo, o que evita erro se um peso mudar:
int somaPesos = peso1 + peso2 + peso3;
double media = (double) (nota1 * peso1 + nota2 * peso2 + nota3 * peso3) / somaPesos;
Aqui o casting fica do lado de fora dos parênteses de propósito: a soma inteira não perde nada, e a conversão acontece antes da divisão.
Saída esperada
Sem parênteses: 40.5
Média ponderada: 6.3
Frequência: 85%
Aprovado? true
Destaque? false
03Tabuleiro circular com resto e floorModDesafio
Num jogo de tabuleiro circular com 12 casas (0 a 11), um peão está na casa 9 com 50 moedas. No turno 1 ele anda 7 casas e ganha 200 moedas por volta completa; no turno 2 tira a carta "volte 6 casas". Mostre cada turno, as moedas, se a casa final é de bônus (par e diferente de 0) e o número do próximo turno.
- As voltas saem com
/e a nova casa com%, ambos sobre a posição mais o dado; as moedas sobem com+=. - O recuo usa
Math.floorMod, e a linha do turno 2 também mostra o valor negativo que o%daria. - O número do turno é impresso com
turno++dentro do próprioprintln, sem linhas separadas para incrementar. - O bônus é um
booleancalculado com%,==,!=e&&, semif.
public class Tabuleiro {
public static void main(String[] args) {
final int CASAS = 12;
int posicao = 9;
int moedas = 50;
int turno = 1;
// Turno 1: ande 7 casas. Turno 2: volte 6 casas.
}
}
Turno 1: casa 4, voltas: 1
Turno 2: casa 10 (com % seria -2)
Moedas: 250
Casa de bônus? true
Próximo turno: 3
Ver dica
Andar de 9 até 16 num círculo de 12 casas passa pela largada uma vez; o resto mostra onde o peão para.
Ver resposta
public class Tabuleiro {
public static void main(String[] args) {
final int CASAS = 12; // casas numeradas de 0 a 11, em círculo
int posicao = 9;
int moedas = 50;
int turno = 1;
// Turno 1: o dado tira 7. / conta as voltas e % acha a casa.
int destino = posicao + 7;
int voltas = destino / CASAS;
posicao = destino % CASAS;
moedas += voltas * 200; // cada volta completa paga 200 moedas
System.out.println("Turno " + turno++ + ": casa " + posicao + ", voltas: " + voltas);
// Turno 2: carta "volte 6 casas". % deixaria o resultado negativo.
int comResto = (posicao - 6) % CASAS;
posicao = Math.floorMod(posicao - 6, CASAS);
System.out.println("Turno " + turno++ + ": casa " + posicao + " (com % seria " + comResto + ")");
// Casa par diferente da largada dá bônus.
boolean bonus = posicao % 2 == 0 && posicao != 0;
System.out.println("Moedas: " + moedas);
System.out.println("Casa de bônus? " + bonus);
System.out.println("Próximo turno: " + turno);
}
}
Passo a passo
CASASéfinalporque o tamanho do tabuleiro não muda; o nome em maiúsculas segue a convenção de constantes.destino = posicao + 7dá16.16 / 12é1volta completa, e16 % 12é4, a casa onde o peão para.moedas += voltas * 200;multiplica primeiro, por precedência, e depois soma ao saldo:50 + 200dá250."Turno " + turno++ + ...usa o valor atual deturno(1) na mensagem e só depois o incrementa para2; o mesmo acontece no turno 2.(posicao - 6) % CASASé-2 % 12, que dá-2: o%do Java mantém o sinal do dividendo e não serve para recuar num círculo.Math.floorMod(posicao - 6, CASAS)devolve10, sempre entre0e11, que é a casa certa ao recuar 6 a partir da 4.posicao % 2 == 0 && posicao != 0resolve primeiro o%, depois as comparações e por último o&&, então não precisa de parênteses.- Na última linha,
turnojá vale3, porque cadaturno++anterior somou 1 depois de ser usado.
Por que funciona
O par / e % decompõe um número em "quantas vezes coube" e "quanto sobrou", que no tabuleiro são as voltas e a casa. Para andar para trás, o resto precisa ficar sempre na faixa de 0 a 11, e é exatamente isso que Math.floorMod garante, enquanto o % devolve um número negativo que não corresponde a nenhuma casa.
O pós-incremento dentro do println mostra que turno++ é uma expressão com valor: ela entrega o número antigo para a concatenação e deixa a variável atualizada para a próxima linha. A precedência (% antes de ==, que vem antes de &&) permite escrever o bônus sem parênteses extras.
Erros comuns
- Recuar com
%:posicao = (posicao - 6) % CASAS;deixa o peão na casa-2, que não existe. UseMath.floorMod, ou someCASASantes:(posicao - 6 + CASAS) % CASAS. - Pré-incremento no lugar do pós:
"Turno " + ++turnoimprimeTurno 2eTurno 3, pulando o primeiro turno. - Contar a volta depois de mudar a posição: calcular
voltas = posicao / CASASdepois deposicao = destino % CASASdá0, porque a posição já foi reduzida. Guarde o destino antes. - Esquecer os parênteses no comResto:
posicao - 6 % CASASé lido comoposicao - (6 % 12), e imprime-2só por coincidência; com outra posição o valor sairia errado.
Outra forma de resolver
Como o tamanho do tabuleiro é positivo, somar CASAS antes do % também evita o negativo, desde que o recuo seja menor que o tabuleiro:
posicao = (posicao - 6 + CASAS) % CASAS; // (4 - 6 + 12) % 12 = 10
É um truque comum em código antigo, mas falha com recuos maiores que 12 casas; Math.floorMod funciona com qualquer valor e deixa a intenção explícita.
Saída esperada
Turno 1: casa 4, voltas: 1
Turno 2: casa 10 (com % seria -2)
Moedas: 250
Casa de bônus? true
Próximo turno: 3