Programmation Java
Conventions de code Java : nommage, structure, Javadoc et règles automatisables
Débutant12 min de lectureMis à jour
Cette page a été traduite de l'anglais avec l'aide de l'IA, puis vérifiée par rapport à l'original. Lire la version anglaise
Les conventions de code existent parce que le code est lu bien plus souvent qu'il n'est écrit, et généralement par quelqu'un d'autre que son auteur. Ce guide présente les règles vers lesquelles convergent la plupart des équipes Java — nommage, organisation des classes, formatage, imports, Javadoc, gestion du null, idiomes pour les chaînes et les logs —, explique la raison d'être de chacune pour que vous les appliquiez avec discernement, et montre comment les automatiser avec un formateur, Checkstyle, Error Prone et SonarQube plutôt qu'à coups de remarques en code review.
Ce que vous allez apprendre
- Appliquer les conventions de nommage Java standard aux packages, types, méthodes, constantes et génériques
- Organiser une classe selon l'ordre conventionnel des membres, avec des imports propres et des méthodes courtes
- Écrire une Javadoc qui documente le contrat au lieu de répéter la signature
- Traiter le null, les chaînes, les nombres magiques et les logs de manière idiomatique
- Automatiser le style avec un formateur et de l'analyse statique pour que les revues portent sur la conception
L'essentiel de la vie d'une base de code, c'est de la maintenance, et l'essentiel de la maintenance, c'est de la lecture. Un style cohérent n'est pas une affaire d'esthétique : il supprime les petites décisions (où va l'accolade, est-ce une constante ou une variable, que veut dire mgr) pour que le lecteur consacre son attention à ce que fait le code. Java dispose de conventions documentées depuis les années 1990, et l'industrie a depuis convergé vers un ensemble de règles assez restreint, dont la plupart peuvent être vérifiées par un outil. Ce guide présente ces règles avec leurs justifications, cible Java 21 et se termine par la manière de les faire respecter pour que le style ne soit plus jamais un sujet de code review.
Nommage
Les noms sont la partie la plus importante d'une convention, car ils portent un sens qu'aucun formateur ne peut ajouter. Les conventions Java sont précises :
| Élément | Convention | Exemples |
|---|---|---|
| Package | tout en minuscules, domaine inversé, sans underscore | com.example.billing.invoice |
| Classe, interface, enum, record | UpperCamelCase, des substantifs | InvoiceService, PaymentResult |
| Méthode | lowerCamelCase, des verbes ou groupes verbaux | calculateTotal(), isExpired(), findByEmail() |
| Variable, champ, paramètre | lowerCamelCase, des substantifs | retryCount, customerEmail |
Constante (static final immuable) | UPPER_SNAKE_CASE | MAX_RETRIES, DEFAULT_TIMEOUT |
| Paramètre de type | une seule lettre majuscule | T, E, K, V, R |
| Constante d'enum | UPPER_SNAKE_CASE | Status.IN_PROGRESS |
| Méthode de test | descriptive, underscores autorisés | rejectsNegativeAmount() ou withdraw_insufficientFunds_throws() |
Au-delà de la mécanique, les bons noms partagent trois propriétés. Ils sont prononçables et faciles à rechercher : customerRepository se retrouve avec grep et se dit à voix haute, alors que custRepo2 est introuvable et que x correspond à tout. Ils indiquent l'unité ou le type quand il y a ambiguïté : timeoutMillis, priceCents, sizeBytes évitent le classique bug du facteur 1000 mieux que n'importe quel commentaire. Et ils évitent les mots creux : InvoiceData, InvoiceInfo, InvoiceObject ne disent rien de plus que Invoice.
Les booléens se lisent mieux sous forme de prédicats : isActive, hasChildren, canRetry. N'introduisez pas de négation dans le nom (isNotReady), sinon les appelants finiront par écrire !isNotReady. Les acronymes suivent le camel case comme des mots ordinaires — HttpClient, parseXml, userId —, car HTTPURLConnection est illisible et XMLHTTPRequest reste un avertissement venu du passé.
Un champ static final n'est une constante que si sa valeur est profondément immuable. static final List<String> NAMES = new ArrayList<>() est une variable globale mutable déguisée : utilisez List.of(...), ou nommez-la en lowerCamelCase pour signaler qu'elle peut changer.
Organisation du fichier source et de la classe
Un seul type public de premier niveau par fichier, qui porte son nom. À l'intérieur du fichier, l'ordre conventionnel aide le lecteur à s'orienter :
- L'en-tête de licence ou de copyright, si le projet en utilise un
- L'instruction
package - Les imports : imports statiques d'abord, puis imports normaux, sans wildcard ni import inutilisé
- La Javadoc de la classe et sa déclaration
- Les champs statiques (constantes en tête), puis les champs d'instance
- Les constructeurs et les fabriques statiques
- Les méthodes, regroupées par rôle — l'API publique d'abord, ou dans l'ordre des appels pour qu'on puisse lire de haut en bas
equals,hashCode,toString- Les types imbriqués
Les imports wildcard (import java.util.*;) masquent la provenance d'un type et provoquent des conflits silencieux lorsque deux packages finissent par contenir une classe du même nom ; les IDE les regroupent automatiquement, alors configurez le vôtre pour qu'il ne le fasse pas. Dans une classe, gardez les champs ensemble en haut au lieu de les disperser à côté des méthodes qui s'en servent : le lecteur doit pouvoir voir tout l'état d'un objet sur un seul écran.
L'ordre des modificateurs est fixé par la recommandation de la spécification du langage et vérifié par tous les linters : public protected private abstract static final transient volatile synchronized native strictfp. Écrivez public static final, jamais final static public.
Formatage
Le formatage est la partie d'une convention qui devrait être entièrement automatique. Les règles consensuelles :
- Indentation : 4 espaces (Oracle) ou 2 espaces (Google) ; jamais de tabulations, car leur largeur varie d'un éditeur à l'autre.
- Longueur de ligne : 100 ou 120 colonnes. L'ancienne limite de 80 colonnes date d'avant les écrans larges, mais les lignes très longues restent difficiles à parcourir.
- Accolades : style K&R (accolade ouvrante sur la même ligne), et toujours présentes, même pour un
ifd'une ligne. Le buggoto faild'Apple venait d'unifsans accolades avec une ligne dupliquée. - Une instruction par ligne, une déclaration de variable par ligne, au plus près de la première utilisation.
- Les lignes vides séparent les groupes logiques : entre les méthodes, entre les champs et les constructeurs, et à l'intérieur d'une longue méthode pour marquer les étapes — mais une méthode qui a besoin de nombreux groupes de ce genre doit être découpée.
- Espaces : après les mots-clés (
if (,for (), autour des opérateurs binaires, après les virgules ; aucun à l'intérieur des parenthèses.
// 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));
}
La décision de formatage la plus utile qu'une équipe puisse prendre est d'arrêter de prendre des décisions de formatage : adoptez google-java-format (ou le formateur de l'IDE avec une configuration partagée et versionnée, via Spotless) et exécutez-le à l'enregistrement et dans la CI. Un formateur qui réécrit tout le fichier, au lieu de vérifier un sous-ensemble de règles, met fin pour de bon à toutes les discussions sur les espaces.
Taille des méthodes et des classes
Les petites unités sont plus faciles à nommer, à tester et à réutiliser. Les plafonds pratiques que beaucoup d'équipes imposent avec Checkstyle sont : des méthodes de moins de 30 lignes environ, 4 ou 5 paramètres au maximum, des classes de moins de quelques centaines de lignes et pas plus de trois niveaux d'imbrication. Ce sont des signaux à examiner, pas des lois : un switch sur une enum de 20 constantes peut très bien faire 40 lignes, et un parseur peut légitimement en faire 500.
Deux refactorings règlent la plupart des dépassements. Extraire une méthode transforme un bloc commenté en appel nommé. Introduire un objet paramètre transforme create(String name, String email, String street, String city, String zip) en create(Person person, Address address), ce qui empêche aussi les appelants d'intervertir deux arguments String. L'imbrication profonde se corrige généralement avec des clauses de garde : on retourne ou on lève une exception tôt pour les cas invalides, puis on écrit le chemin principal sans indentation.
// 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);
}
Commentaires et Javadoc
La Javadoc est de la documentation d'API : elle décrit le contrat des types et méthodes publics pour des gens qui les appelleront sans lire l'implémentation. Elle est transformée en HTML et lue dans les infobulles de l'IDE, donc sa structure compte.
/**
* 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) { ... }
La première phrase est un résumé repris dans les index : faites-en un énoncé complet de ce que fait la méthode. Le corps explique le contrat : effets de bord, atomicité, comportement en cas d'échec, thread-safety, unités. @param, @return et @throws documentent chaque paramètre, le résultat et chaque exception qu'un appelant pourrait traiter. Ne documentez pas les RuntimeException levées uniquement en cas d'erreur de programmation, sauf si la condition fait partie du contrat.
Ce qu'une Javadoc ne doit pas contenir : une reformulation de la signature (« Returns the name »), des détails d'implémentation qui finiront par diverger, ou un tag d'auteur et un historique des modifications — le contrôle de version le fait mieux. Depuis Java 23, la forme Markdown /// est disponible, mais en Java 21, restez sur la forme HTML avec <p> entre les paragraphes.
Dans le corps des méthodes, utilisez les commentaires // pour le pourquoi : le ticket à l'origine d'un contournement, la raison pour laquelle une boucle est écrite à la main pour les performances, l'invariant sur lequel repose un bout de code. Un commentaire qui dit ce que fait un bloc est un nom de méthode qui s'ignore. TODO et FIXME sont acceptables avec un responsable ou une référence de ticket ; un TODO sans responsable est permanent.
Idiomes au niveau du langage
Plusieurs règles de la plupart des conventions portent sur la correction du code plutôt que sur son apparence.
Le null. Ne retournez jamais null pour une collection : retournez-en une vide. Retournez un Optional pour une valeur unique qui peut être absente. Validez les paramètres au début des méthodes publiques avec Objects.requireNonNull, et documentez les paramètres nullables (avec une annotation comme @Nullable des projets JSpecify ou Checker Framework, que les analyseurs statiques comprennent).
Les chaînes. Comparez avec equals (ou equalsIgnoreCase), jamais avec ==. Préférez String.isBlank() à trim().isEmpty(). La concaténation avec + convient sur une ligne — depuis Java 9, le compilateur émet une concaténation efficace basée sur invokedynamic —, mais dans une boucle, utilisez StringBuilder, et pour les templates multilignes, les text blocks (""") avec formatted().
Les nombres magiques. Tout littéral autre que 0, 1 et -1 mérite une constante nommée : if (retries > 3) devient if (retries > MAX_RETRIES). Le nom documente l'intention et la valeur n'a qu'un seul endroit où changer.
Égalité et hachage. Redéfinissez equals, hashCode et toString ensemble, ou utilisez un record. Ne les basez jamais sur des champs mutables pour des objets utilisés comme clés.
L'immuabilité par défaut. final sur les champs, sur les paramètres utilisés dans des lambdas et sur les variables locales jamais réaffectées ; des collections non modifiables renvoyées par les getters ; des records pour les valeurs.
Les ressources. Tout ce qui est AutoCloseable va dans un try-with-resources ; jamais de close() isolé dans un finally.
Le switch. Utilisez la forme fléchée (case A -> ..., Java 14) pour éliminer le fall-through ; utilisez des switch expressions lorsque le résultat est une valeur ; omettez default sur les types sealed et les enums quand vous voulez que le compilateur signale les nouvelles constantes.
Les logs. Utilisez SLF4J avec des messages paramétrés — log.warn("Payment {} declined: {}", id, code) —, jamais la concaténation, qui paie le coût du formatage même quand le niveau est désactivé. Passez l'exception en dernier argument pour que la stack trace soit capturée. Ne journalisez jamais de secrets ni de données personnelles complètes. Une ligne de log par événement, à la frontière, pas une par couche.
La visibilité. La portée la plus étroite qui fonctionne : champs private, classes utilitaires package-private, protected pour les méthodes dont les sous-classes ont besoin, et public uniquement pour la véritable API. Les classes qui ne sont pas conçues pour l'héritage sont final.
Les tests font partie de la convention
Le code de test suit les mêmes conventions, avec deux assouplissements : les noms de méthodes peuvent contenir des underscores pour se lire comme une phrase, et un peu de duplication entre tests vaut mieux qu'une hiérarchie de helpers qui cache ce que vérifie chaque test. Structurez chaque test en arrange / act / assert (ou given / when / then), un comportement par test, avec un nom qui énonce ce comportement : returnsEmptyWhenCustomerHasNoInvoices. Les assertions utilisent une bibliothèque fluide (AssertJ) ou assertEquals(expected, actual) de JUnit 5 — dans cet ordre d'arguments, pour que les messages d'échec soient justes. La rubrique consacrée aux tests couvre l'outillage en détail.
Faire respecter la convention
Une convention qui vit dans un wiki n'est qu'une suggestion. Rendez-la exécutable :
Adopter un formateur
Ajoutez Spotless (Gradle ou Maven), configuré avecgoogle-java-formatou avec le XML de formateur Eclipse/IntelliJ partagé par l'équipe. LancezspotlessApplyen local etspotlessCheckdans la CI. Faites un unique commit de formatage sur toute la base de code pour garder un historique propre.Ajouter Checkstyle pour la structure
Checkstyle impose ce qu'un formateur ne peut pas : le nommage, l'ordre des imports, la longueur des méthodes, le nombre de paramètres, la Javadoc manquante sur l'API publique, l'ordre des modificateurs, l'absence d'imports étoile. Partez de la configuration Google ou Sun et retirez les règles avec lesquelles vous n'êtes pas d'accord plutôt que d'ajouter des exceptions dans le code.Ajouter un analyseur de bug patterns
Error Prone (un pluginjavac) et SpotBugs détectent les problèmes de correction —equalssur des tableaux, valeurs de retour ignorées, chaînes de format incohérentes,@Overridemanquant. PMD ajoute des règles de conception. Traitez leurs résultats comme des échecs de build, pas comme des avertissements, sinon ils seront ignorés.Suivre le reste dans SonarQube
Un quality gate sur le nouveau code — couverture, duplication, aucun nouveau problème critique — empêche une base de code legacy de se dégrader sans exiger de tout corriger d'un coup.Garder l'IDE cohérent
Versionnez.editorconfiget les réglages de style de code de l'IDE pour que l'éditeur de chaque développeur formate comme la CI, et activez les actions à l'enregistrement pour organiser les imports et reformater.
Le guide sur l'analyse statique, dans la rubrique consacrée aux tests, approfondit la configuration de ces outils. Le principe est le même pour tous : les règles qu'une machine peut vérifier doivent être vérifiées par une machine, afin que la revue humaine porte sur le nommage, la conception et la question de savoir si le code résout le bon problème.
Erreurs courantes
Notation hongroise et préfixes de type. strName, iCount, m_field décrivent le type, que le compilateur connaît déjà, et deviennent faux dès que le type change.
Les abréviations. calcTtlAmt économise six frappes une fois et coûte un décodage à chaque lecteur. Écrivez les mots en entier.
Le code commenté. Le contrôle de version s'en souvient ; le commentaire ne fait qu'embrouiller.
La suppression fourre-tout. @SuppressWarnings("all") sur une classe masque de vrais problèmes. Supprimez un seul avertissement, sur la portée la plus petite possible, avec un commentaire qui explique pourquoi.
Des champs publics et des setters partout. Des setters générés transforment une classe en simple structure mutable. Exposez du comportement, pas de l'état, comme le défend le guide sur les bases de l'objet.
Les paramètres booléens. render(true, false) est illisible à l'endroit de l'appel. Utilisez une enum ou deux méthodes.
Une orthographe incohérente dans la base de code. colour dans un module et color dans un autre rendent la recherche peu fiable. Choisissez une variante d'anglais pour les identifiants (l'anglais américain correspond au JDK) et une pour la prose.
Points clés à retenir
- Les noms portent le sens : suivez exactement les conventions de camel case, indiquez les unités, évitez les abréviations et les mots creux, et faites en sorte que les booléens se lisent comme des prédicats.
- Organisez les classes dans l'ordre conventionnel, avec des imports explicites, les champs en haut et des méthodes courtes structurées par des clauses de garde.
- Le formatage est un problème résolu : adoptez un formateur et ne discutez plus jamais des espaces.
- La Javadoc documente le contrat de l'API publique ; les commentaires en ligne expliquent le pourquoi, pas le quoi.
- Les idiomes qui préviennent les bugs ont leur place dans la convention : pas de collections nulles,
equalspour les chaînes, des constantes nommées, l'immuabilité par défaut, try-with-resources, des logs paramétrés. - Faites respecter le tout avec Spotless, Checkstyle, Error Prone ou SpotBugs et un quality gate SonarQube, pour que les revues portent sur la conception.
Sources et lectures complémentaires
Questions fréquentes
- Quel guide de style Java mon équipe doit-elle adopter ?
- Choisissez-en un qu'un outil sait faire respecter, et arrêtez d'en débattre. Le Google Java Style Guide associé à google-java-format est le choix le plus répandu, car le formateur ne laisse plus rien à discuter ; les conventions d'origine d'Oracle sont la référence historique dont la plupart des autres dérivent. L'essentiel est que toute la base de code suive un seul style et que la CI refuse les écarts.
- Quelle longueur doit faire une méthode ?
- Assez courte pour se comprendre sans défiler et pour recevoir un nom honnête. Beaucoup d'équipes fixent une limite indicative autour de 20 à 30 lignes et une limite Checkstyle stricte un peu au-dessus. Une méthode qui a besoin d'un commentaire pour séparer ses sections demande à être découpée ; une méthode appelée une seule fois mais dotée d'un nom clair mérite quand même d'être extraite pour la lisibilité.
- Chaque méthode doit-elle avoir une Javadoc ?
- Non. L'API publique — tout ce qu'un autre module ou une autre équipe appelle — doit documenter son contrat : préconditions, valeur de retour, exceptions, thread-safety. Les méthodes privées et les getters évidents ne doivent pas porter un commentaire qui répète la signature. Une Javadoc « Gets the name » sur getName() n'est que du bruit, qui habitue les lecteurs à sauter les commentaires.
- Les commentaires sont-ils le signe d'un mauvais code ?
- Ceux qui expliquent ce que fait le code le sont généralement, car un meilleur nom les rendrait inutiles. Ceux qui expliquent pourquoi — le contournement d'un bug de driver, une règle métier avec son numéro de ticket, un choix de performance peu évident — ont de la valeur et sont difficiles à remplacer. Écrivez les seconds et supprimez les premiers.