Introduction

La très grande richesse de Java constitue également l'un de ses défauts du point de vue de la personne qui débute et qui ne sait pas trop par où commencer. De fait, les tutoriels pour apprendre les bases sont nombreux, mais lorsqu'il faut aller au-delà, chaque cours va être lié à un aspect spécifique de Java, et il n'est pas toujours aisé de voir les liens entre toutes les facettes, en particulier lorsque l'on débute. Le but de ce tutoriel est donc de présenter une approche parmi d'autres de la découverte progressive de l'écosystème Java et également du J2EE. La complexité de ce tutoriel n'est pas linéaire, elle augmente à mesure que des choses plus compliquées sont abordées. Arrêtez-vous lorsque vous commencerez à ne plus comprendre, et n'hésitez pas à cliquer sur les liens qui renvoient vers des tutoriels ayant trait aux sujets abordés ;).

I. Ma première classe

Ce paragraphe est davantage destiné à situer le point de départ de la progression du tutoriel qu'à fournir un point de départ dans l'apprentissage de Java. De fait, dans l'ensemble de ce tutoriel, on supposera que les bases de la programmation orientée objet (POO) en Java sont connues du lecteur.

Pour l'écriture de la première classe Java, point n'est besoin d'utiliser un outillage complexe. À partir du moment où vous avez installé un JDK et configuré les variables d'environnement, de simples éditeurs de texte tels que Notepad sous Windows ou Gedit sous Ubuntu suffisent pour écrire le premier programme.

 
Sélectionnez
public class PremiereClasse {
    public static void main(String[] args) {
        System.out.println("Bonjour ceci est mon premier essai avec Java");
    }
}

Après sauvegarde dans un fichier que l'on nomme donc "PremiereClasse.java" on peut compiler puis exécuter en ligne de commande à l'aide de:

 
Sélectionnez
$ javac PremiereClasse.java 
$ java PremiereClasse 
Bonjour ceci est mon premier essai avec Java

On peut coder un moment de cette manière. Tant que l'on ne va utiliser que les bibliothèques fournies avec le JDK, ça ne pose aucun souci. Il suffit de placer toutes ses classes à la suite dans le même fichier (une seule sera publique, celle qui contient la méthode main). Cependant vous éprouverez rapidement le besoin d'utiliser un éditeur de texte qui fournisse au minimum la coloration syntaxique afin de rendre la lecture de code plus aisée. Gedit sous Linux le fait déjà, mais il existe des solutions plus poussées comme Geany (qui est multiplateforme) ou Notepad++ (pour Windows et Mac).

II. Utilisation d'un environnement de développement

Si vous progressez en Java, vous allez rapidement vous retrouver confronté aux limites de l'approche précédente, et commencer à utiliser un environnement de développement: Il s'agit d'un logiciel qui va automatiser tout ce que vous faisiez à la main en ligne de commande, et vous fournir des outils d'aide au développement qui vont grandement vous faciliter la vie. Le plus connu est Eclipse, mais il existe plusieurs autres possibilités.

Dans un environnement de développement (EDI, ou en anglais: IDE), vous allez pouvoir très facilement compiler et tester vos programmes, utiliser des fichiers différents pour chaque classe, structurer les classes en packages et sous-packages , exporter l'ensemble du projet sous forme d'un jar exécutable, ajouter des bibliothèques tierces, et dans le cas d'un projet J2EE, déployer le projet dans un serveur d'applications, toutes opérations qui étaient déjà possibles en ligne de commande, mais qui vont se trouver en pratique grandement facilitées par l'utilisation d'un EDI. Dans la suite de ce tutoriel nous nous concentrerons sur Eclipse, qui est le plus répandu (mais pas forcément le plus simple). Vous trouverez ici de nombreux cours pour apprendre à débuter avec Eclipse.

