- Utiliser les conventions de Sun les créateurs du langage Sun Java Coding Style Guide.
- Ne pas utiliser d'outil du type
Format on Save
. - Utiliser
EditorConfig
. - Éviter les parenthèses inutiles.
- Ajouter toujours une javadoc aux composants publics (classes, méthodes, constantes, enum, ...).
- Soigner l'ordre de déclaration des éléments dans une classe : membres statiques -> membres -> méthodes statiques -> constructeur(s) -> méthodes d'instance (Java Coding Style Guide de Sun).
- Limiter la taille des méthodes.
- Les classes générées doivent être dans des modules séparés.
- Soigner le nommage :
- Un nom doit expliquer ce que fait / ce qu'est l'élément nommé mais pas comment il le fait.
- Éviter les préfixes et suffixes.
- Le code doit être assez clair pour se passer de commentaires. Limiter leur utilisation car ils deviennent vite obsolètes ou erronés.
- Les commentaires sous la forme
/** xxx */
(avec 2 *) sont exclusivement réservés à la Javadoc. Dans le code il faut utiliser soit//
soit/* xxx */
(avec 1 *). 🔗 🇬🇧 Kinds Of Comments par Nicolai Parlog
- Il ne faut rendre
public
que des constantes qui le sont réellement (utilisées par plusieurs classes) sinon elles doivent être déclarées commeprivate
dans les classes qui les utilisent. - Ne pas mettre en constante (
static final
) des objets mutables. Par exemple les tableaux,StringBuilder
, collections, ...
- Ne jamais masquer un problème, les exceptions sont typiquement faites pour identifier ou alerter sur quelque chose d'exceptionnel.
- Ne pas attraper
java.lang.Exception
/java.lang.RuntimeException
/java.lang.Error
/java.lang.Throwable
. - Toujours s'assurer de la fermeture des ressources ouvertes.
- Utiliser "try-with-resources" (depuis Java 7).
- Ne pas utiliser d'instruction
return
dans un blocfinally
. - Ne pas lever d'exception dans un bloc
finally
. - Ne pas lever d'exception pour l'attraper immédiatement.
- Ne pas attraper une exception juste pour la logguer (log & re-throw).
🔗 🇬🇧 Best (and Worst) Java Exception Handling Practices par David Landup
- Nommer les threads.
- Préférer utiliser les pools de threads de la JVM ou du serveur d'applications plutôt que de gérer soit même les threads.
- Préférer implémenter un
Runnable
(ou unCallable
) plutôt que d'étendre la classeThread
. - Démarrer un thread via la méthode
start()
plutôt que d'appelerrun()
. - Utiliser des variables
volatile
pour gérer le comportement (l'arrêt par exemple) d'un thread. - Ne pas utiliser les ParallelStreams pour des petits ensembles (< 1000 éléments).
- N'utiliser la parallélisation qu'en cas de besoin : problème de performance avéré par exemple.
- Optimiser le nombre de threads alloués, par exemple en tenant compte de la fréquence des I/O :
- si beaucoup d'I/O on peut augmenter le nombre de threads car ils seront souvent en attente
- au contraire si beaucoup de calculs il faut limiter le nombre de threads (ne pas dépasser le nombre de coeurs par exemple)
Voir la page dédiée
- Favoriser l'utilisation des références de méthodes.
- Ne forcément réinventer des
@FunctionalInterface
il en existe déjà beaucoup de base dansjava.util.function
qui couvrent beaucoup de besoins (Supplier, Consumer, Predicate, Function, BiFunction, ...). - Éviter ou limiter l'utilisation des blocs
() -> { /* bloc */ }
en favorisant l'utilisation de méthodes. 1 lambda = 1 ligne sinon faire une méthode.
- Ne pas retourner de stream, préférer une collection ou un array. Un stream n'est pas un conteneur de données !
- Attention à l'utilisation de la méthode
parallel()
qui utilise le common Fork/join Pool de la JVM. Les risques :- utilisation de tous les processeurs disponibles
- pénurie de thread pour les autres tâches
- Favoriser l'utilisation de JPQL / HQL par rapport au SQL et l'API
Criteria
. - Favoriser l'utilisation de
NamedQueries
. - Éviter l'utilisation de
NativeQueries
. - Ne pas utiliser
@Enumerated(EnumType.ORDINAL)
, le simple ajout d'un élément ou un refactoring casserait l'intégrité de la base de données. - Les champs de type date doivent être précisés à l’aide de l’annotation
@Temporal
. - Pour limiter le nombre de résultats d'une requête utiliser
query.setMaxResults(max);
. - Pour les clefs composites favoriser l'utilisation du couple
@Embeddable
/@EmbeddedId
plutôt que le couple@Id
/@IdClass
.
- Spring : Favoriser l'injection par constructeur dont les avantages sont :
- Plus facilement testable.
- Le membre de classe peut être déclaré
final
, la classe peut devenir immuable. - C'est également le pattern appliqué dans Angular.
- L'annotation (
@Autowired
) devient optionnelle.
🔗 🇬🇧 Java API Design Best Practices par Jonathan Giles
- Favoriser l'utilisation des interfaces (
List
,Map
,Set
, ...) pour déclarer les objets :List<String> l = new ArrayList<>();
plutôt queArrayList<String> l = new ArrayList<>();
. - Favoriser les implémentations immuables : plus robustes, thread-safe, ...
- Éviter les paramètres booléens car peu clair et potentiellement source d'erreur (inversion).
- Considérer l'utilisation des Static Factory Methods (Joshua Bloch's Effective Java) ou des méthodes de type builder :
- Les noms des méthodes sont plus parlant sur les intentions que les constructeurs.
- On peut donc avoir des méthodes différentes avec des paramètres identiques.
- Celles-ci peuvent retourner différents types (sous-types).
- Celles-ci peuvent encapsuler la logique nécessaire à la création des instances, par exemple on peut utiliser un simple
Objects.requireNonNull(param, "message")
. - Celles-ci permettent de contrôler les instanciations, voire les limiter.
- Encapsuler les
switch
dans une méthode dédiée : permet de faire desreturn
de valeurs directement (pas besoin debreak
, le compilateur nous dira si on oublie ledefault
, permet de nommer clairement l'intention via le nom de la méthode). - Favoriser le typage fort
- Utiliser des
enum
plutôt que desString
ou desint
. - Très dangereux sinon en cas de refactoring.
- Éviter la Primitive Obsession : préférer encapsuler les concepts dans des types dédiés même s'ils peuvent être représentés simplement avec un type de base (int, String, ...).
- Utiliser des
- Ne pas utiliser inutilement
this
. - Ne pas utiliser inutilement
@Annot(value = "xxx")
(mais directement@Annot("xxx")
) pour une annotation si c'est l'unique attribut valorisé. - Ne pas initialiser inutilement un objet à
null
. - Ne pas initialiser un type primitif à sa valeur par défaut :
0
,0L
,0d
,false
. - Ne pas déclarer de méthodes
public
dans une interface (elles le sont obligatoirement). - Ne pas déclarer de constantes
public static final
dans une interface (elles le sont obligatoirement). - Ne pas spécifier que le constructeur de l'
enum
estprivate
(il y est obligatoirement). - Ne pas mettre de
;
à la fin d'une déclaration des éléments d'uneenum
:VALEUR1, VALEUR2, VALEUR3,
. - Ne pas mettre de
;
à la fin d'une déclaration des éléments d'un bloctry-with-resource
:try (element = new Element())
. - Ne pas tester le
null
avant d'utiliser une instructioninstanceof
(qui retournefalse
si l'élément estnull
).
Afin d'éviter les timeouts infinis, souvent positionnés par défaut, on veillera à toujours positionner, paramétrer et externaliser les différentes valeurs des timeouts. Par exemple pour les appels suivants :
- WebServices SOAP JAX-WS
- WebServices REST JAX-RS
- Clients HTTP
- Clients SMTP
On différenciera et on positionnera :
- le timeout de connexion (connection timeout), relativement court (exemple : 10sec)
- le timeout de réponse (read timeout, response timeout) adapté à ce que l'on est sensé récupérer (un petit flux JSON ou un gros fichier), ni trop faible ni trop élevé (exemple : 60 sec).
💡 Préconisation : de la moins structurante à la plus structurante. En d'autres termes, plus l'annotation est proche de l'élément auquel elle fait référence plus elle est structurante. Enfin on placera la Javadoc en 1er avant les annotations.
- Annotation processor : génération de byte code, boiler plate, ... (Lombok, MapStruct, ...)
- Documentation (Swagger, ...)
- Validation
- Fonctionnalité
Exemple :
@Slf4j
@RequiredArgsConstructor
@Api(value = "Service d'accès à XXX")
@Validated
@RestController
@RequestMapping(path = "/service")
public class MyService {
}
Exemple :
@Getter
@NotNull
@Column(name = "COL")
private String member;
Exemple :
public XXXResponse rechercher(@ApiParam(value = "Le critère de recherche") @Valid @NotBlank(message = "Critère de recherche obligatoire") @PathVariable(name = "value") String value) {
}
- Comparator vs Comparable : On utilise en général
Comparable
pour l'ordre naturel et l'on écrit un ou plusieursComparator
pour les autres besoins, ou si l'on n'est pas le propriétaire de la classe. - Ne jamais utiliser la méthode
close
sur un objetScanner
initialisé avecSystem.in
(sinon celui-ci deviendra inutilisable jusqu'à l'arrêt de la JVM).