Java Essencial: Operadores, expressões e boas práticas de legibilidade

Capítulo 6

Tempo estimado de leitura: 8 minutos

+ Exercício

Operadores aritméticos

Operadores aritméticos trabalham com números (tipos primitivos numéricos) e produzem um resultado numérico. Em Java, a escolha do tipo do resultado pode envolver promoção numérica (por exemplo, operações com int podem “subir” para long se um dos operandos for long).

  • + soma
  • - subtração
  • * multiplicação
  • / divisão
  • % resto (módulo)

Divisão inteira vs. divisão com ponto flutuante

Se ambos os operandos forem inteiros, a divisão é inteira (trunca a parte decimal). Para obter casas decimais, pelo menos um operando deve ser double ou float.

int a = 7; int b = 2; int divInteira = a / b;      // 3 double divReal = a / 2.0;     // 3.5 double divReal2 = (double) a / b; // 3.5

Passo a passo prático: calculando total com desconto

Vamos calcular o total de uma compra com desconto percentual e frete fixo, cuidando para não cair em divisão inteira.

  1. Defina o subtotal e o desconto em porcentagem.
  2. Converta a porcentagem para fator decimal (desconto / 100.0).
  3. Calcule o valor do desconto e subtraia do subtotal.
  4. Some o frete.
double subtotal = 199.90; int descontoPercentual = 10; double frete = 15.00; double fatorDesconto = descontoPercentual / 100.0; // 0.10 double valorDesconto = subtotal * fatorDesconto; double total = (subtotal - valorDesconto) + frete;

Operadores de incremento e decremento

Os operadores ++ e -- aumentam ou diminuem 1 unidade. Eles podem ser usados em forma prefixada ou pós-fixada, e isso muda o valor “observado” na expressão.

  • ++x: incrementa e depois usa o valor
  • x++: usa o valor e depois incrementa
int x = 10; int a = ++x; // x vira 11, a recebe 11 int y = 10; int b = y++; // b recebe 10, y vira 11

Boa prática

Evite misturar ++/-- dentro de expressões longas. Prefira linhas separadas para reduzir ambiguidades.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

// Menos legível: int resultado = valores[i++] + valores[i++]; // Mais legível: int primeiro = valores[i]; i++; int segundo = valores[i]; i++; int resultado = primeiro + segundo;

Operadores relacionais e igualdade

Operadores relacionais comparam valores e retornam boolean.

  • >, >=, <, <=
  • == (igualdade)
  • != (diferença)

Armadilha comum: comparar Strings com ==

Para tipos por referência (como String), == compara referências (se é o mesmo objeto), não o conteúdo. Para comparar conteúdo, use equals (ou equalsIgnoreCase quando fizer sentido).

String s1 = new String("java"); String s2 = new String("java"); boolean refIgual = (s1 == s2);      // false (referências diferentes) boolean conteudoIgual = s1.equals(s2); // true

Passo a passo prático: comparação segura com possível null

Se uma variável pode ser null, chamar variavel.equals("x") pode lançar NullPointerException. Uma prática comum é inverter: chamar "x".equals(variavel).

  1. Escolha o literal como receptor de equals.
  2. Passe a variável possivelmente nula como argumento.
String status = null; boolean ativo = "ATIVO".equals(status); // false, sem exceção

Operadores lógicos e curto-circuito

Operadores lógicos combinam condições booleanas.

  • && (E lógico com curto-circuito)
  • || (OU lógico com curto-circuito)
  • ! (negação)

Curto-circuito (short-circuit)

Com &&, se a primeira condição for false, a segunda não é avaliada. Com ||, se a primeira for true, a segunda não é avaliada. Isso é útil para evitar erros e reduzir custo de avaliação.

String nome = null; // Evita NullPointerException porque a segunda parte não roda se nome == null boolean valido = (nome != null) && (nome.length() >= 3);

Armadilha: confundir && com & e || com |

