NFA 035 Compléments Java Conservatoire National des Arts et Métiers 2013 Guillaume Levieux Plan du cours • Les packages – import, déclaration, visibilité • Normes de codage proposées par Sun : – organisation du fichier, commentaires, indentation, nommage • Documenter son code : Javadoc – objectif, formatage, tags • Tests unitaires : JUnit 4 – principe, assertion, fixtures Les packages en JAVA Définition, import Les packages • Système d’organisation des types (classes ou interfaces) qu’on déclare dans notre programme • Chaque package définit un espace de nommage pour les types qui y sont déclarés – Evite les conflits de noms : on peut donner le même nom à des types, si leur package est différent • Nous utilisons le système de fichiers (hiérarchie de répertoires) pour définir nos packages. Package et répertoires • Chaque package (répertoire) contient : – De 0 à n fichiers .java (unités de compilation) – De 0 à n autres répertoires (sous packages) • Chaque unité de compilation (fichier .java) contient : – Une seule classe ou interface publique, nommée comme le fichier qui la contient. – De 0 a n autres classes ou interfaces, invisibles hors de l'unité de compilation Les packages • Nom d’un package : son chemin d’accès depuis le package de plus haut niveau, avec des “.” • Exemple : java.lang – Le compilateur connait plusieurs répertoires de packages. – L’un de ces répertoires contient un répertoire java, qui contient lui meme un répertoire lang, qui contient par exemple le fichier Object.class • Il existe un package "non nommé", pour les petits programmes qui ne souhaitent pas utiliser les packages Les archives .jar • Un ensemble de packages peut être publié sous forme d'archive .jar (Java ARchive) • C'est un fichier zip des packages et parfois un fichier manifest : – Lien vers la classe principale si jar executable – Lien vers d'autres jar utiles – Signature numérique… • Par exemple, le package java.lang est dans rt.jar, lui meme dans le répertoire lib de votre JRE Déclaration du package • Si une classe est placée dans un package, elle doit le dire au début du fichier où elle est déclarée : – package truc.machin.chose • Si la classe n’est dans aucun package, pas besoin de le spécifier au début du fichier et elle sera placée dans le package sans nom. Import d'un package • Il est possible d'accéder aux classes d'un package depuis un autre package. • Pour simplifier cet accès, on utilise la commande import • Ex : import java.io.File; import java.util.ArrayList; Import d'un package • "import" ne concerne pas un package, mais tout ou partie de son contenu : – import machin.truc est faux ! • On peut importer un type spécifique – import machin.truc.MaClasse • On peut importer tous les types d’un package (le type sera chargé si on l’utilise) – import machin.truc.* Import d'un package • Ne pas oublier : les packages définissent des espaces de noms. • On peut accéder à n'importe quelle classe sans utiliser import : – Il suffit de préfixer le nom de la classe par son nom de package (laborieux) : ex : truc.machin.MaClasse.test(); //methode statique de MaClasse • Donc import "n’ajoute" pas les types d’un package : – Simplement, les types définis dans un package seront accessibles directement, sans les préfixer de leur nom de package complet. Visibilité • Tous les types publiques de plus haut niveau d’un même package se voient entre eux directement • La hiérarchie n’a aucun impact : – Une classe du package machin ne voit pas plus une classe de machin.truc (sous package) qu’une classe de blah.hop. Visibilité • On a accès aux types publiques des autres packages. • Le fait d'être du même package apporte des droits supplémentaires sur les membres des classes du même package – Si package différent : on ne voit que les membres publiques. – Si même package : on voit aussi les "protected" et "par defaut" • Voir aussi si héritage, mais autre cours. CLASSPATH • Les chemins de packages sont relatifs. • Il faut bien indiquer le chemin absolu de la racine au compilateur et à la JVM. • Le CLASSPATH permet de dire où se trouvent les packages sur le disque. On peut aussi le définir dans l’EDI. • On donne la racine des répertoires qui contiennent des packages, ensuite les directives import donnent le reste du chemin package-info.java • Le fichier package-info.java est un fichier particulier du package. • Il permet de fournir des informations sur le package – annoter le package – placer des commentaires javadoc qui concernent le package Exemple sous Eclipse Présentation des normes de codage définies par SUN Disponibles en ligne http://www.oracle.com/technetwor k/java/codeconvtoc-136057.html Pourquoi des normes de codage ? • Selon SUN : 80% du coût d’une portion de code provient de sa maintenance. • Améliorer la lisibilité du code • La logique de traitement doit être facilement assimilable visuellement (identation, nommage) • Le code doit être transférable entre différents auteurs (conventions) • Vous même oublierez… et deviendrez votre propre client. Le nommage des fichiers • Fichiers sources : – Contient un type (classe / interface) publique – Même nom que le type publique – Extension .java • Fichiers compilés : – Bytecode pour la machine virtuelle – Extension .class Le contenu des fichiers On se limite à 2000 lignes de source par classe Le fichier commence par un commentaire, lié au fichier : (version, droits d'utilisation) • • • • • • • • • • • • • /* * @(#)Blah.java 1.82 99/03/18 * * Copyright (c) 1994-1999 Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. * All rights reserved. * * This software is the confidential and proprietary information of Sun * Microsystems, Inc. ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Sun. */ Le contenu des fichiers • On donne ensuite les informations relatives aux packages : package puis import • package tps.tp1; • import java.awt.peer.CanvasPeer; • import java.net.* Place le fichier dans un package donné : package Importe les classes situées dans d’autres packages (.class) Le contenu des fichiers • Vient ensuite le commentaire de classe • • • • • • /** * Class description goes here. * * @version 1.82 18 Mar 1999 * @author Firstname Lastname */ • Explique l’utilité de la classe, en donne la version, l’auteur. • Différent du commentaire de fichier, utilité logique de la classe Le contenu des fichiers On déclare ensuite la classe ou l'interface • • • public class Blah extends SomeClass { /* A class implementation comment can go here. */ Peut être suivi d’un commentaire d’implémentation de classe (ne concerne que l’implémentation et donc le/les programmeur de cette classe, inutile dans la description générale) Commentaire d’implémentation non extrait, une seule * Le contenu des fichiers Ensuite, dans l’ordre, le contenu de la classe : • Attributs de classe (statiques), dans l’ordre public / protected / private • Attributs d’instance, dans l’ordre public / protected / private • Les constructeurs • Les méthodes (groupage logique, pas par portée ou visibilité) Indentation Indentation : espaces ou tabulations en début de ligne L’indentation est primordiale. Elle permet de saisir au premier coup d’œil la structure du code. Les lignes entre { } définissent un bloc. Les blocs peuvent être soumis à des structures de contrôle (if, while…), ce qui crée une hiérarchie. Chaque fois qu’un bloc est soumis à une structure de contrôle, toutes ses lignes sont indentées un cran plus loin que cette dernière. On indente à une tabulation = 4 espaces. (tab ou espaces, au choix) Exemple int a = 10; //Un énoncé seul, if(a>10) { //structure de contrôle et ouverture d’un bloc a = a+1; //Indentation une tab if(a>20) { //structure de contrôle et ouverture bloc a = a+3; //Indentation deux tabs a = a/2; //Indentation deux tabs a = a-1; //Indentation deux tabs }//Fin du second bloc }//Fin du premier bloc Eclipse ctrl + shift + F pour auto-format sous Eclipse Indentation On limite la taille des lignes de code à environ 80 cars Si du code dépasse, on coupe : • Après une virgule • Après un opérateur • Au plus haut niveau possible • On s’aligne avec le début de l’expression, ligne précédente • Si c’est illisible -> 8 espaces d’indentation (pour ne pas cacher les blocs du dessous à 4 espaces) Indentation On préfère le premier, on coupe au plus haut niveau Indentation C’est avant tout une question de lisibilité… Indentation Sur les structure de contrôle, décaler de 8 espaces, pour ne pas cacher le bloc contrôlé. Indentation Pour un operateur ternaire Commentaires Commentaires d’implémentation : /*...*/, et // Ces commentaires expliquent des choix d’implémentation Commentaires de documentation: /**...*/ Ils sont extraits par javadoc Ces commentaires donnent les spécifications d’une partie de code, les détails liés à l’utilisation et non pas les détails liés à l’implémentation. Commentaires en bloc /* * Here is a block comment. * * Explaining some code. */ Toujours précédé d’une ligne blanche Commentaires sur une ligne /* Exemple de commentaire sur une ligne */ // Exemple de commentaire sur une ligne Toujours précédé d’une ligne blanche Indenté au meme niveau que le code qui le suit Permet de décrire rapidement la suite du code Commentaires de fin de ligne Commentaire très court qui explique une ligne Commenter une portion de code Préférer // à /* */ : - un commentaire déjà présent peut stopper le bloc de commentaire - les éditeurs ont des raccourcis pour les ajouter automatiquement en début de ligne. Sur Eclipse : raccourci Ctrl+Shift+/ ou Ctrl + Shift + C Déclarations Une seule déclaration par ligne. Plus grande clarté, et encourage les commentaires Initialiser sur la même ligne (sauf ssi calcul nécessaire) int nbForces = 0; //Le nombre de forces a ajouter float speed = 0.0f; //La vitesse du train Si c’est possible, aligner les déclarations et leur commentaires. Déclarations Placer les déclarations au début des blocs (portée de la variable) Ne pas attendre l’utilisation de la variable pour la déclarer. Autorisé uniquement pour les boucles for : for (int i=0;i<10:i++) { … } Déclarations Ne pas masquer une déclaration de plus haut niveau, c’est valide mais dangereux. Déclarations ‘(‘ juste après le nom de la méthode ‘{‘ sur la même ligne que le nom de méthode ‘}’ indenté comme la méthode (comme pour tout bloc) { } autorisé pour bloc null Méthodes séparées par une ligne blanche Enoncés Un énoncé se termine par ‘;’ Un seul énoncé par ligne : argv++; argc--; //Interdit !!! Si plusieurs énoncés forment un bloc, indenter en conséquence. Toujours utiliser la notation en bloc { } pour les structures de contrôle (évite les erreurs quand ajout d’une ligne) Le if Le do while Lignes vides • On place une ligne vide : – Entre deux méthodes – Entre les variables locales et le premier énoncé dans une méthode – Avant un commentaire en bloc ou sur une ligne – Pour séparer des sections logiques dans le code d’un méthode. Espaces • On place un espace : – Après les mots clefs qui utilisent des parenthèses (while par exemple) • Attention, pas après les noms de méthodes (on colle la parenthèse) • Permet justement de distinguer les mot clefs des méthodes – Après les virgules dans les listes d’arguments – Autour des tous les opérateurs binaires sauf ‘point’ – Jamais autour des opérateurs unaires comme négatif, positif, incrémentation, décrémentation – Autour des accolades { et } – Après un cast e.g. int b = (int) a; Conventions de nommage • Sun propose des convention pour le nommage des variables, classes, packages, etc… • Quel type de noms choisir et comment les écrire • Toujours pour rendre le code plus lisible Nommage des classes • • • • • Ne pas abréger si possible Une capitale à chaque début de mot Tous le mots collés Pas de caractères accentués Ex – Raster, Etudiant, ListeChainee, MaClasseAMoi • Pareil pour interfaces ou classes abstraites. Nommage des méthodes • Des verbes • Toujours éviter les abbréviations • Première lettre en minuscule • Ex : – run(), runFaster(), runVeryVeryFast() Nommage des variables • Des noms courts, signifiants, commencent par une minuscule puis capitale a chaque mot • Entiers i,j,k… • Caractères : c,d,e… • Ex : – int i; – float myWidth; Nommage des constantes • Mot clef final (qui ne changera plus) • Toutes en capitales • Séparées par un ‘_’ final int MAX_WIDTH = 100; • Utiliser des constantes (pas de +10 ou -12.5f au milieu du code) – À part pour les +1 et -1 des incrémentations et décrémentations et 0. Nommage des packages • Tout en minuscule • [a-z], [0-9] – com.sun.eng – com.apple.quicktime.v2 – edu.cmu.cs.bovik.cheese Variables d’instances et public • On déclare en général les variables d’instantes privées, et on utilise des accesseurs private int nbClients = 0; public int getNbClients() { return nbClients; } public void setNbClients(int nbClients) { this.nbClients = nbClients; } • Sauf si la classe peut être vue comme un simple structure de données (pas de méthodes) • Sous Eclipse : A+Shift+S puis Generate Getters and Setters Assignements • Ne pas faire d’assignement au milieu d’une expression : – a = b + (c = i + j); • Ecrire plutôt – c = i + j; – a = b + c; Operateurs et parenthèses • Ne pas s’appuyer sur la précédence et l'associativité des opérateurs : – x = x * 5 + 2 / 8 > 0 && a++ != 1; //Illisible – x = ((x * 5) + (2 / 8) > 0) && ((a++) != 1) //Mieux • Toujours mettre des parenthèses, même si ca parait évident : – if ((a > 1) && (b < 0)) Documenter son code avec Javadoc Reference disponible sur http://docs.oracle.com/javase/7/d ocs/technotes/tools/windows/java doc.html#referenceguide Commentaires de documentation • Comme vu précédemment, on utilise les commentaires de documentations /** */ • Il sont extrait par le programme javadoc qui va les utiliser pour générer une documentation HTML • Commentaires pout l’utilisateur, pas pour le programmeur de la classe (pour ca on utilise /* */, qui n’est pas extrait du code Commentaires de documentation • Permet de documenter une classe (interface) ou un de ses membres • La documentation se met toujours avant l’objet documenté dans le fichier. Commentaires de documentation • La première phrase du commentaire est utilisée comme résumé du commentaire total • Toutes les phrase du début du commentaire jusqu’au premier @ sont la section de description principale • On peut y adjoindre du code HTML • Ensuite chaque ligne @ permet de définir un tag particulier Commentaires de documentation • Il existe plusieurs tags : @authorv {@code} {@docRoot} @deprecated @exception etc... • Les tags entre { } peuvent être placés au milieu de la description principale • Tags les plus courants : – – – – – @author : l’auteur @param : paramètre en entrée d’une méthode @return : ce que retourne la méthode @see : renvoie à une autre partie de la doc @version : numero de version Ex : commentaire de classe /** * A class representing a window on the screen. * For example: * <pre> * Window win = new Window(parent); * win.show(); * </pre> * * @author Sami Shaio * @version 1.15, 13 Dec 2006 * @see java.awt.BaseWindow * @see java.awt.Button */ class Window extends BaseWindow { ... } <pre> permet de garder le formatage intact Ex : commentaire de variable d’instance /** * The X-coordinate of the component. * * @see #getLocation() */ int x = 1263732; Ex : commentaire de methode /** * Returns the character at the specified index. An index * ranges from <code>0</code> to <code>length() - 1</code>. * * @param index the index of the desired character. * @return the desired character. * @exception StringIndexOutOfRangeException * if the index is not in the range <code>0</code> * to <code>length()-1</code>. * @see java.lang.Character#charValue() */ public char charAt(int index) { ... } Comment compiler la documentation • On utilise l’utilitaire javadoc.exe présent dans le répertoire bin du JDK Tester son code Junit 4 JUnit • Principe du développement dirigé par les tests. • On réfléchit à l’objet en pensant à ce que son comportement devra toujours vérifier pour être correct. • On met en scène ces cas dans des méthodes de tests. • On peut ensuite appeler ces méthodes automatiquement, pour vérifier que l’objet les respecte toujours. JUnit • Permet de faire très rapidement des tests de non régression. • Une modification dans le code peut avoir des effets inattendus sur le reste du programme • Grace à JUnit : – Vous aviez codé des classe – Vous aviez codé leurs tests – Vous modifiez une seule classe : il est très simple de relancer tous les tests pour tester automatiquement l’ensemble du programme JUnit • Permet de tester le comportement des objets. • Pour chaque classe du programme, on définit une classe de test JUnit (bonne pratique) • Quand on lancera une séquence de test : – Les classes test que l’on a définies seront instanciées – Leur méthodes seront appelées et nos tests effectués – Toutes les erreurs nous seront signalées. Assertions • JUnit utilise des assertions – – – – – assertTrue(...), assertFalse(...) assertEquals(Object, Object) assertSame(Object, Object) assertNull(...) assertEquals(double expected, double actual, double delta) • Exception AssertionFailedError levée si l’assertion est fausse. Une classe de test JUnit 4 • On crée une classe comme les autres, pour qui la classe testée est visible (même package par exemple) • Annotation @Test pour les méthodes de test • Annotation @Before pour une méthode qui sera toujours effectuée avant chaque test de cette classe. • Annotation @After pour une méthode qui sera effectuée après chaque test de cette classe • @Before et @After permettent de créer des Fixtures : fixer l’état dans lequel s’effectue chaque test. Exemple classe de test JUnit 4 public class Calc { public long add(int a, int b) { return a+b; } } import org.junit.Test; import static org.junit.Assert.assertEquals; public class CalcTest { @Test public void testAdd() { assertEquals(5, new Calc().add(2, 3)); } } Lancer un test JUnit 4 • On exécute cette classe au travers de JUnit (Eclipse sait par exemple les lancer) JUnit 4 : Fixture au niveau class • @BeforeClass public static void runBeforeClass() { // run for one time before all test cases } • @AfterClass public static void runAfterClass() { // run for one time after all test cases } JUnit 4 : Gestion des exceptions • @Test(expected = ArithmeticException.class) public void divisionWithException() { // divide by zero simpleMath.divide(1, 0); } • On peut aussi utiliser un try catch et un fail • public void divisionWithException() { try { simpleMath.divide(1, 0); fail("On aurait du avoir une ArithmeticException"); } catch (ArithmeticException e) { //Le test est ok ! } } JUnit 4 : Gestion des boucles infinies • @Test(timeout = 1000) public void retourneJamais() { for (int i=0;i<10;) { } }