Les parties de Java qui ne se comportent pas comme elles se lisent.
Effacement de type, ordre d'initialisation, résolution de surcharge, modèle mémoire. Lisez le concept, puis prouvez que vous le maîtrisez — et exécutez le code vous-même quand la réponse vous surprend.
Cycle de vie des objets
- 0001 Construction et ordre d'initialisation Créer une instance exécute une séquence fixe : les initialiseurs de champs et les blocs d'instance dans l'ordre du source, puis le corps du constructeur — et la superclasse termine tout cela avant que la sous-classe ne commence.
- 0002 Ramasse-miettes et finaliseurs Le collecteur récupère les objets inatteignables depuis toute racine GC, selon un calendrier sur lequel on ne peut pas compter. finalize() est dépréciée et inutilisable pour libérer des ressources ; Cleaner ou try-with-resources est la réponse.
- 0003 Motifs de construction Des façons de contrôler l'instanciation : un singleton garantit une seule instance, une fabrique statique nomme et met en cache la construction, et l'injection de dépendances fournit les collaborateurs plutôt que de laisser un objet les construire lui-même.
Méthodes d'objet
- 0004 Le contrat equals/hashCode Des objets égaux doivent avoir des hash codes égaux. Enfreignez cela et l'objet se comporte mal silencieusement dans toute collection à base de hachage — il entre dans une HashMap et n'en ressort jamais.
- 0005 clone, copie et copies défensives Object.clone() produit une copie superficielle à travers un protocole cassé. Un constructeur de copie ou une fabrique statique est plus clair, et les champs mutables qui traversent une frontière d'API doivent être copiés dans les deux sens.
Bonnes pratiques générales
- 0006 Le pool de constantes de chaînes Les littéraux String sont internés dans un pool à l'échelle de la JVM, donc des littéraux identiques sont le même objet. Les chaînes construites à l'exécution ne sont pas mises en pool sauf appel explicite à intern().
- 0016 Boxing, unboxing et le cache Integer L'autoboxing convertit silencieusement entre primitives et wrappers. Les valeurs de -128 à 127 proviennent d'un cache partagé, donc == semble fonctionner sur les petits nombres et échoue sur les grands.
Conception des classes
- 0007 Interfaces fonctionnelles, méthodes default et static Une interface avec exactement une méthode abstraite peut être implémentée par une lambda. Les méthodes default permettent aux interfaces de gagner du comportement sans casser leurs implémenteurs, ce qui est comment Java a rétrofitté les streams sur Collection.
- 0008 Classes immuables Un objet dont l'état observable ne peut pas changer après construction. Les objets immuables sont automatiquement thread-safe, librement partageables, et sûrs comme clés de map.
- 0009 Héritage vs composition L'héritage couple une sous-classe aux détails d'implémentation de son parent, si bien qu'un changement du parent peut silencieusement la casser. La composition délègue à une instance détenue et ne dépend que du contrat publié.
Génériques
- 0010 Effacement de type Les arguments de type générique n'existent qu'à la compilation. Le compilateur les vérifie, puis les efface vers leurs bornes et insère des casts, si bien que le bytecode ne conserve aucune trace de la paramétrisation.
- 0011 Wildcards et PECS ? extends T donne un producteur que l'on peut seulement lire ; ? super T donne un consommateur dans lequel on peut seulement écrire. Producer Extends, Consumer Super.
Enums et annotations
- 0012 Les enums en tant que classes spéciales Une constante d'enum est une instance singleton d'une classe finale générée par le compilateur. Les enums peuvent porter des champs, implémenter des interfaces, et donner à chaque constante son propre corps de méthode.
- 0013 Rétention et cibles des annotations @Retention détermine si une annotation survit jusqu'au fichier .class et jusqu'à l'exécution ; @Target restreint où elle peut être écrite. Seule la rétention RUNTIME est visible par réflexion.
Méthodes
- 0014 Surcharge vs redéfinition La résolution de surcharge choisit une méthode à partir des types statiques, à la compilation. La répartition de redéfinition choisit l'implémentation à partir du type à l'exécution. Les confondre produit du code qui appelle autre chose que ce à quoi vous vous attendiez.
- 0015 Références de méthode Une lambda compacte qui se contente d'appeler une méthode existante. Quatre formes : statique, instance liée, instance non liée, et constructeur.
Exceptions
- 0017 Exceptions vérifiées vs non vérifiées Les exceptions vérifiées héritent d'Exception et doivent être déclarées ou gérées ; les non vérifiées héritent de RuntimeException et n'ont besoin ni de l'un ni de l'autre. La distinction porte sur la capacité réaliste de l'appelant à récupérer.
- 0018 try-with-resources et exceptions supprimées Toute AutoCloseable déclarée dans l'en-tête du try est fermée automatiquement, dans l'ordre inverse, même en cas d'exception — et un échec pendant la fermeture est attaché à l'exception primaire plutôt que de la remplacer.
Concurrence
- 0019 Happens-before Un ordre partiel sur les opérations mémoire. Si l'action A happens-before l'action B, tout ce que A a écrit est visible pour B. Sans un tel lien, la JVM est libre de réordonner et un thread peut lire des valeurs périmées indéfiniment.
- 0020 Executors, futures et pools de threads Un ExecutorService découple la soumission de tâches des threads qui les exécutent. Future est une référence vers un résultat en attente ; CompletableFuture ajoute de la composition sans bloquer.
- 0021 Variables atomiques et compare-and-swap Les classes atomiques utilisent une instruction matérielle de comparaison-échange pour mettre à jour une valeur sans verrou : lire, calculer, puis échanger seulement si rien n'a changé entre-temps.
- 0022 Collections concurrentes et wait/notify ConcurrentHashMap et les files bloquantes offrent un accès thread-safe sans verrou global. wait/notify est la primitive de bas niveau sous-jacente, et c'est presque toujours le mauvais outil aujourd'hui.
Sérialisation
Réflexion
- 0024 L'API de réflexion La réflexion inspecte et invoque des types découverts à l'exécution. Elle propulse chaque framework que vous utilisez, et elle vous coûte la sécurité à la compilation, la performance, et la facilité de refactoring.
- 0025 Method handles Une référence typée et directement exécutable vers une méthode, résolue une fois via un Lookup qui capture les droits d'accès de l'appelant. Plus rapide que la réflexion et à la base d'invokedynamic.
Langages dynamiques
Outillage du compilateur
- 0027 L'API du compilateur Java javax.tools expose javac en tant que bibliothèque, donc une JVM en cours d'exécution peut compiler du source qu'elle a généré ou reçu, puis charger le résultat.
- 0028 Processeurs d'annotations Un plugin de compilation qui lit les éléments annotés et génère de nouveaux fichiers source. Il s'exécute par rounds pendant javac, et peut ajouter du code mais jamais modifier du code existant.