& e | também funcionam com boolean, mas não fazem curto-circuito (avaliam ambos os lados). Isso pode causar exceções ou efeitos colaterais inesperados.

String nome = null; // Lança NullPointerException porque (nome.length() >= 3) será avaliado boolean valido = (nome != null) & (nome.length() >= 3);

Operador ternário (?:)

O operador ternário escolhe entre dois valores com base em uma condição: condicao ? valorSeVerdadeiro : valorSeFalso. Ele é uma expressão (produz um valor), então é útil para atribuições.

int idade = 17; String categoria = (idade >= 18) ? "ADULTO" : "MENOR";

Boa prática

Use ternário para decisões simples. Se a lógica ficar complexa (ternários aninhados, múltiplas condições), prefira if/else ou extraia para métodos com nomes descritivos.

// Pouco legível (aninhado): String faixa = idade < 12 ? "CRIANCA" : idade < 18 ? "ADOLESCENTE" : "ADULTO"; // Mais legível: String faixa; if (idade < 12) { faixa = "CRIANCA"; } else if (idade < 18) { faixa = "ADOLESCENTE"; } else { faixa = "ADULTO"; }

Concatenação com + e armadilhas com números

O operador + soma números e também concatena strings. Quando uma das partes é String, o restante tende a virar concatenação, seguindo a ordem de avaliação (da esquerda para a direita).

System.out.println("Total: " + 10 + 5); // "Total: 105" System.out.println("Total: " + (10 + 5)); // "Total: 15"

Passo a passo prático: montando uma mensagem sem ambiguidade

  1. Calcule valores numéricos antes.
  2. Use parênteses quando misturar números e strings.
  3. Prefira variáveis intermediárias com nomes claros.
int itens = 10; int bonus = 5; int totalItens = itens + bonus; String mensagem = "Itens no carrinho: " + totalItens;

Operadores bitwise: noções e casos práticos simples

Operadores bitwise atuam no nível de bits de tipos inteiros (byte, short, int, long). São úteis para flags, máscaras, permissões e manipulação eficiente de estados.

  • & AND bit a bit
  • | OR bit a bit
  • ^ XOR bit a bit
  • ~ NOT bit a bit
  • << shift à esquerda
  • >> shift à direita (preserva sinal)
  • >>> shift à direita sem sinal

Flags com máscara (caso comum)

Você pode representar permissões como bits: cada bit indica se uma permissão está ativa. Exemplo: leitura, escrita e execução.

final int PERM_LER = 1;    // 001 final int PERM_ESCREVER = 2; // 010 final int PERM_EXECUTAR = 4; // 100 int permissoes = 0; // Ativar leitura e execução permissoes = permissoes | PERM_LER; permissoes = permissoes | PERM_EXECUTAR; // Testar se tem escrita boolean podeEscrever = (permissoes & PERM_ESCREVER) != 0; // Remover execução permissoes = permissoes & ~PERM_EXECUTAR;

Passo a passo prático: alternar uma flag com XOR

^ (XOR) é útil para alternar um bit: se estiver ligado, desliga; se estiver desligado, liga.

  1. Defina a máscara do bit.
  2. Use valor ^ mascara para alternar.
final int MODO_NOTURNO = 1; // 0001 int config = 0; // modo noturno desligado config = config ^ MODO_NOTURNO; // liga config = config ^ MODO_NOTURNO; // desliga

Shifts: multiplicar/dividir por potências de 2 (com cuidado)

x << 1 equivale a multiplicar por 2; x >> 1 equivale a dividir por 2 (arredondando para baixo em inteiros, com particularidades para negativos). Use quando fizer sentido e a legibilidade não for prejudicada.

int x = 6; int vezes2 = x << 1; // 12 int metade = x >> 1; // 3

Precedência e associatividade de operadores

Precedência define quais operações acontecem primeiro em uma expressão. Associatividade define como operadores do mesmo nível se agrupam (geralmente da esquerda para a direita). Mesmo conhecendo as regras, uma boa prática é usar parênteses para deixar a intenção explícita.