Quelques raccourcis clavier pour vous mettre l'eau à la bouche :

  • CTRL+ESPACE = autocomplétion. Vous commencez à écrire un mot et Eclipse le termine pour vous.
  • ALT+MAJ+J = Javadoc. Permet de générer automatiquement le squelette des commentaires qui ont trait au code survolé par le curseur.
  • CTRL+MAJ+O = Import automatique des dépendances requises par votre classe pour peu qu'elles soient contenues dans des bibliothèques situées dans le CLASSPATH du projet.

Le Classpath est une variable d'environnement du projet qui recense l'ensemble des chemins dans lesquels le compilateur va pouvoir trouver des classes ou des bibliothèques du projet.

Attention à l'UTF-8. Par défaut, Eclipse enregistre les fichiers dans le codage de caractères système. C'est-à-dire en gros ISO pour Windows et UTF-8 pour les autres. C'est pourquoi il est conseillé de choisir un codage donné et de s'y tenir si vous ne souhaitez pas avoir de problèmes avec des caractères accentués qui s'affichent mal (je vous recommande le tout UTF-8).

III. Premières interfaces graphiques

En général, on fait ses armes sur AWT et (surtout) SWING, deux frameworks graphiques inclus dans le JDK, qui permettent la création relativement rapide d'interfaces graphiques. Pour quelqu'un qui débute, le niveau de difficulté peut sembler augmenter, car il y a un certain nombre de nouveaux concepts à assimiler, notamment ceux de Layout (comment organiser les contrôles de l'interface) et d'EventListener (ou comment relier un bouton à une méthode). Si vous venez d'un langage assez unifié comme le .Net, vous allez commencer à prendre conscience qu'en Java comme dans beaucoup d'autres langages, on code à partir de multiples briques fonctionnelles forgées par d'autres, et qui ne sont pas toujours faciles à manipuler. Si jamais vous avez la sensation de vous battre avec un framework, dites-vous que ça peut valoir le coup de chercher à mieux comprendre quand, par qui, comment et pourquoi il a été conçu ;). Pour vous aider à démarrer, il y a pléthore de tutoriels sur les interfaces graphiques Java.

À noter qu'alternativement à SWING, vous pouvez aussi vous tourner vers SWT, qui a été développé par IBM pour pallier certains inconvénients de SWING. On peut également mentionner ici les RCP (RCP signifie "Rich CLient Platform"), qui fournissent des interfaces standard et fournissent un important package logiciel prenant en compte les concepts d'extensibilité, de docking, d'onglets, de menus, de préférences, etc. mais se situent un niveau au-dessus de SWING et SWT en termes d'abstraction. Citons par exemple les plateformes Eclipse RCP et Netbeans RCP, sur lesquelles sont basés les IDE du même nom. Pour aller plus loin...

IV. Traduction au sein de mon application (i18n et l10n)

Envie de créer un programme qui soit utilisable par des gens parlant différents langages ? Il est relativement aisé de parvenir à ce but. L'astuce est toute simple : là où d'ordinaire vous écriviez en dur dans le code la phrase à afficher, vous allez en lieu et place utiliser une clé de traduction et utiliser la classe ResourceBundle pour faire le lien avec la traduction correspondante que vous aurez placée dans un fichier de propriétés situé dans le classpath. Ceci est davantage développé dans le chapitre 24. L'internationalisation du cours Développons en Java de J.M.Doudoux. Pour aller plus loin, il est également intéressant de mentionner ce tutoriel.

Comme le mentionne la documentation de la classe PropertyResourceBundle, il est préférable que les fichiers de propriétés soient encodés au format ISO-8859-1

Si vous vous demandez ce que veulent dire i18n et l10n, sachez que ces sigles barbares sont des raccourcis pour InternationalizatioN et LocalisatioN. À chaque fois on a gardé la première et la dernière lettre, et entre les deux on a mis le nombre de lettres intermédiaires.

V. Un peu d'architecture

