Programação Java
Padrões de código em Java: nomenclatura, estrutura, Javadoc e regras automatizáveis
Iniciante11 min de leituraAtualizado
Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês
Padrões de código existem porque o código é lido muito mais vezes do que é escrito, e quase sempre por alguém que não é o autor. Este guia apresenta as convenções em que a maioria dos times Java acaba convergindo — nomenclatura, organização das classes, formatação, imports, Javadoc, tratamento de null, expressões idiomáticas para strings e logs —, explica o motivo de cada uma para que você as aplique com bom senso e mostra como automatizá-las com um formatador, Checkstyle, Error Prone e SonarQube, em vez de ficar cobrando isso em code review.
O que você vai aprender
- Aplicar as convenções de nomenclatura padrão do Java a pacotes, tipos, métodos, constantes e generics
- Organizar uma classe na ordem convencional dos membros, com imports limpos e métodos curtos
- Escrever Javadoc que documente o contrato em vez de repetir a assinatura
- Lidar com null, strings, números mágicos e logs de forma idiomática
- Automatizar o estilo com um formatador e análise estática para que as revisões foquem no design
A maior parte da vida de uma base de código é manutenção, e a maior parte da manutenção é leitura. Um estilo consistente não é questão de estética: ele elimina as pequenas decisões (onde vai a chave, isto é uma constante ou uma variável, o que significa mgr) para que quem lê gaste a atenção com o que o código faz. O Java tem convenções documentadas desde os anos 1990, e o mercado convergiu desde então para um conjunto de regras bem enxuto, a maioria das quais pode ser verificada por uma ferramenta. Este guia apresenta essas regras com suas justificativas, tem o Java 21 como alvo e termina mostrando como fazê-las valer para que o estilo nunca mais vire assunto de code review.
Nomenclatura
Os nomes são a parte mais importante de um padrão, porque carregam um significado que nenhum formatador consegue acrescentar. As convenções do Java são precisas:
| Elemento | Convenção | Exemplos |
|---|---|---|
| Pacote | tudo em minúsculas, domínio invertido, sem underscores | com.example.billing.invoice |
| Classe, interface, enum, record | UpperCamelCase, substantivos | InvoiceService, PaymentResult |
| Método | lowerCamelCase, verbos ou locuções verbais | calculateTotal(), isExpired(), findByEmail() |
| Variável, campo, parâmetro | lowerCamelCase, substantivos | retryCount, customerEmail |
Constante (static final imutável) | UPPER_SNAKE_CASE | MAX_RETRIES, DEFAULT_TIMEOUT |
| Parâmetro de tipo | uma única letra maiúscula | T, E, K, V, R |
| Constante de enum | UPPER_SNAKE_CASE | Status.IN_PROGRESS |
| Método de teste | descritivo, underscores permitidos | rejectsNegativeAmount() ou withdraw_insufficientFunds_throws() |
Além da mecânica, bons nomes compartilham três propriedades. São pronunciáveis e fáceis de buscar: customerRepository pode ser encontrado com grep e dito em voz alta; custRepo2 ninguém acha e x casa com tudo. Indicam a unidade ou o tipo quando há ambiguidade: timeoutMillis, priceCents, sizeBytes evitam o clássico bug do fator 1000 melhor do que qualquer comentário. E evitam palavras vazias: InvoiceData, InvoiceInfo, InvoiceObject não dizem nada que Invoice já não diga.
Booleanos ficam mais legíveis como predicados: isActive, hasChildren, canRetry. Não coloque negação no nome (isNotReady), porque quem chama vai acabar escrevendo !isNotReady. Siglas seguem o camel case como palavras comuns — HttpClient, parseXml, userId —, porque HTTPURLConnection é ilegível e XMLHTTPRequest é um alerta que a história deixou.
Um campo static final só é constante se o valor for profundamente imutável. static final List<String> NAMES = new ArrayList<>() é uma variável global mutável disfarçada: use List.of(...) ou dê a ela um nome em lowerCamelCase para sinalizar que pode mudar.
Organização do arquivo-fonte e da classe
Um único tipo público de nível superior por arquivo, com o mesmo nome dele. Dentro do arquivo, a ordem convencional ajuda o leitor a se localizar:
- O cabeçalho de licença ou copyright, se o projeto usar um
- A declaração
package - Os imports: primeiro os estáticos, depois os normais, sem curingas e sem imports não utilizados
- O Javadoc da classe e sua declaração
- Os campos estáticos (constantes primeiro) e depois os campos de instância
- Os construtores e as factories estáticas
- Os métodos, agrupados por finalidade — primeiro a API pública, ou na ordem de chamada, para que se possa ler de cima para baixo
equals,hashCode,toString- Os tipos aninhados
Imports com curinga (import java.util.*;) escondem de onde vem um tipo e causam conflitos silenciosos quando dois pacotes passam a ter uma classe com o mesmo nome; as IDEs os agrupam automaticamente, então configure a sua para não fazer isso. Dentro de uma classe, mantenha os campos juntos no topo em vez de espalhados ao lado dos métodos que os usam: quem lê deve conseguir ver todo o estado de um objeto em uma única tela.
A ordem dos modificadores é definida pela recomendação da especificação da linguagem e verificada por qualquer linter: public protected private abstract static final transient volatile synchronized native strictfp. Escreva public static final, nunca final static public.
Formatação
A formatação é a parte de um padrão que deveria ser totalmente automática. As regras de consenso:
- Indentação: 4 espaços (Oracle) ou 2 espaços (Google); nunca tabs, porque a largura do tab muda de editor para editor.
- Tamanho da linha: 100 ou 120 colunas. O antigo limite de 80 colunas é anterior às telas largas, mas linhas muito longas continuam difíceis de percorrer.
- Chaves: estilo K&R (chave de abertura na mesma linha) e sempre presentes, mesmo em um
ifde uma linha. O buggoto failda Apple era umifsem chaves com uma linha duplicada. - Uma instrução por linha, uma declaração de variável por linha, o mais perto possível do primeiro uso.
- Linhas em branco separam grupos lógicos: entre métodos, entre campos e construtores e dentro de um método longo para marcar etapas — mas um método que precisa de muitos grupos desses deveria ser dividido.
- Espaços: depois de palavras-chave (
if (,for (), em volta de operadores binários, depois de vírgulas; nenhum dentro dos parênteses.
// Consistent, tool-formatted
public Optional<Invoice> latestFor(CustomerId customerId, Clock clock) {
Objects.requireNonNull(customerId, "customerId");
LocalDate today = LocalDate.now(clock);
return invoices.stream()
.filter(invoice -> invoice.customerId().equals(customerId))
.filter(invoice -> !invoice.issuedOn().isAfter(today))
.max(Comparator.comparing(Invoice::issuedOn));
}
A decisão de formatação mais valiosa que um time pode tomar é parar de tomar decisões de formatação: adote o google-java-format (ou o formatador da IDE com uma configuração compartilhada e versionada via Spotless) e rode-o ao salvar e na CI. Um formatador que reescreve o arquivo inteiro, em vez de verificar um subconjunto de regras, encerra de vez qualquer discussão sobre espaços.
Tamanho de métodos e classes
Unidades pequenas são mais fáceis de nomear, testar e reutilizar. Os tetos práticos que muitos times impõem com o Checkstyle são: métodos com menos de umas 30 linhas, no máximo 4 ou 5 parâmetros, classes com menos de algumas centenas de linhas e no máximo três níveis de aninhamento. São sinais a investigar, não leis: um switch sobre um enum com 20 constantes pode ter 40 linhas sem problema, e um parser pode legitimamente ter 500.
Duas refatorações resolvem a maioria das violações. Extrair método transforma um bloco comentado em uma chamada com nome. Introduzir objeto de parâmetro transforma create(String name, String email, String street, String city, String zip) em create(Person person, Address address), o que também impede que quem chama troque dois argumentos String de lugar. Aninhamento profundo costuma ser resolvido com cláusulas de guarda: retorne ou lance a exceção cedo nos casos inválidos e depois escreva o caminho principal sem indentação.
// Nested
public void ship(Order order) {
if (order != null) {
if (order.isPaid()) {
if (!order.isShipped()) {
carrier.dispatch(order);
}
}
}
}
// Guard clauses
public void ship(Order order) {
Objects.requireNonNull(order, "order");
if (!order.isPaid()) throw new IllegalStateException("Order " + order.id() + " is not paid");
if (order.isShipped()) return;
carrier.dispatch(order);
}
Comentários e Javadoc
Javadoc é documentação de API: descreve o contrato de tipos e métodos públicos para quem vai chamá-los sem ler a implementação. Ele é convertido em HTML e lido nos tooltips da IDE, então a estrutura importa.
/**
* Reserves stock for every line of the order.
*
* <p>Reservation is all-or-nothing: if any line cannot be satisfied, no stock is
* reserved and an {@link InsufficientStockException} identifies the first failing SKU.
* Reservations expire after {@value #RESERVATION_MINUTES} minutes unless confirmed.
*
* @param order the order to reserve for; must contain at least one line
* @return a reservation token to pass to {@link #confirm(ReservationToken)}
* @throws InsufficientStockException if any line exceeds available stock
* @throws IllegalArgumentException if the order has no lines
*/
public ReservationToken reserve(Order order) { ... }
A primeira frase é um resumo que aparece nos índices, então faça dela uma descrição completa do que o método faz. O corpo explica o contrato: efeitos colaterais, atomicidade, o que acontece em caso de falha, thread-safety, unidades. @param, @return e @throws documentam cada parâmetro, o resultado e cada exceção que quem chama possa tratar. Não documente RuntimeExceptions lançadas apenas por erros de programação, a menos que a condição faça parte do contrato.
O que o Javadoc não deve conter: uma repetição da assinatura (“Returns the name”), detalhes de implementação que vão ficar desatualizados, ou uma tag de autor e um histórico de mudanças — o controle de versão faz isso melhor. Desde o Java 23 existe a forma Markdown ///, mas no Java 21 fique com a forma HTML, com <p> entre os parágrafos.
Dentro do corpo dos métodos, use comentários // para o porquê: o ticket que originou um contorno, o motivo de um loop ter sido escrito à mão por desempenho, o invariante do qual um trecho de código depende. Um comentário que diz o que um bloco faz é um nome de método esperando para nascer. TODO e FIXME são aceitáveis com um responsável ou uma referência de ticket; TODOs sem dono são permanentes.
Expressões idiomáticas da linguagem
Várias regras da maioria dos padrões dizem respeito à correção, e não à aparência.
Null. Nunca retorne null no lugar de uma coleção — retorne uma vazia. Retorne Optional para um valor único que pode estar ausente. Valide os parâmetros no início dos métodos públicos com Objects.requireNonNull e documente os parâmetros que aceitam null (com uma anotação como @Nullable dos projetos JSpecify ou Checker Framework, que os analisadores estáticos entendem).
Strings. Compare com equals (ou equalsIgnoreCase), nunca com ==. Prefira String.isBlank() a trim().isEmpty(). Concatenar com + é tranquilo em uma única linha — desde o Java 9 o compilador gera uma concatenação eficiente baseada em invokedynamic —, mas dentro de um loop use StringBuilder, e para templates de várias linhas use text blocks (""") e formatted().
Números mágicos. Qualquer literal que não seja 0, 1 ou -1 merece uma constante com nome: if (retries > 3) vira if (retries > MAX_RETRIES). O nome documenta a intenção e o valor passa a ter um único lugar para mudar.
Igualdade e hash. Sobrescreva equals, hashCode e toString juntos, ou use um record. Nunca os baseie em campos mutáveis quando os objetos forem usados como chaves.
Imutabilidade por padrão. final nos campos, nos parâmetros usados em lambdas e nas variáveis locais que nunca são reatribuídas; coleções não modificáveis saindo dos getters; records para valores.
Recursos. Tudo o que for AutoCloseable vai em um try-with-resources; nunca um close() solto em um finally.
Switch. Use a forma com seta (case A -> ..., Java 14) para não haver fall-through; use switch expressions quando o resultado for um valor; omita o default em tipos sealed e enums quando quiser que o compilador aponte constantes novas.
Logs. Use SLF4J com mensagens parametrizadas — log.warn("Payment {} declined: {}", id, code) —, nunca concatenação, que paga o custo da formatação mesmo quando o nível está desativado. Passe a exceção como último argumento para que o stack trace seja capturado. Nunca registre segredos nem dados pessoais completos. Uma linha de log por evento, na fronteira, e não uma por camada.
Visibilidade. O escopo mais restrito que funcione: campos private, classes auxiliares package-private, protected para os métodos de que as subclasses precisam e public só para a API de verdade. Classes que não foram projetadas para herança são final.
Testes fazem parte do padrão
O código de teste segue as mesmas convenções com duas flexibilizações: nomes de métodos podem usar underscores para serem lidos como uma frase, e um pouco de duplicação entre testes é preferível a uma hierarquia de helpers que esconde o que cada teste verifica. Estruture cada teste como arrange / act / assert (ou given / when / then), um comportamento por teste, com um nome que enuncie esse comportamento: returnsEmptyWhenCustomerHasNoInvoices. As asserções usam uma biblioteca fluente (AssertJ) ou o assertEquals(expected, actual) do JUnit 5 — nessa ordem de argumentos, para que as mensagens de falha saiam corretas. A trilha de testes cobre as ferramentas em detalhe.
Fazendo o padrão valer
Um padrão que mora numa wiki é só uma sugestão. Torne-o executável:
Adote um formatador
Adicione o Spotless (Gradle ou Maven) configurado com ogoogle-java-formatou com o XML de formatador Eclipse/IntelliJ compartilhado pelo time. RodespotlessApplylocalmente espotlessCheckna CI. Faça um único commit só de formatação em toda a base de código para manter o histórico limpo.Adicione o Checkstyle para a estrutura
O Checkstyle impõe o que um formatador não consegue: nomenclatura, ordem dos imports, tamanho dos métodos, quantidade de parâmetros, Javadoc ausente na API pública, ordem dos modificadores, nada de imports com asterisco. Comece pela configuração do Google ou da Sun e remova as regras de que você discorda em vez de adicionar exceções no código.Adicione um analisador de padrões de bugs
O Error Prone (um plugin dojavac) e o SpotBugs pegam problemas de correção —equalsem arrays, valores de retorno ignorados, strings de formato incompatíveis,@Overrideausente. O PMD acrescenta regras de design. Trate os achados deles como falhas de build, não como avisos, senão vão ser ignorados.Acompanhe o resto no SonarQube
Um quality gate sobre o código novo — cobertura, duplicação, nenhum novo problema crítico — impede que uma base de código legada piore sem exigir que tudo seja corrigido de uma vez.Mantenha a IDE consistente
Versione o.editorconfige as configurações de estilo de código da IDE para que o editor de cada desenvolvedor formate igual à CI, e ative as ações ao salvar para organizar os imports e reformatar.
O guia de análise estática da trilha de testes se aprofunda na configuração dessas ferramentas. O princípio é o mesmo para todas: regras que uma máquina consegue verificar devem ser verificadas por uma máquina, para que a revisão humana seja gasta com nomes, design e com a questão de o código resolver ou não o problema certo.
Erros comuns
Notação húngara e prefixos de tipo. strName, iCount, m_field descrevem o tipo, que o compilador já conhece, e ficam desatualizados quando o tipo muda.
Abreviações. calcTtlAmt economiza seis toques de tecla uma vez e custa a cada leitor o esforço de decifrar. Escreva por extenso.
Código comentado. O controle de versão já guarda esse código; o comentário só confunde.
Supressão genérica. @SuppressWarnings("all") em uma classe esconde problemas reais. Suprima um único aviso, no menor escopo possível, com um comentário explicando o porquê.
Campos públicos e setters para tudo. Setters gerados transformam uma classe em um struct mutável. Exponha comportamento, não estado, como defende o guia de fundamentos de orientação a objetos.
Parâmetros booleanos. render(true, false) é ilegível no ponto de chamada. Use um enum ou dois métodos.
Grafia inconsistente na base de código. colour em um módulo e color em outro tornam a busca pouco confiável. Escolha uma variante do inglês para os identificadores (o inglês americano bate com o JDK) e uma para a prosa.
Principais pontos
- Os nomes carregam o significado: siga à risca as convenções de camel case, indique as unidades, evite abreviações e palavras vazias e faça os booleanos se lerem como predicados.
- Organize as classes na ordem convencional, com imports explícitos, campos no topo e métodos curtos moldados por cláusulas de guarda.
- Formatação é um problema resolvido — adote um formatador e nunca mais discuta espaços.
- O Javadoc documenta contratos da API pública; comentários no código explicam o porquê, não o quê.
- Expressões idiomáticas que previnem bugs fazem parte do padrão: nada de coleções null,
equalspara strings, constantes com nome, imutabilidade por padrão, try-with-resources e logs parametrizados. - Faça o padrão valer com Spotless, Checkstyle, Error Prone ou SpotBugs e um quality gate do SonarQube, para que as revisões foquem no design.
Fontes e leituras complementares
Perguntas frequentes
- Qual guia de estilo Java meu time deve adotar?
- Escolha um que uma ferramenta consiga fazer valer e pare de discutir. O Google Java Style Guide com o google-java-format é a escolha mais comum, porque o formatador não deixa nada em aberto; as convenções originais da Oracle são a referência histórica da qual a maioria das outras deriva. O que importa é que toda a base de código siga um único estilo e que a CI rejeite os desvios.
- Qual deve ser o tamanho de um método?
- Curto o bastante para ser entendido sem rolar a tela e para receber um nome honesto. Muitos times definem um limite de referência entre 20 e 30 linhas e um limite rígido no Checkstyle um pouco acima disso. Um método que precisa de um comentário para separar suas seções está pedindo para ser dividido; um método chamado uma única vez, mas com um nome claro, ainda vale a extração pela legibilidade.
- Todo método deve ter Javadoc?
- Não. A API pública — tudo o que outro módulo ou outro time chama — deve documentar seu contrato: pré-condições, valor de retorno, exceções, thread-safety. Métodos privados e getters autoexplicativos não devem carregar um comentário que repete a assinatura. Um Javadoc que diz “Gets the name” em getName() é ruído que ensina o leitor a pular comentários.
- Comentários são sinal de código ruim?
- Os que explicam o que o código faz geralmente são, porque um nome melhor os tornaria desnecessários. Os que explicam o porquê — o contorno para um bug de driver, uma regra de negócio com o número do ticket, uma escolha de desempenho nada óbvia — são valiosos e difíceis de substituir. Escreva os do segundo tipo e apague os do primeiro.