CategoriaExemplosObservação
Unários++ -- ! ~Alta precedência
Multiplicativos* / %Antes de + -
Aditivos+ -Concatenação com String entra aqui
Shift<< >> >>>Após aditivos
Relacionais< <= > >=Antes de igualdade
Igualdade== !=Cuidado com referências
Bitwise& ^ |Sem curto-circuito
Lógicos&& ||Com curto-circuito
Ternário?:Baixa precedência
Atribuição= += -= *= ...Uma das mais baixas

Exemplos de precedência que confundem

boolean r1 = true || false && false; // && vem antes de ||, então: true || (false && false) => true int r2 = 10 + 2 * 3; // 16 (multiplica antes) int r3 = (10 + 2) * 3; // 36

Boas práticas de legibilidade em expressões

1) Use parênteses para explicitar intenção

Mesmo quando a precedência “resolve”, parênteses tornam a leitura mais rápida e reduzem bugs em manutenção.

// Menos claro: boolean ok = idade >= 18 && temDocumento || acompanhado; // Mais claro (intenção explícita): boolean ok = (idade >= 18 && temDocumento) || acompanhado;

2) Prefira nomes descritivos para subexpressões

Quebre expressões longas em variáveis booleanas intermediárias. Isso melhora leitura, facilita testes e reduz duplicação.

// Difícil de ler: boolean podeFinalizar = (cliente != null) && ("ATIVO".equals(cliente.getStatus())) && (total > 0) && (!bloqueado) && (limite >= total); // Mais legível: boolean clienteExiste = (cliente != null); boolean clienteAtivo = clienteExiste && "ATIVO".equals(cliente.getStatus()); boolean totalValido = total > 0; boolean dentroDoLimite = limite >= total; boolean podeFinalizar = clienteAtivo && totalValido && !bloqueado && dentroDoLimite;

3) Refatore expressões longas para métodos com nome

Quando a regra de negócio é reutilizada ou tem significado próprio, extraia para um método com nome que descreva a intenção.

// Em vez de repetir a regra em vários lugares: boolean elegivel = (idade >= 18) && (renda >= 3000) && !inadimplente; // Extraia: boolean elegivel = isElegivelParaCredito(idade, renda, inadimplente);
static boolean isElegivelParaCredito(int idade, double renda, boolean inadimplente) { boolean maiorDeIdade = idade >= 18; boolean rendaMinima = renda >= 3000; return maiorDeIdade && rendaMinima && !inadimplente; }

4) Evite efeitos colaterais em condições

Condições devem preferencialmente apenas verificar, não alterar estado. Isso reduz surpresas com curto-circuito e facilita depuração.

// Evite: if (usuario != null && usuario.incrementaTentativas() < 3) { ... } // Prefira: if (usuario != null) { int tentativas = usuario.incrementaTentativas(); if (tentativas < 3) { ... } }

5) Use operadores compostos com parcimônia

Operadores como +=, *= são úteis e legíveis em atualizações simples. Em expressões complexas, prefira etapas intermediárias.

double total = 0; total += 19.90; total += 5.00; // claro // Se ficar complexo, quebre: double taxa = subtotal * 0.12; total = subtotal + taxa + frete;

Agora responda o exercício sobre o conteúdo:

Ao validar uma String que pode ser null, qual abordagem evita NullPointerException e ainda permite comparar o conteúdo com segurança?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Se a variável pode ser null, chamar status.equals(...) pode lançar exceção. Ao usar o literal como receptor ("ATIVO".equals(status)), a comparação retorna false quando status é null, sem erro.

Próximo capitúlo

Java Essencial: Controle de fluxo com if/else, switch e expressões condicionais

Arrow Right Icon
Capa do Ebook gratuito Java Essencial: Fundamentos da Linguagem e do Ecossistema (JDK, IDE, Maven)
33%

Java Essencial: Fundamentos da Linguagem e do Ecossistema (JDK, IDE, Maven)

Novo curso

18 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.