Arrivés à un certain stade de développement, avec un projet sans cesse croissant en taille et en complexité, vous allez éprouver le besoin de découper le projet en plusieurs couches fonctionnelles et d'organiser vos classes au sein de ces couches afin de mieux dégager la structure d'ensemble et rendre la compréhension générale plus aisée. Le mécanisme des packages est d'une grande aide pour cela. Une architecture très courante est l'architecture dite "trois tiers": elle consiste à construire une application en trois couches: la couche dite "de présentation" gère l'interface graphique, la couche dite "métier" contient le c½ur de l'application, et la couche dite "d'accès aux données" permet de lire/persister les données nécessaires à l'application (typiquement l'accès à une base de données).

Ayez à l'esprit que pour réussir à créer une application dotée d'une bonne architecture, il faut y réfléchir avant de commencer à coder. C'est toute la phase de modélisation, préliminaire nécessaire à la création de toute application un tant soit peu complexe. Pour cette raison, il y a de nombreux tutoriels sur la question. Mais attention, il n'est pas toujours bon de se lancer dans une modélisation très complexe, cela doit vraiment dépendre de vos besoins. Abandonnez l'idée du design parfait (qui n'existe pas), car vous aurez forcément besoin de retoucher des détails pendant que vous coderez. Mieux vaut définir une architecture générale, et spécifier en détail le fonctionnement des algorithmes les plus complexes, mais inutile d'aller plus loin.

Vous entendrez probablement parler aussi de Design Patterns. Il s'agit tout simplement des problèmes courants que peut rencontrer un développeur, et des moyens reconnus par la communauté comme les plus pertinents pour y répondre. De mon expérience, la plupart ne sont pas immédiatement accessibles au débutant. Il est nécessaire d'avoir un minimum d'expérience et avoir vraiment été confronté au problème pour saisir la pertinence d'un pattern donné.

VI. Bibliothèques externes et Frameworks

Java est un langage très populaire et relativement ancien, il existe donc de nombreuses bibliothèques et frameworks qui implémentent les besoins les plus courants du développeur et lui permettent de se concentrer sur la partie métier, c'est-à-dire ce qui l'intéresse vraiment et qui a trait au but dans lequel il développe, et non pas des trucs bateaux comme les entrées sorties, la connexion internet, l'export sous forme d'images ou de PDF, l'écriture d'emails, etc. La différence que je verrai entre une librairie et un framework est la suivante: une librairie est une archive jar que vous allez rajouter dans votre classpath, et dont vous allez utiliser les classes directement. Exemple typique, jdbc (qui permet d'accéder à une base de données). Alors qu'un framework, pour prendre une analogie ça va être comme un programme déjà existant au cœur duquel vous inséreriez le vôtre. Le framework se chargeant de toutes les opérations communes et vous apportant des fonctionnalités intéressantes comme l'inversion de contrôle (ce n'est plus vous qui appelez les classes du framework, mais le framework qui appelle vos classes) et l'injection de dépendances (utile en programmation par contrats: vous déclarez un objet d'après l'interface qu'il doit implémenter et vous utilisez son setter pour lui fournir son implémentation véritable), ce qui vous permettra un meilleur découplage des différentes couches qui constituent l'architecture de l'application. Les frameworks sont particulièrement utiles dans l'univers J2EE (construction d'applications Java tournant dans un serveur Web). Exemple typique: Spring.

  • Par échange de message (cf. middleware)
  • Par contrat (cf. interface)

Pour les plus curieux, quelques explications complémentaires sur l'inversion de contrôle. L'explication qui en a été donnée ici : "ce n'est plus vous qui appelez les classes du framework, mais le framework qui appelle vos classes" est réductrice car elle ne présente qu'une facette de l'inversion de contrôle. Voici une explication plus détaillée :
Le principe consiste à briser une dépendance bidirectionelle (ou cyclique), ou encore à briser une dépendance unidirectionnelle non voulue comme une couche métier qui invoquerait directement des méthodes de la couche de l'IHM. Il existe deux grosses techniques. Dans le premier cas, il s'agit d'avoir un pont de communication abstrait via lequel les éléments s'échangent des messages. Dans le second cas, on détermine un jeu d'interfaces (ou de classes abstraites) qui seront implémentées par la couche invoquée de manière directe. Ensuite cette même couche fournit à la couche qui en a besoin les implémentations à utiliser (souvent par injection).

VII. Fichiers de configuration et annotations

Tôt ou tard si vous continuez à programmer en Java, vous allez avoir besoin d'utiliser des fichiers de configuration et/ou des annotations. Le but est de décrire le comportement du programme et la manière dont il va interagir avec d'autres programmes. Typiquement vous pouvez utiliser un fichier de configuration qui vous est propre pour indiquer des chemins d'accès, ou des paramètres que vous voulez pouvoir changer sans avoir besoin de recompiler le programme. Mais la première fois que vous aurez besoin d'utiliser un fichier de configuration sera sans doute pour configurer un framework ou une librairie.

On distingue généralement deux types de fichiers de configuration: les fichiers XML et les fichiers de propriétés. Les fichiers de propriété sont les plus simples : ils contiennent un ensemble de paires CLÉ=VALEUR à raison d'une par ligne. Les développeurs utilisent souvent des noms de clé qui reflètent la catégorie de celle-ci. Par exemple "monappli.macouchemetier.coefficients.usure=15". De cette manière il est très aisé de savoir à quoi va servir la clé (et d'éviter que deux clés nommées usure ayant trait à deux choses différentes ne se télescopent). Les fichiers XML sont un peu plus complexes, mais il ne faut pas en avoir peur. La structure est similaire à celle d'un fichier HTML et elle est généralement plus simple. L'avantage du XML est qu'il permet d'organiser facilement les données de configuration. Ce qu'on lui reproche généralement est sa verbosité: de nombreuses balises sont nécessaires à la description d'une information même simple. Si vous vous lancez dans le J2EE, vous ne pourrez pas en faire l'économie, puisque vous aurez nécessairement besoin d'un fichier web.xml pour décrire le comportement de votre application au serveur d'applications dans lequel elle sera déployée.

Venons-en aux annotations. Elles sont apparues avec la cinquième version de Java vers fin 2004. Elles visent à réduire le volume des fichiers de configuration (voire à s'en passer totalement), en écrivant directement dans le code à l'aide d'une balise spéciale des informations liées à son comportement. Ce qu'il faut garder en tête, c'est que les annotations ne sont pas un remède miracle, et il ne faut pas verser dans le tout « annotations » comme certains font, mais savoir les utiliser avec discernement. Le danger est le risque d'éparpiller des informations de configuration un peu partout dans le programme alors qu'elles seraient plus claires en étant centralisées dans un unique fichier de configuration. C'est en particulier le cas des annotations qui décrivent le comportement des classes les unes vis-à-vis des autres. À l'inverse, il y a peu d'intérêt à reporter dans un fichier de configuration des annotations propres au fonctionnement interne d'une classe donnée.

VIII. Tests unitaires

Les tests unitaires sont une partie importante des applications que les développeurs ont trop souvent tendance à oublier par manque de temps. Il s'agit tout simplement d'une classe de test qui va vérifier que chaque méthode d'une classe effectue bien le travail demandé. C'est particulièrement utile en cas de refactoring pour se rendre compte immédiatement des problèmes qui ont pu arriver. Par ailleurs les tests unitaires contiennent de précieuses informations sur le comportement attendu de l'application qui sont plus utiles que de longs commentaires. Une bonne pratique consiste à écrire ses tests unitaires avant d'écrire les classes auxquelles ils correspondent. Cela permet tout à la fois :

  • de se poser les bonnes questions : "Quel est le comportement attendu de ma classe? " plutôt que de commencer par modéliser et d'adapter les méthodes a posteriori ;
  • d'être sûr d'avoir son test unitaire fonctionnel une fois la classe finie, et de ne pas être influencé dans le sens "je fais un test qui correspond à ce que j'avais en tête quand j'ai fait la méthode", ce qui peut parfois constituer un piège, puisque l'on ne vérifie pas la conformité aux spécifications.

Cette manière de programmer est appelée le TDD (Test Driven Developpement).

En Java, le framework communément utilisé pour les tests unitaires est JUnit, mais il y a également TestNG, qui est une variante intéressante. De nombreux plugins pour Eclipse rendent aisée l'utilisation des tests unitaires. Citons tout d'abord le plugin pour JUnit lui-même (généralement fourni par défaut avec Eclipse), mais également MoreUnit, qui facilite le lien entre une classe et son test unitaire et la génération de mocks, ainsi qu'Infinitest, qui permet de relancer automatiquement le dernier test effectué à chaque enregistrement d'une modification de fichier.

Rapidement vous pourrez rencontrer le problème suivant : lorsqu'une classe dépend d'une autre classe, comment tester juste cette classe précise sans tester aussi la dépendance ? La solution passe par l'utilisation de stubs (qui simulent le comportement d'une classe donnée) ou de mocks (qui vont rejouer une série de comportements préétablis). Citons EasyMock, JMockit, et Mockito parmi les bibliothèques de génération de mock répandues.

IX. Traces logicielles (Logging)

Lorsque l'on teste un programme, on a besoin de savoir ce qu'il se passe. Au début on utilise l'instruction "System.out.println()" pour écrire dans la console des informations sur le déroulement du programme, mais très rapidement, cela ne suffit plus car il est ennuyeux de devoir recompiler pour le désactiver et qu'il serait également souhaitable de pouvoir définir des niveaux de criticité des messages. C'est pourquoi une solution communément utilisée dans le monde de Java est la librairie commons-logging d'Apache. Elle contient toutes les classes que vous aurez besoin d'appeler depuis votre programme Java. Au lancement elle recherchera dans votre classpath une dépendance chargée de la réalisation effective de l'écriture ou l'affichage des traces. En général, on utilise log4j pour cette tâche. Il est également possible d'appeler log4j directement sans passer par commons-logging, mais généralement on s'en abstient pour éviter de rendre les traces du programme dépendantes d'une implémentation particulière.

Il existe d'autres solutions que commons-logging. On peut citer par exemple SLF4J qui est assez largement utilisé. Il permet de pallier certains défauts que l'on reproche parfois à commons-logging. De la même manière, il en existe aussi pour log4J, dont notamment le projet Logback, qui se pose comme le successeur non officiel de log4J.

X. Bases de données

La plupart des applications un tant soit peu évoluées vont avoir besoin de s'interfacer avec des bases de données. L'outil universel utilisé dans ce but est JDBC. Il est composé de deux parties distinctes : une API Java contenant les fonctions que vous aurez besoin d'appeler, et un driver propre à la base de données que vous allez utiliser. En général on commence à faire ses armes sur l'utilisation des bases de données en Java avec cet outil.

Cependant il faut savoir qu'il existe des API de plus haut niveau qui permettent de gérer plus aisément la persistance des objets métiers en base de données. Citons par exemple JPA (implémentation de la spécification de référence), Hibernate (de loin la solution la plus répandue), et IBatis (qui permet de contrôler finement les requêtes SQL exécutées).

XI. Outils de build

Maven devient très vite incontournable dès lors que l'on commence à avoir un projet Java qui inclut de multiples bibliothèques. Il s'agit d'un outil qui permet d'inclure facilement les bibliothèques dont le projet à besoin, et de les configurer aisément. Il permet également de gérer les dépendances de manière transitive sans en oublier aucune, ce qui est un luxe que vous apprécierez lorsque vous vous serez suffisamment battu avec les "ClassNotFoundException", caractéristiques de l'oubli d'inclusion d'une librairie.

Mais Maven ne s'arrête pas là. Il s'agit en effet d'un outil de build (en français: de construction) de projet hautement configurable, et qui peut s'interfacer à de nombreux outils d'analyse de code (comme Sonar) et d'intégration continue (comme Hudson ou Cruise Control), ce qui en fait un outil à connaitre absolument pour tout développeur Java qui se respecte.

Alternativement à Maven, il convient de mentionner également ANT. Cet outil est plus ancien que Maven et reste d'autant plus répandu qu'il est aussi très utilisé pour construire des projets développés dans d'autres langages. Notons que les deux ne sont nullement incompatibles, il est possible de les adapter l'un à l'autre, par exemple pour migrer en douceur. Pour vous faire une idée des avantages/inconvénients des différents outils de build, voici un comparatif.

XII. Java EE

Java EE (ou J2EE) désigne l'ensemble des technologies permettant de créer des applications d'entreprise. Le principe est simple: on veut éviter de devoir recoder dans chaque projet tout ce qui n'est pas spécifique au cœur de métier de l'application. Par conséquent, on va utiliser un serveur d'applications (qui va contenir tout le code non spécifique), et y embarquer notre application (centrée sur ce qui est propre à notre métier). Des fichiers de configuration permettent de décrire au serveur d'applications comment l'application doit se comporter.
J2EE sert souvent à la création d'applications web, de manière assez similaire à ce que fait PHP. Elle est plus complexe à mettre en œuvre que PHP, mais elle apporte beaucoup plus de possibilités lorsque l'on recherche des fonctionnalités un peu poussées.

Il y a deux facettes dans J2EE. Celle qui est la plus connue est la facette "présentation Web". On l'aborde généralement en commençant par coder des servlets et des JSP suivant le modèle MVC . On va généralement utiliser un serveur d'applications dit "conteneur léger" qui n'implémente la spécification J2EE que dans ce domaine, comme Tomcat ou Jetty.
L'étape suivante consiste à utiliser un framework tel que Spring ou Seam pour avoir la plupart des fonctionnalités requises par une application WEB trois tiers un petit peu plus complexe, et éventuellement un framework de présentation tel que JSF (obligatoire dans le cas de Seam, et en train de devenir un standard, JSP étant plus ou moins déprécié) pour parfaire l'expérience utilisateur mais aussi celle du développeur.

Ce qui est moins connu, c'est que J2EE ne s'arrête pas là. La spécification touche aussi des aspects qui n'intéressent que les concepteurs des applications les plus complexes, comme la distribution d'une application sur plusieurs serveurs, avec des fonctionnalités comme le failover (en gros la tolérance aux pannes, si possible sans interruption du service), et le load-balancing (c'est-à-dire la répartition de charge, tant au niveau du trafic HTTP, que de la charge de l'application elle-même). Il est donc possible d'avoir une même application Java dont les différentes instances d'une même classe seront réparties sur différents serveurs. Pour faire tourner de telles applications, il est nécessaire d'utiliser un serveur d'applications dit "conteneur lourd" qui implémente la totalité de la spécification J2EE, tel que par exemple Glassfish et JBossAS. C'est dans ce contexte que vous pouvez être amené à manipuler des EJB et JCA, qui lors de sa sortie était présentée par Sun comme "La solution" au problème d'intégration entre le monde J2EE et les systèmes d'information d'entreprise.

Conclusion

C'est la fin de ce tour d'horizon de l'écosystème Java, j'espère ne rien avoir oublié d'essentiel, mais normalement ça devrait suffire à quelqu'un débutant le Java pour se faire une idée de la complexité de ce monde et avoir un aperçu d'une manière de l'aborder progressivement.

Petite astuce pour finir, sachez qu'il existe de nombreux plugins Eclipse qui peuvent vous faciliter grandement la vie. Le plugin m2eclipse pour l'intégration de Maven, le plugin Instasearch pour effectuer des recherches dans les fichiers du projet, et le plugin "properties editor" pour éditer des fichiers de propriétés avec un codage en UTF8 transparent en sont de bons exemples.

Remerciements

Au moment de publier enfin cet article, je voudrais remercier Mlny84 et Keulkeul pour leur soutient et leurs conseils, Le y@m's et tout particulièrement Nemek pour leur relecture technique, Djibril pour ses conseils techniques sur les outils de mise en ligne, et enfin ClaudeLELOUP pour sa relecture orthographique détaillée.
Ma bibliographie serait beaucoup trop importante pour que je puisse être exhaustif, mais je voudrais décerner une mention spéciale au Blog de Xebia, qui est une source d'information très fructueuse pour ma veille technologique sur le monde du Java.