Informatique / Programmation
Transcription
Informatique / Programmation
Java / Introduction Caractéristiques principales Informatique / Programmation § Orientation Objet § (C++) -- Complément § Sécurité intégrée Programmation orientée objet avec Java 01 : Introduction / Éléments de base § Portabilité § Robustesse (typage fort, gestion de la mémoire, ...) § Richesse des librairies de classes (plate-forme Java) § Multitâche intégré au langage (Multithreading) § Bonne intégration des communications réseau (Sockets, RMI, …) Jacques Bapst § Évolutivité (SDK 1.0, 1.1, 1.2, 1.3, 1.4, 5.0, 6.0, 7.0, 8.0, …) [email protected] § Java ¹ JavaScript § C# est fortement inspiré de Java EIA-FR / Jacques Bapst Java / Introduction Mode de déploiement [1] § Application § Le langage Java a été conçu au sein de l'entreprise Sun Microsystems (par une équipe dirigée par James Gosling). • Java est synonyme de "café" en argot américain Complément Complément § Initialement baptisé Oak (chêne), il a été officiellement lancé en 1995 sous le nom Java § Le langage a été popularisé par : Sa portabilité (indépendance des plates-formes) Le slogan "Write Once Run Anywhere" Une syntaxe de base très proche de C/C++ Une orientation Web native Des éléments de sécurité intégrés La gratuité de son kit de développement (JDK ) Son adoption dans la formation (écoles, universités) Son nom ? EIA-FR / Jacques Bapst 3 Java / Introduction Bref historique du langage Java • • • • • • • • PR1_01 PR1_01 2 • • • • Se comporte comme une application native Lancement par une ligne de commande (lanceur natif) Nécessite la disponibilité d'une machine virtuelle Java (JVM, JRE) Peut accéder (par défaut) aux ressources locales (fichiers, réseau, périphériques, ...) § Applet • Lancement intégré dans des pages HTML • S'exécute à l'intérieur du navigateur (IE, Firefox, ...) • Utilise la machine virtuelle Java du navigateur (attention aux versions de la machine virtuelle ) • Ne peut pas accéder (par défaut) aux ressources locales (notion de Sandbox) Þ Sécurité • Pratiquement plus utilisé (remplacé par Flash, HTML5, …) EIA-FR / Jacques Bapst PR1_01 4 Java / Introduction Java / Introduction Premier programme Java Modes de déploiement [2] § Applet § Un premier programme (très poli) enregistré dans le fichier "Bonjour.java" : Navigateur Complément Applet Nom de la classe : il doit correspondre au nom du fichier source (sans l'extension) JVM Page HTML //------------------------------------------// Un premier programme Java //------------------------------------------- Machine cible public class Bonjour { § Java Web Start public static void main(String[] args) { • Application déployée et mise à jour par le Web • Mise en mémoire cache locale (lancement plus rapide) • L'application peut être lancée depuis une page HTML ou comme une application locale • Technologie destinée à remplacer les Applets (mais assez peu utilisée) EIA-FR / Jacques Bapst PR1_01 System.out.println("Bonjour à tous !"); } Point d'entrée du programme } 5 EIA-FR / Jacques Bapst Différences par rapport à C/C++ Complément Compilation et Interprétation Syntaxe simplifiée (élimination des redondances C/C++) Tailles définies pour les types primitifs Garbage Collector (ramasse-miettes) Éléments supprimés : • • • • • • • • • Interprétation Compilation Java VM Linux Pré processeur / #define / #include / typedef Fichiers header (.h) Structures / Unions Fonctions et variables globales Héritage multiple Pointeurs Surcharge des opérateurs Instruction goto Certains transtypages (casting) automatiques EIA-FR / Jacques Bapst 7 Java / Introduction Java / Introduction § § § § PR1_01 Source Compilateur Bytecode Interpréteur Java VM Windows P1.java javac P1.java P1.class java P1 Java VM ... On utilise un éditeur pour créer et modifier les fichiers sources PR1_01 6 EIA-FR / Jacques Bapst Dans un environnement de développement intégré (IDE) les commandes sont disponibles sous forme de menus et barres d'outils Machines virtuelles (JVM/JRE) PR1_01 8 Java / Eléments de base Java / Eléments de base Commentaires Identificateurs § Les identificateurs sont des noms symboliques permettant de référencer les éléments des programmes Java (variables, fonctions, ...). § Trois formes de commentaires : // …Texte… § Règles pour les identificateurs : ü Commence dès // et se termine à la fin de la ligne ü Sur une seule ligne ü A utiliser de préférence pour les commentaires généraux • Doivent commencer par une lettre ou un souligné (ou, à éviter, un caractère monétaire) • Suivi (éventuellement) d'un nombre quelconque de lettres, chiffres ou soulignés (ou, à éviter, de caractères monétaires ) • Distinction entre les majuscules et les minuscules /* …Texte… */ ü Le texte entre /* et */ est ignoré par le compilateur ü Peuvent s'étendre sur plusieurs lignes ü Ne peuvent pas être imbriqués ü Peuvent être utiles pour inactiver (temporairement) une zone de code (éviter de créer des identificateurs qui ne se distinguent que par la casse) • Utilisent le jeu de caractères Unicode (16 bits) • Selon les plates-formes, les caractères alphabétiques spéciaux (par exemple les caractères accentués, les lettres grecques, … ) peuvent encore poser des problèmes => A éviter • Les mots réservés du langage sont exclus (voir liste à la page suivante) /** …Texte… */ ü Commentaire de documentation (comme /* … */ mais avec fonction spéciale) ü Interprétés par l'utilitaire javadoc pour créer une documentation au format HTML ü Peuvent contenir des balises de documentation (@author, @param, …) ü Peuvent contenir des balises HTML (Tags) EIA-FR / Jacques Bapst PR1_01 9 EIA-FR / Jacques Bapst 11 PR1_01 12 Java / Eléments de base Java / Eléments de base Mots réservés (Keywords) Exemples de commentaires § Mots réservés du langage Java (mots-clé) : /** * Somme : Calcule une somme * * @author Marc Duchemin * @version 2.3 08.07.2014 */ public class Somme { /*--------------------------------------------------+ | Programme principal | +--------------------------------------------------*/ public static void main(String[] params) { abstract default goto do boolean synchronized if private this double implements protected throw break else import public throws byte enum instanceof return transient case extends int short true catch false interface static try char final long strictfp void class finally native super volatile float new switch while for null const total = a + b; 3) 1) continue //--- Affichage du résultat de l'addition System.out.println("Résultat = " + total); 1) package assert //--- Initialisation des variables int a = 1; int b = 2; int total; // Totalisateur 4) 2) 2) 2) 1) Mots réservés du langage mais non utilisés actuellement Littéraux prédéfinis 3) Depuis la version 1.4 du JDK 4) Depuis la version 1.5 du JDK 2) } } EIA-FR / Jacques Bapst PR1_01 PR1_01 10 EIA-FR / Jacques Bapst Java / Eléments de base Java / Eléments de base Représentation en mémoire Notion de variable [1] § En informatique, la notion de variable est une notion fondamentale (un concept essentiel). § De manière interne, la zone mémoire allouée à une variable est identifiée par une adresse. § Une variable définit une case mémoire nommée et typée. § Cette adresse est complètement cachée au programmeur : l'accès à la zone mémoire s'effectue en utilisant le nom de la variable. • Le nom est représenté par un identificateur • Le type définit le genre d'informations qui sera enregistré ainsi que les opérations qui pourront être effectuées sur ces informations • Il existe un certain nombre de types prédéfinis mais il est également possible de créer ses propres types Code Java Variable Adresse Contenu note EIA-FR / Jacques Bapst (à utiliser) note 1216 1217 Type Identificateur ; float note; Polygone triangle; Notation abstraite 1215 . . . float note; . . . § Avant de pouvoir être utilisée, une variable doit préalablement être déclarée (définition de l'identificateur et du type). § Syntaxe : Représentation interne Variable Adresse Contenu note PR1_01 1215 . . . note = 5.2; . . . 4.75 13 Java / Eléments de base note 1216 note 5.2 5.2 1217 EIA-FR / Jacques Bapst PR1_01 Java / Eléments de base Notion de variable [2] Types primitifs [1] § La valeur de la variable peut être définie (initialisée), consultée, modifiée en utilisant le nom de la variable. § Les variables ont une visibilité et une durée de vie qui dépendent du contexte dans lequel elles sont déclarées. § Le type d'une variable peut être soit un type primitif, soit un type référence (c'est-à-dire le nom d'une classe ou d'un tableau) § Exemples : Type Contient Taille Valeurs boolean Booléen [1 bit 1) ] true, false char Caractère (entier non-signé) 16 bits \u0000..\uFFFF byte Entier signé 8 bits -128..127 short Entier signé 16 bits -215.. 215-1 int unNombreEntier; // Type primitif int int Entier signé 32 bits -231.. 231-1 String motDePasse; // Classe String (prédéfinie) Entier signé 64 bits -263.. 263-1 Point position; // Classe Point long char[] voyelles; // Tableau de caractères float Nombre en virgule flottante 32 bits ±3.4*1038 // Tableau de Point(s) double Nombre en virgule flottante 64 bits ±1.7*10308 Point[] nuage; 1) EIA-FR / Jacques Bapst 15 PR1_01 14 EIA-FR / Jacques Bapst Dépend de l'implémentation de la machine virtuelle PR1_01 16 Java / Eléments de base Java / Eléments de base Types primitifs [2] Types primitifs [4] § Booléen (boolean) § Entiers signés (byte, short, int, long) • Ne peuvent prendre que deux valeurs : Vrai ou Faux • Ne peuvent pas être interprétés comme des valeurs numériques [0, 1] • Valeurs littérales (valeurs qui figurent directement dans le code ) ü true ü false • Valeurs littérales (int par défaut) : notation habituelle • Suffixe l ou L pour type long (L est préférable) Vrai Faux (base 8) • Préfixe 0b ou 0B pour valeur binaire (base 2) • Exemples : 0 123 -56 0377 433L 0b1011 0xff 0xA0B3L Caractères Unicode (codage normalisé sur 16 bits) Site de référence de la norme : www.unicode.org Peuvent être traités comme des entiers (non-signés !) Valeurs littérales ü Entre apostrophes : 'A' (attention : "A" n'est pas de type char) ü Séquence d'échappement pour certains caractères spéciaux (voir tabelle page suivante) EIA-FR / Jacques Bapst PR1_01 17 Java / Eléments de base Signification \b Retour en arrière (Backspace) \t Tabulateur horizontal \n Saut de ligne (Line-feed) \f Saut de page (Form-feed) \r Retour de chariot (Carriage-Return) \" Guillemet \' Apostrophe \\ Barre oblique arrière (Backslash) \xxx Caractère Latin-1 (xxx : valeur octale 000..377) \uxxxx Caractère Unicode (xxxx : valeur hexadécimale 0000..FFFF) valeur de type int valeur de type int valeur de type int 377 [octal] = 255 [décimal] valeur de type long valeur binaire de type int valeur hexadécimale de type int valeur hexadécimale de type long PR1_01 19 Java / Eléments de base Types primitifs [5] § Nombres en virgule flottante (float, double) § Séquences d'échappement (char) Code // // // // // // // // EIA-FR / Jacques Bapst Types primitifs [3] EIA-FR / Jacques Bapst Java 7 • Préfixe 0x ou 0X pour valeur hexadécimale (base 16) § Caractère (char) • • • • • Préfixe 0 (zéro) pour valeur octale • Norme IEEE 754-1985 • Précision ü float ü double : env. 6 chiffres significatifs : env. 15 chiffres significatifs • Valeurs littérales (double par défaut) : notation habituelle (n.m) • Suffixe f ou F pour type float • Suffixe d ou D pour type double (rarement nécessaire ) • Notation exponentielle avec e ou E suivi de l'exposant • Valeurs spéciales : infini, -infini, zéro négatif, NaN (Not a Number) Attention : PR1_01 18 EIA-FR / Jacques Bapst Les nombres en virgule flottante sont des approximations de nombres réels (tous les nombres réels ne peuvent pas être représentés de manière exacte). Il faut en tenir compte dans les comparaisons et notamment dans les tests d'égalité (prendre en compte un "epsilon"). PR1_01 20 Java / Eléments de base Java / Eléments de base Types primitifs [6] Conversions de types [1] § Mis à part le type booléen, tous les types primitifs peuvent être convertis entre eux. § Nombres en virgule flottante (float, double) 123.45 17d -4.032F 6.02e23 -5.076E-2f // // // // // valeur valeur valeur 6.02 -5.076 de type double (par défaut) de type double de type float x 1023 de type double x 10-2 de type float § L'encodage Unicode permet (si nécessaire) de considérer les caractères comme des nombres. Le type char peut donc agir comme un entier short mais il est non signé (contrairement aux autres entiers). § Le caractère '_' peut être inséré dans les littéraux numériques long int int float creditCardNr tenMillions bytePattern pi = = = = Java 7 1234_5678_9012_3456L; 10_000_000; 0b11000011_00001111_11110000_00110011; 3.14_15_93f; // Infini // Moins l'infini // Not a Number (NaN) (avec perte éventuelle de précision lors du passage en virgule flottante) § Conversion restrictive explicite (par transtypage / casting) (sous la responsabilité du programmeur) Remarque : Certaines conversions restrictives implicites sont automatiquement effectuées lorsque le résultat d'une expression constante est assignée à une variable (byte, short ou char) pour autant que la valeur de l'expression puisse être représentée (sans perte) par la variable. § Cas particuliers (problèmes numériques) : 1.2/0.0 -5.1/0.0 0.0/0.0 § Conversion élargissante / Promotion (automatique) Double.POSITIVE_INFINITY Double.NEGATIVE_INFINITY short compteur = 12*34; Double.NaN EIA-FR / Jacques Bapst PR1_01 21 EIA-FR / Jacques Bapst Affichage sur la console § Conversions des types primitifs Conversion vers // Affichage (reste sur la même ligne) // Affichage et retour à la ligne § Exemples : Résultats --------- Conversion de int size = 123; char unit = 'm'; System.out.print("Longueur : "); System.out.print(size); System.out.print(" "); System.out.println(unit); Longueur : 123 m Même affichage mais en utilisant l'opérateur de concaténation (+) int size = 123; char unit = 'm'; System.out.println("Longueur : " + size + " " + unit); EIA-FR / Jacques Bapst 23 Conversions de types [2] § Pour afficher une valeur littérale ou le contenu d’une variable sur la console de sortie, on utilise les lignes de code suivantes : System.out.println("Résultats"); System.out.println("---------"); PR1_01 Java / Eléments de base Java / Eléments de base • System.out.print(…); • System.out.println(…); // Conversion implicite int -> short PR1_01 22 boolean byte short char int long float double boolean – N N N N N N N byte N – E R E E E E short N R – R E E E E char N R R – E E E E int N R R R – E E* E long N R R R R – E* E* float N R! R! R! R! R! – E double N R! R! R! R! R! R* – N : pas de conversion possible R : conversion restrictive E : conversion élargissante (promotion) * : perte de précision EIA-FR / Jacques Bapst ! : valeur tronquée PR1_01 24 Java / Eléments de base Java / Eléments de base Transtypage (Casting) Chaînes de caractères § Les chaînes de caractères ne font pas partie des types primitifs § Conversion de type explicite (au risque du programmeur) § Les chaînes de caractères font partie des types référence (ce sont des objets de la classe String) § Syntaxe : (Type_de_destination) Valeur_à_convertir § D'usage courant Þ Syntaxe particulière (simplifiée) pour les valeurs littérales : • Texte entre guillemets ("…") § Exemples : int i = 17; byte b = 4; b = i; b = (byte)i; • Peuvent contenir des séquences d'échappement identiques à celles définies pour les types char • Ne peuvent pas s'étendre sur plusieurs lignes // Conversion implicite : littéral int -> byte // Erreur à la compilation // Conversion explicite (int -> byte) • Concaténation (mise bout à bout) avec l'opérateur "+" • Conversions possibles : types primitifs ó String float f = 23.86f; i = (int)f; // Valeur tronquée (i==23) "Ceci est une chaîne de caractères" "Le caractère \" se nomme 'guillemet'" "La conca" + "ténation peut s'étendre " + "sur plusieurs lignes" EIA-FR / Jacques Bapst PR1_01 25 Java / Eléments de base EIA-FR / Jacques Bapst PR1_01 Java / Eléments de base Conversions types primitifs ó String Types "Référence" § Il est possible de convertir des valeurs de types primitifs en chaînes de caractères (String) et inversement en utilisant des fonctions (méthodes) disponibles dans des classes appelées Wrappers. § En plus des huit types primitifs, Java définit deux autres catégories de types de données : - les classes et - les tableaux Conversions qui seront étudiés en détail plus tard. en String de String Type primitif Wrapper § Les classes et les tableaux sont des types généralement composites (représentant des valeurs multiples). byte Byte toString(byte b) parseByte(String s) short Short toString(short s) parseShort(String s) § Ils sont collectivement nommés : Types "Référence" (on parle également parfois de types non-primitifs). int Integer toString(int i) parseInt(String s) long Long toString(long l) parseLong(String s) § Pour une variable de type référence, la case mémoire ne contient pas directement les valeurs de la variable mais une référence (une sorte de pointeur) vers une autre zone mémoire contenant les valeurs. float Float toString(float f) parseFloat(String s) double Double toString(double d) parseDouble(String s) String nom = "Dupont"; EIA-FR / Jacques Bapst 27 nom D'autres fonctions de conversion sont également disponibles dans la classe String, notamment la méthode valueOf() qui permet de convertir en String les valeurs de tous les types primitifs. "Dupont" PR1_01 26 EIA-FR / Jacques Bapst PR1_01 28 Java / Eléments de base Exemples de conversions int float double String i; f; d; s; // // // // Variable Variable Variable Variable de de de de type type type type int float double String s = "412"; i = Integer.parseInt(s); // Conversion String -> int s = "1.234"; d = Double.parseDouble(s); // Conversion String -> double f = 123.8F; s = Float.toString(f); s = String.valueOf(f); // Conversion float -> String // Conversion float -> String Attention : Ne pas confondre les littéraux de type caractère ou chaîne de caractères avec les littéraux numériques. Par exemple '4', "4" et 4. Le premier est un caractère (type char), le deuxième une chaîne de caractères (type String) et le troisième est une valeur numérique entière (type int). EIA-FR / Jacques Bapst PR1_01 29 Java / Expressions et opérateurs Expressions [2] Informatique / Programmation § Les expressions complexes sont composées d'expressions primaires (opérandes) et d'opérateurs (y c. invocation de méthodes) § Exemples : total = 141 1 + 2 total = 7 + b r = p.min(a, b) Programmation orientée objet avec Java 02 : Expressions et opérateurs Expression Expression Expression Expression d'affectation avec opérateur d'addition comprenant une addition et une affectation comprenant une invocation de méthode § La valeur et le type d'une expression complexe sont déterminés par les valeurs et types des opérandes ainsi que par les opérateurs qui interviennent dans l'expression (les règles seront vues plus tard). § Certains opérateurs ont des effets de bord : ils changent par ex. la valeur des opérandes et donc modifient l'état du programme (affectation, incrémentation, décrémentation, invocation de méthodes, création d'objets) Jacques Bapst [email protected] a = 2 i++ EIA-FR / Jacques Bapst Java / Expressions et opérateurs PR1_02 Java / Expressions et opérateurs Opérateurs Expressions [1] § Les expressions sont des entités composées de littéraux, de variables, d'invocations de méthodes et d'opérateurs. Ex : 4 + a / f(x) § Les expressions sont des éléments syntaxiques permettant de déterminer une valeur à partir d'autres valeurs § Opérateurs unaires (monadiques) -b a++ § Les expressions primaires : littéraux ou variables a * b y = z (int)y § Exemples : PR1_02 Résultat 1 opérande 2 opérandes L'opérateur de multiplication est binaire L'affectation est également binaire Transtypage (casting) § Opérateurs ternaires Littéral entier (type int, valeur 141) Littéral booléen (type boolean, valeur true) Variable (type selon déclaration de variable, valeur actuelle de total) Op Opérateur monadique moins Opérateur monadique de post-incrémentation § Opérateurs binaires (dyadiques) § La valeur résultante de l'évaluation d'une expression primaire est la valeur littérale ou la valeur enregistrée dans la variable. Le type résultant est celui de la valeur littérale ou de la variable EIA-FR / Jacques Bapst § Les opérateurs sont des éléments syntaxiques qui effectuent certaines opérations en utilisant les valeurs de leurs opérandes (paramètres de l'opération). Opérandes § L'évaluation d'une expression détermine une valeur et un type de retour 141 true total 3 3 opérandes • Un seul opérateur ternaire en Java x > y ? x : y 2 EIA-FR / Jacques Bapst Retourne la valeur maximale entre x et y PR1_02 4 Java / Expressions et opérateurs Java / Expressions et opérateurs Précédence et Associativité Liste des opérateurs [1] P A Opérateur 15 G . [] ( params ) ++, -14 D ++, -+, ~ ! 13 D new ( type ) 12 G *, /, % 11 G +, + 10 G <<, >>, >>> 9 G <, <= >, >= instanceof 8 G == != == != P : Précédence Type(s) des opérandes objet, membre tableau, int méthode, liste de paramètres variable variable nombre entier booléen classe, liste de paramètres type, quelconque nombre, nombre nombre, nombre chaîne de caract., quelconque entier, entier nombre, nombre nombre, nombre référence, type primitif, primitif primitif, primitif référence, référence référence, référence Opération accès au membre d'un objet accès à l'élément d'un tableau invocation (appel) de méthode post-incrémentation, décrémentation pré-incrémentation, décrémentation plus monadique, moins monadique complément binaire NON logique création (instanciation) d'objet transtypage (conversion de type) multiplication, division, modulo addition, soustraction concaténation décalage à gauche, droite, non-signé inférieur, inférieur ou égal supérieur, supérieur ou égal comparaision de types égalité (valeurs égales) non-égalité égalité (référence au même objet) non-égalité § Précédence (voir colonne P dans la liste des opérateurs) • Définit, en l'absence de parenthèses, quelles opérandes sont liés à quels opérateurs • Plus le degré de précédence est élevé et plus les opérandes sont liés à l'opérateur considéré • Les parenthèses permettent de spécifier explicitement les liens entre les opérandes et les opérateurs • Conseil : Sauf cas triviaux, toujours mettre des parenthèses dans les expressions complexes afin d'éviter toute ambiguïté d'interprétation § Associativité • S'il y a plusieurs opérateurs de même précédence, l'associativité définit l'ordre dans lequel les opérations seront exécutées ü De la gauche vers la droite (G) ü De la droite vers la gauche (D) A : Associativité EIA-FR / Jacques Bapst PR1_02 5 Java / Expressions et opérateurs EIA-FR / Jacques Bapst P A Opérateur 7 G & & 6 G ^ ^ 5 G | | 4 G && 3 G || 2 D ? : 1 D = *=, /=, %=, +=, -=, <<=, >>=, >>>=, &=, ^=, |= Type(s) des opérandes entier, entier booléen, booléen entier, entier booléen, booléen entier, entier booléen, booléen booléen, booléen booléen, booléen booléen, quelconque, quelconque variable, quelconque variable, quelconque § Effectuent des opérations arithmétiques et retournent des résultats numériques Opération ET binaire (bit à bit) ET logique OU exclusif binaire (bit à bit) OU exclusif logique OU binaire (bit à bit) OU logique ET logique conditionnel 1) OU logique conditionnel 1) opérateur conditionnel (ternaire) affectation affectation avec opération § Le type du résultat dépend du type des opérandes § Si nécessaire, une conversion élargissante (automatique) des opérandes est effectuée avant l'opération + - plus, moins monadique + - addition, soustraction ++ -- pré/post incrémentation, pré/post décrémentation * / % multiplication, division, modulo (reste de la division entière) -2.7 i++ --j - 2 a * x*x + 4.2 5.6/3 L'opérande de droite n'est évaluée que si c'est nécessaire 17 % 5 EIA-FR / Jacques Bapst 7 Opérateurs arithmétiques A : Associativité 1) PR1_02 Java / Expressions et opérateurs Liste des opérateurs [2] P : Précédence (voir colonne A dans la liste des opérateurs) PR1_02 6 ( ne pas confondre avec 17 / 5 ) EIA-FR / Jacques Bapst PR1_02 8 Java / Expressions et opérateurs Java / Expressions et opérateurs Ordre d'évaluation Évaluation des opérations [1] § Pour mettre en évidence l'ordre d'évaluation des opérations d'une expression, on peut placer des parenthèses en fonction de la précédence et de l'associativité des opérateurs impliqués : § Si l'un des opérandes est un nombre en virgule flottante, l'arithmétique décimale (arithmétique réelle) est utilisée (calcul en virgule flottante) 1 + 3 * 4 - 2 - 5 Précédence 11 G 12 G 11 G 11 G § Si les deux opérandes sont des nombres entiers, l'arithmétique entière est utilisée (important pour les divisions et pour le domaine des valeurs possibles) Associativité ( ( 1 + ( 3 * 4 ) ) - 2 ) - 5 § Les caractères (char) sont traités comme des entiers short (leur valeur Unicode est utilisée) v = a - i++ 1 D 11 G 14 D v = ( a - ( i++ ) ) § Après placement des parenthèses, les opérateurs ne devraient avoir comme opérandes que des expressions entre parenthèses ou éventuellement un symbole ou une valeur littérale unique (isolé). § Analyse non terminée : u = (a * b) - (c * d) + f(e); EIA-FR / Jacques Bapst 9 PR1_02 § L'arithmétique entière est circulaire et ne produit jamais de dépassement (une division par zéro lève cependant l'exception ArithmeticException) § L'arithmétique décimale (en virgule flottante) peut produire des valeurs spéciales (infini, -infini, zéro négatif, NaN) mais ne lève jamais d'exceptions EIA-FR / Jacques Bapst Évaluation des opérations [2] Ordre d'évaluation / Ordre des opérations § Dans des expressions plus complexes (et un peu tordues) il est nécessaire de bien distinguer l'ordre d'évaluation des opérandes (qui s'effectue de gauche à droite) et l'ordre des opérations (qui dépend de la précédence et de l'associativité des opérateurs). Complément 11 Java / Expressions et opérateurs Java / Expressions et opérateurs § De telles expressions pièges sont à éviter (il faut impérativement écrire le code de manière moins ambiguë). int i = 10; int i=2; int i=1, j=2; i = i + (i=5); i = ++i + ++i * ++i; i = i+++j; § Le résultat d'une expression entière est de type int1) sauf si l'un des opérandes est de type long, auquel cas l'expression est de type long (voir diagramme page suivante) § Le résultat d'une expression réelle (comprenant au moins un opérande en virgule flottante) est de type float1) sauf si l'un des opérandes est de type double, auquel cas l'expression est de type double (voir diagramme page suivante) 1) par conversion élargissante automatique (promotion) des opérandes byte short float b = 12; s = 167; f = 1.2f; int i = s - b; double d = f * 2.5; EIA-FR / Jacques Bapst PR1_02 PR1_02 10 EIA-FR / Jacques Bapst // Expression entière, s et b sont convertis en int // avant l'opération // Expression réelle, f est converti en double avant // l'opération (car 2.5 est de type double) PR1_02 12 Java / Expressions et opérateurs Java / Expressions et opérateurs Évaluation des expressions arithmétiques Opérateurs et nombres en virgule flottante § Les nombres en virgule flottante (float et double) ne sont que des approximations des nombres réels car ils sont codifiés ( en base 2) avec un nombre limité de bits pour la mantisse et pour l'exposant (voir par ex. www.binaryconvert.com ou babbage.cs.qc.edu/IEEE-754). § Pour chaque opérateur arithmétique, d'éventuelles conversions préalables sont effectuées en fonction du type de ses opérandes : N N Nb à virg. ? Y Y long N Conversion év. et calcul en int Y double ? § Même à l'intérieur du domaine de valeurs prévu et avec un nombre de décimales inférieur à la précision maximale, une grande quantité de nombres réels ne peuvent pas être représentés exactement. ? Conversion év. et calcul en long Conversion év. et calcul en float Arithmétique entière Conversion év. et calcul en double § C'est pourquoi il faut être très prudent lors de l'utilisation des opérateurs relationnels avec les nombres en virgule flottante et tenir compte de ces problèmes d'arrondi. float a=1.0f, b=0.1f, c=0.2f; (a+(b+c)) != ((a+b)+c) // !!! true !!! (a+(b+c)) < ((a+b)+c) // !!! true !!! Arithmétique décimale Exemple : 17 / 3 + 1.0 EIA-FR / Jacques Bapst float d=2.0E7f; d == (d+1) PR1_02 13 Java / Expressions et opérateurs // !!! true !!! EIA-FR / Jacques Bapst PR1_02 15 Java / Expressions et opérateurs Opérateurs relationnels Opérateurs logiques § Effectuent des opérations de comparaison et retournent des résultats booléens § Effectuent des opérations logiques et retournent des résultats booléens == != égalité, inégalité ! NON logique < > plus petit, plus grand & ET logique <= >= plus petit ou égal, plus grand ou égal && ET logique conditionnel (le 2ème opérande n'est évalué que si le 1er est vrai) i < 5 if (age >= 18) {...} while (i<100) {...} if (objet1 != objet2) OU logique || OU logique conditionnel (le 2ème opérande n'est évalué que si le 1er est faux) {...} // Comparaison des références ^ PR1_02 OU exclusif logique if (!found) Attention : pour les types références (objets et tableaux) les opérateurs d'égalité et d'inégalité comparent les références et non les valeurs référencées. EIA-FR / Jacques Bapst | Ne pas confondre avec l'élévation à la puissance ! {...} if (size>1.4 || age>12) {...} if (t!=null && t.length>=2 && t[1]!=4) 14 EIA-FR / Jacques Bapst {...} PR1_02 16 Java / Expressions et opérateurs Java / Expressions et opérateurs Opérateurs orientés bits Affectation avec opérateur § Effectuent des opérations bit à bit et retournent des résultats entiers (signés) ~ Inversion (complément à 1) & ET (bit à bit) | OU (bit à bit) ^ OU exclusif (bit à bit) << Décalage à gauche (zéros ajoutés à droite) >> Décalage à droite signé (bit du signe ajouté à gauche) >>> Décalage à droite non-signé (zéros ajoutés à gauche) byte b = ~12 10 & 7 10 | 7 10 ^ 7 10 << 2 10 >> 2 -10 >> 2 -10 >>>2 // // // // // // // // § Combinaison de l'affectation avec un opérateur (arithmétique ou orienté bits) § Le type de l'opérande de gauche (variable) doit être compatible avec le type de l'expression de droite var op= expr Ne pas confondre avec l'élévation à la puissance ! ~00001100 00001010 & 00000111 00001010 | 00000111 00001010 ^ 00000111 00001010 00001010 11110110 11110110 => => => => => => => => 11110011 00000010 00001111 00001101 00101000 00000010 11111101 00111101 § Opérateurs combinés : (-13) ( 2) ( 15) ( 13) ( 40) ( 2) ( -3) ( 61) EIA-FR / Jacques Bapst PR1_02 var = var op ( expr ) += -= *= &= <<= |= >>= ^= >>>= /= i += 2; // i = i + 2; a *= z + 4; // a = a * (z + 4); flag |= mask; // flag = flag | mask; // set some bits according to a mask %= EIA-FR / Jacques Bapst 17 Java / Expressions et opérateurs PR1_02 19 Java / Expressions et opérateurs Affectation / Assignation Opérateur instanceof § Permet de tester si une expression (de type référence) est compatible (convertible) avec un type donné. Sera vu plus en détail dans le cadre du polymorphisme § Enregistre dans l'opérande de gauche (variable) la valeur de l'opérande de droite (valeur de l'expression) § Le type de la variable (opérande de gauche) doit être compatible avec le type de l'expression de droite (si nécessaire, conversion élargissante automatique) § Le type et valeur de retour d'une expression d'affectation correspondent au type de la variable et, respectivement, à sa valeur après affectation § Attention : Ne pas confondre l'opérateur d'affectation (=) avec celui d'égalité (==) "prend pour valeur" et non pas "est égal à" a = b + c; // La variable a prend pour valeur le résultat de la somme (b+c) a = b = c; // Associativité à droite Þ a = (b = c) t[i] = circle.center(); EIA-FR / Jacques Bapst est équivalent à PR1_02 18 § Retourne true si l'opérande de gauche (qui doit être une expression de type objet ou tableau) est une instance du type spécifié par l'opérande de droite (qui doit être un type référence) "Texte" instanceof String; "Texte" instanceof Object; null instanceof String; new int[] {1} instanceof Object new int[] {1} instanceof byte[] // // // // // true true false true false tableau d'int Û tableau de byte // Utile pour prévenir des erreurs de transtypage if (forme instanceof Polygone) { Polygone p1 = (Polygone)forme; } EIA-FR / Jacques Bapst PR1_02 20 Java / Expressions et opérateurs Opérateurs particuliers § Ces opérateurs particuliers sont parfois considérés comme des éléments syntaxiques, parfois comme opérateurs : • Accès à un membre d'un objet (.) Complément obj.x obj.f() • Accès à un élément d'un tableau ([]) t[2] • Invocation de méthode (()) rectangle.move(x, y) • Création d'un objet (new) new Point(4.0, -2.5); new ArrayList(); • Conversion de type / Transtypage (()) (float)position (int)(a*1.414f) (Circle)shape EIA-FR / Jacques Bapst PR1_02 21 Java / Instructions Instructions [2] Informatique / Programmation § Une instruction vide est constituée d'un simple point-virgule (;) Une instruction vide ne fait rien, mais son expression syntaxique peut parfois être utile. § Une instruction peut s'étendre sur plusieurs lignes du fichier source Dans ce cas, les retours à la ligne doivent être placés entre les éléments syntaxiques. Programmation orientée objet avec Java § Plusieurs instructions peuvent être placées sur une même ligne du fichier source. Cette pratique est généralement à éviter et doit être réservée aux cas où les instructions sont fortement corrélées et assez courtes. 03 : Instructions et contrôle de flux § Le point-virgule marque la fin d'une instruction (et non la fin d'une ligne). Jacques Bapst [email protected] screen.draw(x, y); x += dx; y += dy; EIA-FR / Jacques Bapst Java / Instructions 3 Java / Instructions Instructions [1] Bloc d'instructions § Un bloc d'instructions (appelé également instruction composée) est constitué d'un certain nombre d'instructions quelconques placées entre accolades ( {...} ). § Une instruction définit une opération qui sera exécutée par la machine virtuelle Java. § Par défaut, les instruction sont exécutées séquentiellement (les unes après les autres), dans l'ordre dans lequel elles ont été rédigées (ordre d'apparition dans le fichier source). § Un bloc d'instructions peut être utilisé pratiquement partout où une instruction est requise par la syntaxe Java. if (a<b) { t = b; b = a; a = t; } § Cependant, certaines instructions de contrôle de flux permettent de modifier l'ordre d'exécution d'une manière bien définie (instructions exécutées conditionnellement, répétition d'instructions, etc.). § Les expressions avec effets de bord (affectation, incrémentation, décrémentation et invocation de méthodes) peuvent être utilisées comme instructions; elles sont alors suivies d'un point-virgule : // Début du bloc d'instructions // Fin du bloc d'instructions § Pour mettre en évidence la structure du code, on indente les instructions à l'intérieur du bloc. La forme suivante est fréquemment utilisée : if (a<b) { j = 2; count++; ( Notez bien l'indentation ) move(dx, dy); EIA-FR / Jacques Bapst PR1_03 } PR1_03 2 EIA-FR / Jacques Bapst t = b; b = a; a = t; PR1_03 4 Java / Instructions Java / Instructions Représentation en mémoire Déclaration de variables locales [1] § Une déclaration de variable locale définit (dans la mémoire vive) une zone mémoire dont la taille et le codage dépend du type de la variable. § Une déclaration de variable locale (souvent simplement appelée variable) définit une zone mémoire en lui associant un nom (identificateur) et un type. La déclaration permet en outre de spécifier (optionnellement) la valeur initiale de la variable. § Les détails d'implémentation ne sont pas importants (mais il faut retenir le principe). § Syntaxe : { Type Identificateur [ = Expression ]; int price; price = 3; { int cost=2*price; } § Toutes les variables locales doivent être déclarées et initialisées avant d'être utilisées. § En Java, les déclarations de variables peuvent être placées n'importe où dans les blocs d'instructions. PR1_03 Contenu 1215 price 1216 3 1217 cost 1218 6 1219 Représentation abstraite usuelle (notation à utiliser) price 5 3 cost 6 EIA-FR / Jacques Bapst PR1_03 7 Java / Instructions Java / Instructions Portée des variables Déclaration de variables locales [2] § Une seule instruction de déclaration de variable permet de déclarer et d'initialiser plusieurs variables, pour autant qu'elles soient toutes du même type. § Lors de déclarations multiples, les noms des variables (avec leurs éventuelles valeurs initiales) sont séparés par des virgules. § Les variables locales ne peuvent être utilisées que dans la méthode ou le bloc d'instruction au sein duquel elles ont été déclarées § On définit la portée (scope) ou portée lexicale d'une variable la zone de code où la variable est accessible (cette zone va de sa déclaration jusqu'à la fin du bloc dans lequel elle est déclarée). § A la fin de leurs portées lexicales, les variables ne sont plus visibles et le système peut récupérer l'espace mémoire qui leur était alloué int counter; Point p; int number= 0; // Déclaration et initialisation String s = getName(); // Valeur connue à l'exécution seulement int i, j, k; // Déclarations multiples int i=2, j=5, k; // i et j sont initialisés, k ne l'est pas EIA-FR / Jacques Bapst Adresse } Conseil : Pour faciliter la lecture du code, groupez de préférence les déclarations de variables au début des structures (méthodes, blocs d'instructions ) EIA-FR / Jacques Bapst Variable PR1_03 public static void m() { int i = 0; while (i<10) { double d = 1.2 * i; System.out.println(d); i++; } System.out.println(i); } 6 EIA-FR / Jacques Bapst // // // // // // // i i i i i i i d d d PR1_03 8 Java / Instructions conditionnelles Java / Instructions conditionnelles La sélection Instruction if ... else § On appelle sélection la possibilité de choisir d'exécuter une ou plusieurs instructions en fonction de la situation (exprimée sous la forme d'une condition) prévalant à l'instant considéré. if ( expression_booléenne ) instruction_A else instruction_B § Une clause else peut (optionnellement) être mentionnée dans l'instruction if § On trouve trois formes de réalisation en Java : § L'instruction qui suit la clause else est exécutée si l'expression booléenne (la condition) est fausse • L'instruction conditionnelle (if (cond) ...) qui permet d'exécuter ou non un bloc d'instructions. • L'instruction d'alternative (if (cond) ... else ...) qui permet d'exécuter un bloc d'instructions ou, alternativement, un autre bloc d'instructions Instruction(s) if (average >= 4) System.out.println("Pass"); else { System.out.println("Fail"); repeat = true; } • L'instruction sélective (switch (expr) case ...) qui permet (dans sa variante habituelle) de choisir d'exécuter un parmi plusieurs blocs d'instructions EIA-FR / Jacques Bapst PR1_03 9 Java / Instructions conditionnelles EIA-FR / Jacques Bapst Expression booléenne true Instruction(s) A PR1_03 11 Emboîtement d'instructions if ... else § L'instruction if / else peut contenir dans chacune de ses deux branches une autre instruction if / else qui peut elle-même contenir une autre instruction … if ( expression_booléenne ) instruction § On peut ainsi avoir plusieurs niveaux d'emboîtement formant une logique plus ou moins complexe. § Exécution d'une instruction de manière conditionnelle § La condition est exprimée par une expression booléenne § En l'absence de blocs d'instructions ({…}), la clause else se rapporte toujours au if précédent (sans else) le plus proche (l'indentation ne change pas la logique du code !). § L'instruction est exécutée si la condition est vraie if (age >= 18) status = "Adult"; Expression booléenne true if (x >= 2) if (x <= 20) status = 1; else if (x <= 10) status = 2; else status = 3; Instruction(s) false if (x >= 2) if (x <= 20) status = 1; else if (x <= 10) status = 2; else status = 3; L'indentation est importante pour la lisibilité du code ! EIA-FR / Jacques Bapst false Java / Instructions conditionnelles Instruction if if ((age<16) || isStudent) { canHaveAllocations = true; amount = 400; } B PR1_03 10 EIA-FR / Jacques Bapst if (x >= 2) { if (x <= 20) status = 1; } else { if (x < 0) status = 0; else status = 2; } PR1_03 12 Java / Instructions conditionnelles Java / Instructions conditionnelles Imbrication if ... else if ... [1] Instruction switch [1] § Lorsque l'on doit choisir une instruction (ou un bloc d'instructions) parmi plusieurs (groupes d'instructions mutuellement exclusifs) il faut imbriquer les clauses if else if sur plusieurs niveaux. if (n==1) { ... } else { if (n==2) { ... } else { if (n==3) { ... } else { if (n==4) { ... } else { ... } switch ( expression ) { case const1 : suite_instructions_1 case const2 : suite_instructions_2 // Code executed if n is 1 ... default : suite_instructions_default } // Code executed if n is 2 § Instruction de sélection multiple parmi une liste d'instructions (indexées) et dont le point d'entrée est déterminé par la valeur d'une expression (constante) de type byte, short, int, char Java 7 d'un type énuméré (enum) ou String // Code executed if n is 3 § A partir du point d'entrée, l'exécution se poursuit ensuite dans les instructions suivantes ( celles des autres clauses case) jusqu'à la fin de l'instruction switch ou jusqu'à l'instruction break (ou return) qui interrompt l'instruction switch // Code executed if n is 4 // Code executed otherwise EIA-FR / Jacques Bapst PR1_03 13 Java / Instructions conditionnelles EIA-FR / Jacques Bapst Instruction switch [2] § Si le nombre d'alternatives est grande, l'indentation provoque un décalage du code vers la droite qui peut devenir gênant. n = ... switch (n) { case 1 : Instruction; Instruction; ... case 5 : case 6 : Instruction; Instruction; ... case 8 : Instruction; Instruction; ... default : Instruction; Instruction; ... } § C'est pourquoi l'on préférera, dans ce cas, la disposition suivante : (n==1) { ... } else if (n==2) { ... } else if (n==3) { ... } else { ... } // Code executed if n is 1 // Code executed if n is 2 // Code executed if n is 3 // Code executed otherwise Remarque : Il ne s'agit pas d'une nouvelle syntaxe mais simplement d'une mise en page plus lisible (sans cumul de l'indentation). EIA-FR / Jacques Bapst 15 Java / Instructions conditionnelles Imbrication if ... else if ... [2] if PR1_03 PR1_03 14 EIA-FR / Jacques Bapst // Variable of type byte, short, int, char, enum or String Java 7 // Start here if n is 1 // Start here if n is 5 or 6 // Start here if n is 8 Point d'entrée A partir du point d'entrée, toutes les instructions qui suivent sont exécutées jusqu'à la fin de l'instruction switch ou jusqu'à l'instruction break (ou autre instruction d'interruption comme return, throw, …) // Start here if n ¹ {1, 5, 6, 8} PR1_03 16 Java / Instructions conditionnelles Java / Instructions conditionnelles Instruction switch [3] Instruction switch [5] § Le mot clé default définit un point d'entrée qui est sélectionné si la valeur de l'expression ne correspond à aucune valeur dans la liste des clauses case. Le mot clé default est optionnel et est généralement placé comme dernière clause (ce n'est pas imposé par le langage mais fortement recommandé). true Case a Instruction(s) A break; false § Plusieurs clauses case peuvent étiqueter la même suite d'instructions (même point d'entrée pour plusieurs valeurs différentes). true Case b § Les valeurs associées aux clauses case doivent être des valeurs ou expressions constantes (évaluables par le compilateur). Instruction(s) B break; false § Toutes les valeurs des clauses case doivent être différentes. L'instruction switch est une instruction de bas niveau qui peut être remplacée par un enchaînement d'instructions if…else. Les instructions if…else offrent, en outre, davantage de flexibilité (expressions de types quelconques, utilisation de variables et d'expressions relationnelles et logiques dans les clauses de sélection) et n'imposent pas l'utilisation quasi systématique d'instructions d'interruption (break, return, …). EIA-FR / Jacques Bapst PR1_03 true Case z Instruction(s) Z break; false 17 EIA-FR / Jacques Bapst PR1_03 19 Java / Instructions conditionnelles Java / Instructions conditionnelles Instruction switch [4] Instruction switch [6] int month, nbDays; month = . . . Case a true Instruction(s) switch (month) { case 4 : case 6 : case 9 : case 11 : nbDays = 30; break; A false Case b true Instruction(s) B case 2 : nbDays = 28; break; false // Années bissextiles non considérées default : nbDays = 31; break; } Case z true Conseil : Instruction(s) Z false EIA-FR / Jacques Bapst PR1_03 18 EIA-FR / Jacques Bapst Sauf cas exceptionnels, placez systématiquement une instruction break (ou return) à la fin de chaque clause case car il est rare que l'on souhaite l'exécution des instructions des clauses case qui suivent celles du point d'entrée. PR1_03 20 Java / Instructions conditionnelles Java / Instructions itératives Itérations / Boucles Instruction switch [7] § On appelle boucle ou itération l'exécution multiple d'une ou de plusieurs instructions en fonction de la situation (exprimée sous la forme d'une condition) prévalant à l'instant considéré. boolean positiveAnswer = false; char switch case case case case case case answer = readKeyboardChar(); (answer) { 'y' : 'Y' : 'j' : 'J' : 'o' : 'O' : positiveAnswer = true; break; § On trouve trois formes de réalisation en Java : • L'instruction while qui permet de répéter un nombre quelconque de fois (0 ou plus) l'exécution d'une instruction ou d'un bloc d'instructions. Le nombre de répétitions n'est pas forcément connu au moment d'entrer dans la boucle. • L'instruction do … while qui permet de répéter un nombre quelconque de fois (1 ou plus) l'exécution d'une instruction ou d'un bloc d'instructions. Le nombre de répétitions n'est pas forcément connu au moment d'entrer dans la boucle. • L'instruction for qui permet de répéter un nombre quelconque de fois (0 ou plus) l'exécution d'une instruction ou d'un bloc d'instructions. Le nombre de répétitions est généralement connu au moment d'entrer dans la boucle ( mais ce n'est pas toujours le cas). case 'n' : case 'N' : positiveAnswer = false; break; default : System.out.println("Caractère incorrect"); } EIA-FR / Jacques Bapst PR1_03 21 Java / Instructions conditionnelles EIA-FR / Jacques Bapst PR1_03 Java / Instructions itératives Instruction switch [8] Instruction while String typeOfDay; switch (dayOfWeek) { case "Monday": typeOfDay = "Start of work week"; Java 7 while ( expression_booléenne ) instruction § Instruction itérative break; case "Tuesday": case "Wednesday": case "Thursday": typeOfDay = "Midweek"; break; case "Friday": typeOfDay = "End of work week"; break; case "Saturday": case "Sunday": typeOfDay = "Weekend"; break; default: typeOfDay = "Invalid day of the week"; 23 • • • • • L'expression booléenne est évaluée Si elle est vraie, l'instruction est exécutée Puis l'expression booléenne est évaluée à nouveau etc... L'instruction while se termine si l'expression booléenne est fausse § L'instruction est exécutée 0, 1 ou n fois // Affiche les nombres de 0 à 9 int count = 0; while (count <= 9) { System.out.println(count); count++; } break; } Expression booléenne true Instruction(s) false Attention : La comparaison prend en compte les minuscules/majuscules EIA-FR / Jacques Bapst PR1_03 22 EIA-FR / Jacques Bapst PR1_03 24 Java / Instructions itératives Java / Instructions itératives Instruction do ... while Instruction for [2] § En lieu et place d'instructions, l'initialisation peut contenir la déclaration (et éventuellement l'initialisation ) d'une ou plusieurs variables locales (mais toutes du même type) dont la portée est limitée au corps de la boucle do instruction while ( expression_booléenne ); § Instruction itérative • • • • • • Exécute l'instruction Évalue l'expression booléenne Si elle est vraie, l'instruction est exécutée à nouveau Évalue à nouveau l'expression booléenne etc... L'instruction do se termine si l'expression booléenne est fausse § La conclusion est fréquemment constituée par une instruction de mise à jour d'un compteur de boucle (incrémentation par exemple ) § L'initialisation, l'expression booléenne et la conclusion sont facultatives mais le point-virgule est obligatoire (par défaut, l'expression booléenne est vraie ) § L'instruction est exécutée 1 ou n fois Initialisation do { display("Nombre positif : "); number = keyboard.readNumber(); } while (number < 0); Instruction(s) Expression booléenne // Affiche les nombres de 0 à 9 for (int count=0; count<=9; count++) { System.out.println(count); } true Conclusion Expression booléenne true Instruction(s) false false EIA-FR / Jacques Bapst PR1_03 25 Java / Instructions itératives EIA-FR / Jacques Bapst 27 Java / Instructions itératives Imbrication de boucles [1] Instruction for [1] § On peut placer n'importe quelle(s) instruction(s) à l'intérieur d'une instruction d'itération while, do ou for, y compris une nouvelle instruction d'itération. for ( initialisation ; expression_booléenne ; conclusion ) instruction § Instruction itérative § Les instructions itératives peuvent donc être imbriquées. § Les expressions d'initialisation et de conclusion peuvent être formées par zéro, une ou plusieurs instructions (expressions) séparées par des virgules § Une telle construction est très fréquente. § A deux exceptions près1), l'instruction for est équivalente à la boucle while suivante : initialisation; while ( expression_booléenne ) { instruction; conclusion; } for (int i=1; i<=5; i++) { System.out.println("i> " + i); for (int k=0; k<=2 ; k++) { System.out.println(" PR1_03 26 k> " + k); } System.out.println("--------"); } 1) - S'il y a un continue, conclusion sera exécuté dans l'instruction for mais pas dans while - Initialisation et conclusion ne peuvent pas être des instructions séparées par des virgules dans la boucle while EIA-FR / Jacques Bapst PR1_03 EIA-FR / Jacques Bapst i> 1 k> 0 k> 1 k> 2 -------i> 2 k> 0 k> 1 k> 2 -------i> 3 k> 0 k> 1 k> 2 -------i> 4 k> 0 k> 1 k> 2 -------i> 5 k> 0 k> 1 k> 2 -------- PR1_03 28 Java / Instructions de rupture Java / Instructions itératives Imbrication de boucles [2] § Autre exemple d'imbrication de boucles : for (int i=0; i<3; i++) { System.out.println("i> " + i); int j = 2*i; while (j>0) { System.out.println(" j> " + j); for (int k=6; k>0 ; k=k-2) { System.out.println(" k> " + k); } j = j/2; } int m = 4-i; do { System.out.println(" m> " + m); } while (--m > 0); } EIA-FR / Jacques Bapst Instruction break i> 0 m> m> m> m> i> 1 j> break [ étiquette ] ; 4 3 2 1 2 k> k> k> j> 1 k> k> k> m> 3 m> 2 m> 1 i> 2 j> 4 k> k> k> j> 2 k> k> k> j> 1 k> k> k> m> 2 m> 1 § L'instruction break force l'interpréteur à passer immédiatement à la fin de l'instruction englobante switch, while, do ou for la plus interne 6 4 2 6 4 2 § L'instruction break peut être suivie du nom d'une étiquette (label). Dans ce cas, elle a pour effet de quitter immédiatement l'instruction englobante (qui peut être quelconque dans ce cas) portant l'étiquette correspondante 6 4 2 testBreak: for (int i=1; i<=5; i++) { for (int j=1; j<=5; j++) { if (j == 3) break; if (i == 4) break testBreak; System.out.println("i*j=" + i*j); } } 6 4 2 6 4 2 PR1_03 29 EIA-FR / Jacques Bapst PR1_03 31 Java / Instructions de rupture Java / Instructions itératives Instructions étiquetées Instruction continue [1] § On peut donner un nom (étiquette, label) à une instruction en la précédant d'un identificateur et d'un caractère deux points (':') : continue [ étiquette ] ; § L'instruction continue force la boucle englobante la plus interne à démarrer une nouvelle itération nom : instruction ; § L'instruction continue ne peut être utilisée qu'au sein d'une boucle while, do ou for § Les étiquettes ne sont utilisées qu'en relation avec les instructions d'interruption break et continue (voir pages suivantes). § Avec une étiquette, l'instruction continue force la boucle englobante identifiée par l'étiquette à démarrer une nouvelle itération § En Java, les étiquettes ne peuvent pas être utilisées pour effectuer un branchement direct (goto label). § Effets : • Avec while et do : évalue l'expression booléenne et, si elle est vraie, exécute une nouvelle fois le corps de la boucle Remarque : même si goto est un mot réservé du langage, il n'est pas utilisé actuellement. EIA-FR / Jacques Bapst // Termine la boucle interne // Termine la boucle externe PR1_03 • Avec for : effectue les instructions de conclusion puis évalue l'expression booléenne et, si elle est vraie, exécute une nouvelle fois le corps de la boucle 30 EIA-FR / Jacques Bapst PR1_03 32 Java / Instructions de rupture Java / Instructions Instruction continue [2] Instruction assert [1] § Un exemple d'utilisation de l'instruction continue : if (i == 4) continue testContinue; Complément testContinue: for (int i=1; i<=5; i++) { for (int j=1; j<=5; j++) { if (j == 3) continue; assert Expression_Booléenne ; assert Expression_Booléenne : Expression_Msg ; // Prochaine itération // de la boucle interne // Prochaine itération // de la boucle externe System.out.println("i*j=" + i*j); } } § L'instruction assert permet d'insérer des assertions dans le code source. Les assertions sont des affirmations que le programmeur tient pour vraies à l'endroit où elles sont placées et qui pourront être vérifiée lors de l'exécution du code (outil de conception). § L'expression booléenne représente l'affirmation qui doit être vraie. § L'expression optionnelle passée comme deuxième argument permettra de définir (par conversion en chaîne de caractères) le message associé à l'exception en cas de violation de l'assertion (c'est-à-dire dans le cas où l'expression booléenne est fausse). § La violation d'une assertion lève l'exception AssertionError. EIA-FR / Jacques Bapst PR1_03 EIA-FR / Jacques Bapst 33 Java / Instructions de rupture 35 Java / Instructions Instruction return Instruction assert [2] § Exemple : § L'instruction return termine l'exécution d'une méthode en cours d'exécution et rend le contrôle à la méthode appelante en retournant (restituant) éventuellement une valeur (expression) qui doit être compatible avec le type de la méthode. § Si une méthode déclare un type de retour, l'instruction return (avec une expression compatible) doit obligatoirement figurer dans le corps de la méthode (dernière instruction exécutable). public static double square(double x) { return x * x; } Complément return [ expression ] ; EIA-FR / Jacques Bapst PR1_03 if (i % 3 == 0) { ... } else if (i % 3 == 1) { ... } else { assert i % 3 == 2 : i; ... } // L'expression booléenne est-elle toujours vraie ? § Des paramètres de la machine virtuelle (-ea / -da) permettent d'enclencher ou de déclencher l'évaluation des assertions lors du lancement d'une application (par défaut, elle ne sont pas évaluées). PR1_03 34 EIA-FR / Jacques Bapst PR1_03 36 Java / Instructions Instruction synchronized Complément synchronized ( objet_ou_tableau ) { instructions } § L'instruction synchronized permet d'éviter que plusieurs threads (exécutions parallèles) de l'application modifient simultanément un même objet § L'objet (ou le tableau) de l'expression est protégé contre toute modification simultanée durant l'exécution des instructions (qui constituent la section critique) § Un verrou exclusif est pris sur l'objet ou le tableau spécifié avant d'exécuter les instructions int[] a; ... synchronized(a) { ... } EIA-FR / Jacques Bapst // Tableau accessible depuis plusieurs // threads // Opérations sur le tableau (par ex. tri) PR1_03 37 Java / Méthodes Une méthode c'est … Informatique / Programmation § On peut dire, en quelques mots, qu'une méthode… • c'est le regroupement d'une suite d'instructions auquel on attribue un nom (identificateur) • peut être invoquée (appelée) par une instruction (dans une méthode appelante) Programmation orientée objet avec Java • permet la déclaration (optionnelle) de variables locales • peut être optionnellement paramétrée au moyen d'une liste d'arguments (paramètres) 04 : Méthodes • peut optionnellement retourner une valeur (un résultat) à l'appelant • peut être liée à l'existence d'un objet (méthode d'instance) ou à l'existence d'une classe (méthode statique) Jacques Bapst [email protected] • peut optionnellement générer une exception qui indique qu'un événement exceptionnel s'est produit durant son exécution (par exemple une erreur) EIA-FR / Jacques Bapst Java / Méthodes 3 Java / Méthodes Pourquoi créer des méthodes Méthodes [1] § La taille des problèmes à résoudre à l'aide d'un programme peut être très complexe, d'où la nécessité de : § Une méthode est une séquence nommée d'instructions. • Subdiviser les problèmes en sous-problèmes (plus simples) que l'on traite indépendamment : [ approche Top / Down ] Æ Abstraction, modularisation, raffinements successifs Å • Réutiliser des parties de code existantes (exemple : entrée/sorties, fonctions mathématiques, graphiques, etc) : [ approche Bottom / Up ] Æ Rapidité de développement, efficacité, fiabilité Å PR1_04 § Ces instructions peuvent être exécutées en invoquant (appelant) la méthode. § Lorsqu'une méthode est invoquée, elle peut recevoir un certain nombre de valeurs appelées arguments (ou paramètres). § Une méthode peut facultativement retourner une valeur d'un certain type (elle se comporte alors comme une expression complexe). On utilise également le terme fonction pour désigner une méthode qui retourne une valeur. § Une invocation de méthode est une expression (avec effet de bord) qui peut être également utilisée en tant qu'instruction simple (sans exploitation d'une éventuelle de valeur de retour). § Une méthode permet de regrouper, sous un nom, une suite d'instructions exécutable à répétition. EIA-FR / Jacques Bapst PR1_04 § Après l'exécution de la dernière instruction de la méthode, le contrôle retourne à l'instruction qui a invoqué la méthode. 2 EIA-FR / Jacques Bapst PR1_04 4 Java / Méthodes Java / Méthodes Méthodes [2] Déclaration de méthode § Une méthode est caractérisée par sa signature (ou en-tête) qui comprend : En_tête_de_méthode { Corps_de_méthode } • le nom de la méthode • le type et le nom de chacun des paramètres formels (arguments) § L'en-tête de la méthode définit la signature de la méthode. • le type de la valeur retournée par la méthode (ou sinon void) § Le corps de la méthode comprend les instructions qui seront exécutées lors de l'invocation de la méthode. • les types des exceptions que la méthode peut générer • divers modificateurs de méthode qui fournissent des informations supplémentaires sur certaines propriétés de la méthode § La signature d'une méthode définit tout ce qu'il est nécessaire de savoir pour l'utiliser (pour l'utiliser à bon escient, une description du comportement est naturellement indispensable). Remarque : Contrairement à d'autres langages (Ada, C++, ...) qui imposent ou permettent de placer dans des unités de compilation séparées les spécifications (en-têtes) et implémentations (corps), le langage Java groupe ces deux éléments dans une unique structure. § La signature constitue une partie importante de la spécification de la méthode appelée aussi API (Application Programming Interface) EIA-FR / Jacques Bapst PR1_04 5 Java / Méthodes EIA-FR / Jacques Bapst PR1_04 7 Java / Méthodes Méthodes [3] En-tête de méthode § La syntaxe pour déclarer l'en-tête de la méthode est la suivante : Modificateurs Type de retour de la méthode Nom de la méthode public static int max(int a, int b) int r = a; if (b > a) r = b; return r; Variable locale } Valeur de retour (Expression) Liste des paramètres formels { [ modificateurs ] type nom ( liste_param ) [ throws exceptions ] modificateurs : Un ou plusieurs mots-clés séparés par des espaces. Ils définissent certaines propriétés de la méthode (visibilité, etc). Optionnel En-tête de la méthode [public, private, protected, final, static, native, abstract, synchronized] (signature) type : Définit le type de retour de la méthode (fonction). Si la méthode ne retourne pas de valeur, le type doit être void (vide) Corps de la méthode nom : Nom de la méthode (même règles que pour les identificateurs). ... int v = max(k, 3); ... liste_param : Liste de paramètres formels séparés par des virgules. Chaque paramètre est défini comme une variable locale (type nom). La liste de paramètres peut être vide (les parenthèses demeurent). Invocation (appel) de la méthode avec les paramètres effectifs EIA-FR / Jacques Bapst exceptions : Liste des types d'exceptions pouvant être générées par la méthode (les exceptions sont séparées par des virgules). Optionnel PR1_04 6 EIA-FR / Jacques Bapst PR1_04 8 Java / Méthodes Java / Méthodes Corps de méthode Exemples de méthodes [2] { Instructions } //----------------------------------------------// Returns the max value between v1 and v2 //----------------------------------------------public static int max(int v1, int v2) { if (v1 > v2) return v1; else return v2; } § Le corps de la méthode est constitué d'une séquence d'instructions délimitées par des accolades. § Des variables locales peuvent être déclarées, leur portée sera limitée au corps de la méthode (à partir de la déclaration). § Si la signature de la méthode déclare un type de retour (autre que void), le corps de la méthode doit comprendre une instruction return suivie d'une expression compatible avec le type de retour déclaré dans la signature. Cette expression définit la valeur de retour de la méthode (la valeur restituée). EIA-FR / Jacques Bapst PR1_04 //----------------------------------------------// Method that calls other methods //----------------------------------------------public static double fctA(double a, int b, int c) { int temp = max(b, c); return square(a) / temp; } 9 Java / Méthodes EIA-FR / Jacques Bapst 11 PR1_04 Java / Méthodes Exemples de méthodes [1] Méthode main § La méthode main() joue un rôle particulier. //----------------------------------------------// Method without parameter // Prints a separator line //----------------------------------------------public static void separate() { System.out.println("------------------------"); } § C'est le point d'entrée d'une application Java (ce n'est pas le cas pour les applets qui doivent disposer des méthodes init() et start()). § Elle est invoquée par la machine virtuelle lors du lancement de l'application. La méthode reçoit en argument un tableau de String contenant les éventuelles valeurs des paramètres de lancement (pour plus de détail consulter le slide intitulé Arguments sur la ligne de commande du chapitre consacré aux Entrées/Sorties). //----------------------------------------------// Returns the square of the parameter val //----------------------------------------------public static double square(double val) { return val * val; } EIA-FR / Jacques Bapst § Sa signature est imposée. //----------------------------------------------// Main method (displays "Hello") //----------------------------------------------public static void main(String[] args) { System.out.println("Hello"); } PR1_04 10 EIA-FR / Jacques Bapst PR1_04 12 Java / Méthodes Java / Méthodes Utilité des méthodes Invocation de méthode [1] § En Java, c'est pratiquement la seule construction permettant d'exécuter des instructions. § L'invocation (l'appel) d'une méthode provoque l'exécution de ses instructions. § Permet le découpage d'un problème en sous-problèmes plus simples. § Pour invoquer une méthode, on utilise son nom suivi de zéro, une ou plusieurs expressions séparées par des virgules et placées entre parenthèses. § Améliore la lisibilité du code source (si le nom des méthodes est bien choisi, leur invocation donnera déjà au lecteur du code des informations précieuses sur le rôle de ces méthodes). § Les valeurs de ces expressions constituent les arguments (ou paramètres effectifs) de la méthode. Ils doivent correspondre en types (compatibilité) et en nombre à la liste définie dans la signature de la méthode. § Favorise le principe d'abstraction et d'encapsulation : l'utilisateur d'une méthode n'a pas à connaître tous les détails de l'implémentation; seule la spécification est nécessaire (notion de "boîte noire"). § Syntaxe : Remarque : Les expressions constituant les paramètres effectifs sont évaluées de gauche à droite. C'est important dans des expressions du genre : f(f(a, f(b, c)), f(d, e)); § Permet la réutilisation des instructions (évite de dupliquer le code avec tous les inconvénients que cela comporte). EIA-FR / Jacques Bapst PR1_04 Nom_de_la_méthode ( expr1, expr2, ... ) 13 Java / Méthodes EIA-FR / Jacques Bapst PR1_04 15 Java / Méthodes Emplacement des méthodes Invocation de méthode [2] § En Java, toutes les méthodes doivent être déclarées au sein d'une classe. Il n'y a pas de méthodes globales. § Contrairement à d'autres langages, Java n'autorise pas de créer directement une méthode à l'intérieur d'une autre méthode. Il n'y a pas d'imbrication de méthodes (à moins de créer une classe locale). § Dans l'ordre des lignes du code source, les méthodes peuvent être invoquées (appelées) avant d'avoir été déclarées. Il est ainsi possible à deux méthodes de s'appeler mutuellement. . . . . . § Les méthodes définies sans type de retour (déclarées avec void) sont invoquées à la manière d'une instruction simple (donc suivies d'un point-virgule). separate(); § Les méthodes définies avec un type de retour sont généralement invoquées dans des expressions (affectation, paramètre effectif de méthode, …) mais peuvent également être invoquées comme des instructions simples ( dans ce cas la valeur de retour n'est pas utilisée). double double int String public static void f(int i) { System.out.println("f(" + i + ")"); if (i>0) g(--i); } . . . . . public static void g(int j) { System.out.println("g(" + j + ")"); f(j); } // Les parenthèses sont obligatoires PI = 3.1416; area, radius = 1.5; error; f = "config.ini"; area = PI * square(radius); error = readFile(f); readFile(f); // Invocation dans une expression // Invocation dans une expression // Invocation comme instruction simple . . . . . EIA-FR / Jacques Bapst PR1_04 14 EIA-FR / Jacques Bapst PR1_04 16 Java / Méthodes Java / Méthodes Séquencement Passage de paramètres [2] § Lors de l'invocation de méthodes, une pile d'appels est constituée de manière à pouvoir revenir au point d'invocation après l'exécution de la méthode (chemin de retour). public void p() { … q(); … } public void q() { … r(); … } public void r() { … … … } § La méthode agit donc sur des copies locales des valeurs des expressions transmises en paramètres lors de l'invocation. § A la sortie de la méthode (après l'exécution de la dernière instruction), les variables contenant les copies locales ne sont pas réassignées en retour (pas de "Copy Out"). § A chaque invocation de la méthode, de l'espace mémoire est alloué pour enregistrer les valeurs des paramètres effectifs (ainsi que pour les autres variables locales déclarées dans la méthode). Cet espace mémoire est libéré à la sortie de la méthode. p(); q(); § Les variables contenant les copies locales des paramètres effectifs ainsi que les variables locales déclarées dans la méthode ne peuvent donc pas être utilisées en dehors du corps de la méthode . r(); EIA-FR / Jacques Bapst PR1_04 17 EIA-FR / Jacques Bapst Java / Méthodes 19 PR1_04 Java / Méthodes Passage de paramètres [1] Passage de paramètres [3] § Lors de la définition d'une méthode, on déclare une liste (éventuellement vide) de paramètres formels qui peuvent être considérés comme des variables locales dans le corps de la méthode. § Illustration du mécanisme de passage de paramètres lors d'un appel de fonction. § Dans l'exemple, le paramètre effectif (nb) est copié dans la variable locale représentant le paramètre formel (i) de la méthode triple(). § Lors de l'invocation d'une méthode, on doit transmettre, pour chaque paramètre formel, une expression dont le type est compatible avec celui qui est déclaré dans la signature de la méthode. Ces expressions constituent les paramètres effectifs. public static void main(String[] args) { int nb = 4; int r § Le passage des valeurs des paramètres peut s'assimiler à une assignation entre les variables constituées par les paramètres formels et les expressions qui représentent les paramètres effectifs. § Cette assignation a lieu à l'entrée de la méthode (avant l'exécution de la première instruction). Ce mécanisme est appelé "Passage par valeur" (ou également mode "Copy In"). nb 4 r = triple(nb); 12 System.out.println("> " + nb + ", " + r); } i = nb; public static int triple(int i) { return 3*i; } i 4 nb 4 i 4 nb 4 Console > 4, 12 EIA-FR / Jacques Bapst PR1_04 18 EIA-FR / Jacques Bapst PR1_04 20 Java / Méthodes Java / Méthodes Passage de paramètres [4] Surcharge de méthodes [2] § Le mécanisme qui a été vu pour le passage de paramètres de types primitifs s'applique également si les paramètres passés sont de types référence (des objets ou des tableaux). § La technique consistant à définir plusieurs méthodes avec le même nom s'appelle la surcharge de méthode (Overloading). § Lors de l'invocation d'une méthode surchargée, c'est le compilateur qui détermine (sur la base du profil des paramètres) la méthode qui doit être appelée. § Dans ce cas, on aura donc une copie locale de la référence mais pas des valeurs référencées (c'est efficace à l'exécution mais peut induire des effets de bord dont il faut être conscient). Conseil : § Le détail du passage de paramètres de type référence ainsi que les implications que peut induire ce mode de transfert seront décrits dans le chapitre suivant consacré aux tableaux. § Il est possible de définir des méthodes comportant un nombre variable de paramètres. Comme cette syntaxe fait appel à des tableaux, elle sera décrite à la fin du chapitre suivant. EIA-FR / Jacques Bapst PR1_04 Dans un contexte donné, plusieurs méthodes ne doivent porter le même nom que si les opérations effectuées ont une forte similitude sémantique. Remarque : Contrairement à d'autres langages (Ada, C++, ...), Java ne permet pas de surcharger les opérateurs (+, -, *, /, ==, …). EIA-FR / Jacques Bapst 21 Java / Méthodes 23 Java / Méthodes Surcharge de méthodes (Overloading) Récursivité [1] § Le langage Java permet de définir plusieurs méthodes avec le même nom pour autant que la liste des paramètres soit différente. § Une méthode est dite récursive si elle s'invoque elle-même. § Deux listes de paramètres sont différentes si : Complément • le nombre de paramètres est différent ou alors • la liste ordonnée des types des paramètres est différente (Remarque : la deuxième règle est suffisante) § On remarquera que : • le type de retour de la méthode n'entre pas en considération • le nom des paramètres formels non plus § Exemples : PR1_04 public static long factorial(long x) { if (x<0) { ... }; // Error if x is negative if (x==0) return 1; else return x * factorial(x-1); } § On parle de récursivité indirecte si la méthode m1 en cours de déclaration invoque une autre méthode m2 qui, elle, invoque m1 dans ses instructions (directement ou indirectement). public static int triple(int i) { return (i*3); } public static float triple(float f) { return (f*3); } EIA-FR / Jacques Bapst PR1_04 22 EIA-FR / Jacques Bapst PR1_04 24 Java / Méthodes Récursivité [2] Complément § Les méthodes récursives doivent toujours contenir une instruction d'arrêt (généralement sous la forme d'une instruction if (…) ) correspondant à un cas de base qui termine la récursivité (sous peine d'épuiser les ressources du système, notamment la mémoire). § L'algorithme doit d'autre part garantir (dans toutes les situations) une convergence vers le cas de base (c'est-à-dire se rapprocher de l'instruction d'arrêt de la récursivité). § Certains algorithmes se prêtent particulièrement bien à l'écriture de code récursif (parcours d'arbres, de graphes, fonctions mathématiques récurrentes, ...). EIA-FR / Jacques Bapst PR1_04 25 Java / Tableaux Déclaration de tableaux [1] Informatique / Programmation Type_des_éléments [] Identificateur ; § Déclaration de la variable identifiant l'objet de type tableau. § La variable déclarée constitue une référence (sorte de pointeur vers une adresse mémoire) qui permet d'accéder au tableau. Programmation orientée objet avec Java § Le littéral null est une valeur particulière qui indique l'absence de référence (ou la référence vers rien). 05 : Tableaux § La taille du tableau n'est pas définie lors de sa déclaration mais seulement lors de sa création. Jacques Bapst [email protected] int[] intArray; // Déclaration d'un tableau de valeurs primitives int Point[] constellation; // Déclaration d'un tableau d'objets Point byte[][] matrix; // Déclaration d'un tableau de tableaux de byte EIA-FR / Jacques Bapst Java / Tableaux 3 Java / Tableaux Tableaux Déclaration de tableaux [2] § Un tableau est une séquence indexée d'éléments (valeurs) de même type. Type_des_éléments [] Identificateur = Initialisation ; § Une expression d'initialisation peut être ajoutée à la déclaration; dans ce cas, le tableau est créé et la taille est déterminée par le résultat de l'expression d'initialisation. § Il est caractérisé par sa taille (nombre d'éléments qu'il peut contenir) et par le type de ses éléments. § Le type tableau est un type référence (comme les objets). § L'expression d'initialisation doit retourner un objet de type tableau compatible avec le type de la variable que l'on déclare. § Les éléments d'un tableau peuvent être des valeurs de types primitifs, des objets ou des tableaux. § L'initialisation peut consister en une expression de création de tableau (avec l'opérateur new [voir pages suivantes] ). § Il faut bien faire la distinction entre les deux étapes : - la déclaration du tableau ([]) qui crée une variable (référence) représentant un objet de type tableau - et la création du tableau (new) qui alloue l'espace mémoire pour enregistrer les éléments du tableau et initialise le contenu EIA-FR / Jacques Bapst PR1_05 § Une syntaxe particulière permet de spécifier un littéral tableau comme expression d'initialisation : {expr1, expr2, expr3, ...} (cette syntaxe, sans new, n'est autorisée que lors de la déclaration) tab 0 0 1 0 2 0 PR1_05 char[] vowels= {'a','e','i','o','u','y'}; 2 EIA-FR / Jacques Bapst PR1_05 4 Java / Tableaux Java / Tableaux Création de tableaux [1] Utilisation des tableaux [1] new Type_des_éléments [ Taille ] § Chaque élément d'un tableau est assimilé à une variable à laquelle on peut accéder individuellement. Par exemple : t[k]++ § La taille du tableau (nombre d'éléments) est déterminée par l'expression entre crochets qui doit avoir une valeur entière positive (ou zéro); la taille ne doit pas obligatoirement être connue à la compilation. § Chaque élément est identifié par un indice entier compris entre 0 et (n-1), n étant la taille du tableau § L'accès aux éléments d'un tableau s'effectue de la manière suivante : § Une fois qu'un tableau est créé, sa taille ne peut plus varier. § Les éléments du tableau sont initialisés à leurs valeurs par défaut (false pour les booléens, 0 pour tous les autres types primitifs, null pour les objets et les tableaux). Nom_tableau [ indice ] § L'opérateur new retourne une référence à l'objet tableau créé . int[] topTen = new int[10]; String[] text = new String[50]; int[] tab; tab = new int[3]; tab[0] = 7; tab[1] = 4; tab[2] = tab[0] + tab[1]; // Tableau de 10 entiers (int) // Déclaration et création simultanées byte[][] matrix = new byte[25][80]; Point[] // Déclaration d'un tableau d'entiers (int) // Création du tableau avec 3 éléments // Assignation du premier élément // Assignation et accès aux éléments constellation = new Point[astro.getDimension()]; EIA-FR / Jacques Bapst PR1_05 5 Java / Tableaux EIA-FR / Jacques Bapst 7 Java / Tableaux Création de tableaux [2] Utilisation des tableaux [2] § Le nombre d'éléments d'un tableau peut être obtenu à l'aide de l'attribut length qui est prédéfini pour tous les objets de type tableau (accessible en lecture uniquement). new Type_des_éléments [] {élém1, élém2, ...} § Combinaison de la création d'un tableau avec l'initialisation de ses éléments (qui doivent être compatibles avec le type déclaré). Exemple : § La taille du tableau n'est pas spécifiée explicitement. Elle est déterminée par le nombre d'éléments énumérés entre les accolades (agrégat). nbElements = tab.length; § Si, lors de l'accès à un élément d'un tableau, l'indice spécifié est négatif ou supérieur à l'indice du dernier élément (length -1), l'exception ArrayIndexOutOfBoundsException sera générée. § Cette notation permet de créer des tableaux anonymes (non affectés à une variable) qui peuvent, par exemple, être passés en paramètre lors de l'invocation d'une méthode. § L'expression qui détermine l'indice doit être de type byte, short, char ou int (le type long n'est pas autorisé). § Le littéral null peut être utilisé pour représenter l'absence de tableau (comme pour tout autre objet) // Exemple de tableau anonyme passé à la méthode askQuestion String answer= askQuestion("Continue?", new String[] {"Yes", "No"}); EIA-FR / Jacques Bapst PR1_05 PR1_05 Exemple : 6 char[] word = null; EIA-FR / Jacques Bapst PR1_05 8 Java / Tableaux Java / Tableaux Tableaux multidimensionnels [1] Tableaux multidimensionnels [2] § L'attribut length peut être consulté pour chacune des dimensions § Les tableaux peuvent avoir plusieurs dimensions. On parle alors de tableaux multidimensionnels (attention à ne pas confondre nombre de dimensions et taille du tableau). char[][] crossword = new char[5][10]; int nbRows = crossword.length; // nbRows=5 int nbCols = crossword[0].length; // nbCols=10 § Un tableau multidimensionnel est un tableau de tableaux (de tableaux, de tableaux, ...). § Lors de la création d'un tableau multidimensionnel, il n'est pas obligatoire de définir la taille de toutes les dimensions § Chaque paire de crochets ([]) représente une dimension (aussi bien pour la déclaration, la création que l'accès aux éléments) int[][] myArray = new int[2][3]; myArray § Par contre, si l'on définit les tailles, il faut le faire dans l'ordre, de gauche à droite. // Éléments initialisés à 0 0 1 2 0 0 0 // Ok, déclaration de la première dimension float[][][] colorPalette = new float[10][][]; // Ok, déclaration des deux premières dimensions float[][][] colorPalette = new float[10][20][]; 0 1 EIA-FR / Jacques Bapst (cependant, la première dimension doit obligatoirement être définie). 0 1 2 0 0 0 // Erreur à la compilation ! float[][][] colorPalette = new float[][20][]; PR1_05 9 Java / Tableaux PR1_05 11 Java / Tableaux Tableaux multidimensionnels [1] Tableaux multidimensionnels [3] § Exemple d'utilisation § Pour les tableaux multidimensionnels, les valeurs littérales (initialiseurs) sont constituées par l'emboîtement d'accolades contenant les éléments de chacune des dimensions (reflet de la structure : tableau de tableaux). //--------------------------------------------------------// Multiplication table 0x0 ... 9x9 //--------------------------------------------------------int[][] multTable = new int[10][10]; // Éléments initialisés à 0 for (int i=0; i<multTable.length; i++) { for (int j=0; j<multTable[i].length; j++) { multTable[i][j] = i*j; } } EIA-FR / Jacques Bapst EIA-FR / Jacques Bapst PR1_05 //---------------------------------------------------------// Tableau de 7 éléments dont chacun représente // un tableau de 5 éléments //---------------------------------------------------------int[][] multTable = { {0, {0, {0, {0, {0, {0, {0, 10 EIA-FR / Jacques Bapst 0, 0, 0, 1, 2, 3, 2, 4, 6, 3, 6, 9, 4, 8, 12, 5, 10, 15, 6, 12, 18, 0}, 4}, 8}, 12}, 16}, 20}, 24} }; PR1_05 12 Java / Tableaux Java / Tableaux Tableaux multidimensionnels [4] Représentation en mémoire [1] § Les tableaux multidimensionnels étant des tableaux de tableaux, ils ne doivent pas obligatoirement être rectangulaires (ou cubiques ou ...). //---------------------------------------------------------// Tableau de 7 éléments dont chacun représente // un tableau de taille variable // (tableau triangulaire) //---------------------------------------------------------int[][] multTable = { {0}, {0, {0, {0, {0, {0, {0, § Chaque variable est associée à l'adresse d'une case mémoire § Pour les types primitifs, la case mémoire contient la valeur § Pour les types référence, la case mémoire contient une référence à la zone mémoire contenant l'objet ou le tableau § Les illustrations qui suivent sont schématiques et servent à mettre en lumière le principe de fonctionnement (l'implémentation réelle peut être sensiblement différente) Variable de type primitif 1}, 2, 4}, 3, 6, 9}, 4, 8, 12, 16}, 5, 10, 15, 20, 25}, 6, 12, 18, 24, 30, 36} }; Variable Adresse Notation abstraite Contenu 1215 1216 int number = 122; number 1217 number 122 122 1218 1219 EIA-FR / Jacques Bapst PR1_05 13 Java / Tableaux EIA-FR / Jacques Bapst 15 Java / Tableaux Tableaux multidimensionnels [5] Notation abstraite [1] § Les éléments tableaux des tableaux multidimensionnels peuvent être définis dynamiquement y compris leur taille si elle n'a pas été déterminée lors de la déclaration. //----------------------------------------------------------// Tableau de 100 éléments représentant chacun un // tableau de taille variable (créé dynamiquement). // Chaque élément du tableau est une chaîne de caractères. //----------------------------------------------------------- for (int i=0; i<s.length; i++) { s[i] = new String[(i%16)+1]; // Création dynamique du sous-tableau for (int j=0; j<s[i].length; j++) { s[i][j] = i + "/" + j; } } PR1_05 Déclaration de tableau int[] array; array array= null; array / La valeur null représente une référence vers rien Création de tableau String[][] s = new String[100][]; EIA-FR / Jacques Bapst PR1_05 array= new int[4]; array 0 1 2 3 0 0 0 0 Initialisation automatique des éléments à leurs valeurs par défaut 14 EIA-FR / Jacques Bapst PR1_05 16 Java / Tableaux Java / Tableaux Notation abstraite [2] Notation abstraite [3] Affectation des éléments du tableau array[0] array[1] array[2] array[3] = = = = 8; 17; 5; 1; array Tableaux multidimensionnels 0 1 2 3 8 17 5 1 char[][] grid = { {'a', 'b'}, {'o', 'i'}, {'x', 'y', 'z'} }; Déclaration et affectation combinées grid word 0 1 2 3 'M' 'a' 'r' 'c' 0 1 2 'Z' 'u' 't' EIA-FR / Jacques Bapst 'b' 1 0 1 'o' 'i' 0 1 2 'x' 'y' 'z' Par extension, le même mécanisme s'applique aux tableaux comportant plus de deux dimensions PR1_05 17 Java / Tableaux EIA-FR / Jacques Bapst PR1_05 19 Java / Tableaux Notation abstraite [3] Référence / Objet référencé [1] Affectation de tableaux § Une référence peut être assimilée à un pointeur vers une adresse en mémoire (mais Java ne connaît pas de réels pointeurs). name = word; 0 1 2 3 'M' 'a' 'r' 'c' 0 1 2 'Z' 'u' 't' Cette zone mémoire n'est plus référencée. Elle peut être effacée par le ramasse-miettes (Garbage Collector) System.out.println(name[1]); word[1] = 'o'; System.out.println(name[1]); // 'u' // 'o' § Les références sont opaques (contenu invisible) et ne peuvent pas être manipulées par le programmeur. § De ce fait, les détails du mécanisme sous-jacent ( détails d'implémentation) ne sont pas très importants car ils n'affectent pas le principe général de fonctionnement. § Avec les types référence, il faut toujours bien distinguer si l'on traite la référence (adresse) ou l'objet référencé (contenu). Cette distinction est très importante, notamment avec les opérateurs d'égalité (==), d'inégalité (!=) et d'affectation (=) ainsi que lors des passages en paramètre (invocation de méthodes). Attention aux effets d'alias (couplage entre variables) EIA-FR / Jacques Bapst 'a' 2 name word 1 0 char[] name= {'M','a','r','c'}; char[] word= {'Z', 'u', 't'}; name 0 § Ces remarques s'appliquent à tous les types référence, c'est-à-dire aux tableaux et aux objets (que nous verrons plus tard). En modifiant un élément de word, on modifie également le contenu de name! PR1_05 18 EIA-FR / Jacques Bapst PR1_05 20 Java / Tableaux Java / Tableaux Référence / Objet référencé [2] Passage de tableaux en paramètre [1] § Le mécanisme de passage de paramètres qui a été vu dans le chapitre consacré aux méthodes s'applique également aux types référence et notamment aux tableaux. § Comparaison et assignation de tableau et de contenu. char[] c1 = {'a', 'b', 'c'}; char[] c2 = {'a', 'b', 'c'}; char[] c3 = c1; § Le passage par valeur de la référence (avec copie locale de cette dernière) a cependant des implications qu'il est important de bien comprendre. if (c1 == c2) System.out.println("c1 égal à c2"); if (c1 == c3) System.out.println("c1 égal à c3"); if (c2 == c3) System.out.println("c2 égal à c3"); § Dans ce cas, la méthode peut accéder et modifier les valeurs référencées car c'est la référence à l'objet qui est assignée à la copie locale (paramètre effectif) lors de l'invocation. if (c1[0] == c2[0]) System.out.println("c1[0] == c2[0]"); if (c1[0] == c3[0]) System.out.println("c1[0] == c3[0]"); if (c2[0] == c3[0]) System.out.println("c2[0] == c3[0]"); § On a donc une copie locale de la référence mais pas des valeurs référencées (c'est efficace à l'exécution mais peut induire des effets de bord non souhaités). c3[0] = 'z'; if (c1[0] == c3[0]) System.out.println("c1[0] == c3[0]"); if (c2[0] == c3[0]) System.out.println("c2[0] == c3[0]"); EIA-FR / Jacques Bapst Remarque : Une méthode qui modifie l'état de certains objets ou tableaux passés en paramètres doit clairement l'indiquer dans sa spécification (description). PR1_05 21 EIA-FR / Jacques Bapst PR1_05 23 Java / Tableaux Java / Tableaux Passage de tableaux en paramètre [2] Référence / Objet référencé [3] § Dans l'exemple ci-dessous, la méthode max() reçoit, au moment de son l'appel, une copie de la référence du tableau numbers (paramètre effectif) dans la variable t (paramètre formel). public static void main(String[] args) { int[] numbers = {3, 2, 4, 2}; Quand le sage montre la lune, l'imbécile regarde le doigt ! PR1_05 0 1 2 3 3 2 4 2 int nMax = max(numbers); System.out.println("Greatest: " + nMax); } Lao-Tseu EIA-FR / Jacques Bapst numbers public static int max(int[] t) { int max = t[0]; for (int i=0; i<t.length; i++) { if (t[i]>max) max = t[i]; } return max; } 22 EIA-FR / Jacques Bapst t Console Greatest: 4 PR1_05 24 Java / Tableaux Java / Tableaux Passage de tableaux en paramètre [3] Méthodes avec paramètre variable [2] § Dans le corps de la méthode, les valeurs du paramètre variable sont lues comme les éléments d'un tableau (selon le type déclaré). § Le mécanisme a pour conséquence que la méthode max() peut, si elle le souhaite, modifier le contenu du tableau t et donc celui de numbers car c'est la même zone mémoire (alias sur les références) ! numbers 0 1 2 3 3 9 4 2 int nMax = max(numbers); System.out.println("Greatest: " + nMax); } public static int max(int[] t) { t[1] = 9; // Caution: side effect ! int max = t[0]; for (int i=0; i<t.length; i++) { if (t[i]>max) max = t[i]; } return max; } t Complément public static void main(String[] args) { int[] numbers = {3, 2, 4, 2}; § Exemple : dispErrors(7, "Erreur de lecture", "Le fichier n'existe pas"); Console dispErrors(5, "Erreur", "Accès bloqué", "Compte expiré"); Greatest: 9 dispErrors(0); PR1_05 EIA-FR / Jacques Bapst 25 Java / Tableaux PR1_05 27 Java / Tableaux Méthodes avec paramètre variable [1] Boucle for revisitée (for-each) [1] § Il est possible de définir des méthodes comportant un nombre variable de paramètres. § A partir de la version 1.5 du langage, une syntaxe complémentaire a été introduite pour l'instruction for. Elle permet de parcourir tous les éléments d'un tableau ou d'une collection de données (itérateur) en utilisant une syntaxe allégée : § Le mécanisme de passage des paramètres variables repose sur une mise en tableau automatique. Il s'agit seulement d'une simplification de la syntaxe de déclaration et d'invocation de la méthode. § Syntaxe de déclaration d'un paramètre variable : Type... NomDuParamètre § Exemple : public static void dispErrors(int cause, String... errText) § Une méthode ne peut comporter qu'un seul paramètre variable et il doit être le dernier dans la liste des paramètres formels. § Dans l'exemple donné, les paramètres seront transmis dans un tableau de chaînes de caractères String[] (le mécanisme est simplement masqué). PR1_05 26 Complément Complément § Cette méthode dispErrors() pourra être invoquée avec un nombre variable d'arguments : dispErrors(2, "Erreur fatale"); EIA-FR / Jacques Bapst EIA-FR / Jacques Bapst public static void dispErrors(int cause, String... errText) { ... for (int k=0; k<errText.length; k++) { System.out.println(errText[k]); } ... } for ( type elem : tableau ) instruction § Cette forme de l'instruction for est parfois appelée for-each § Exemple : boolean[] message = new boolean[200]; ... for (boolean elem : message) { if (elem) System.out.println("-"); else System.out.println("."); } ... EIA-FR / Jacques Bapst PR1_05 28 Java / Tableaux Boucle for revisitée (for-each) [2] Complément § En utilisant cette nouvelle syntaxe, l'exemple donné précédemment pour illustrer les méthodes avec paramètres variables pourrait être codé ainsi : public static void dispErrors(int cause, String... errText) { ... for (String text : errText) { System.out.println(text); } ... } § Le symbole ":" se lit "in" ("dans"). § Cette nouvelle syntaxe ne remplace pas la syntaxe de base (elle la simplifie dans certaines situations particulières ). § Chaque fois que l'on doit accéder à l'itérateur (par exemple l'indice du tableau) dans le corps de la boucle cette syntaxe ne peut pas être utilisée. EIA-FR / Jacques Bapst PR1_05 29 Java / Tableaux Méthodes utilitaires Complément § La classe Arrays (du package java.util) définit une série de méthodes statiques utilitaires permettant de manipuler des tableaux : • • • • • Assignation d'une valeur à certains éléments d'un tableau . . fill() Test d'égalité du contenu de deux tableaux . . . . . . . . . . . . . . equals() Tri du contenu d'un tableau . . . . . . . . . . . . . . . . . . . . . . . . . . sort() Conversion d'un tableau en liste (List) . . . . . . . . . . . . . . . . asList() Recherche binaire dans un tableau trié . . . . . . . . . . . . . . . . . binarySearch() § La classe System (du package java.lang) contient une méthode statique arraycopy() permettant de copier les éléments spécifiés d'un tableau dans un autre tableau, à une position donnée (attention : copie au premier niveau seulement [Shallow copy]). Le second tableau doit être du même type que le premier. Il peut même s'agir du même tableau. § La méthode clone() de la classe Object peut également être utilisée pour copier le contenu d'un tableau (Shallow copy) : int[] b = (int[])a.clone(); EIA-FR / Jacques Bapst PR1_05 30 Java / Exceptions Avantages des exceptions Informatique / Programmation § Augmentent la lisibilité du code en séparant les instructions qui traitent le cas normal des instructions nécessaires au traitement des erreurs ou des événements exceptionnels. § Permettent (voire imposent) la déclaration explicite des exceptions qu'une méthode peut lever (fait partie de la signature). § Forcent le programmeur à prendre en compte (traiter ou propager) les cas exceptionnels (erreurs ou autres événements) qui sont déclarés dans les méthodes qu'il invoque. Programmation orientée objet avec Java 06 : Les exceptions et leur traitement § Permettent de créer une hiérarchie d'événements et de les traiter avec une granularité différente selon les situations. § Offrent un mécanisme de propagation automatique ce qui permet au programmeur de choisir à quel niveau il souhaite traiter l'exception (celui auquel il est à même de prendre les mesures adéquates). Jacques Bapst [email protected] EIA-FR / Jacques Bapst Java / Exceptions 3 Java / Exceptions Exceptions Gestion des exceptions § En Java, les exceptions sont représentées par des objets de type Throwable. Il existe plusieurs familles d'exceptions représentées par différentes sous-classes de Throwable (par ex. Exception). § Les exceptions représentent des événements qui peuvent survenir durant l'exécution d'un programme et qui rompent le flux normal des instructions. § Différentes instructions permettent de gérer les exceptions. § Les exceptions sont principalement utilisées pour représenter des erreurs de différents types : des erreurs matérielles (crash du disque, ...) des erreurs de programmation (indice d'un tableau hors limites, ...) des erreurs liées à l'environnement d'exécution (mémoire insuffisante, ...), des erreurs spécifiques à une librairie (matrice singulière) ou à une application (numéro d'article non-défini), etc. § L'instruction throw sert à générer une exception (on dit également lever une exception, lancer une exception, …). § L'instruction try / catch / finally constitue le cadre dans lequel les exceptions peuvent être détectées (capturées) et traitées. § Le mot-clé throws (à ne pas confondre avec throw) est utilisé dans la déclaration de méthode (signature) pour annoncer la liste des exceptions que la méthode peut générer. L'utilisateur de la méthode est ainsi informé des exceptions qui peuvent survenir lors de son invocation et peut prendre les mesures nécessaires pour gérer ces événements exceptionnels (c'est-à-dire les traiter ou les propager). § Les exceptions peuvent également représenter des événements qui en sont pas à proprement parler des erreurs, mais qui correspondent à des situations exceptionnelles (prévues) qui doivent être traitées de manières différentes du flux normal des opérations (par exemple la fin d'un fichier, un mot de passe incorrect, des ressources momentanément non-disponibles, …). EIA-FR / Jacques Bapst PR1_06 PR1_06 2 EIA-FR / Jacques Bapst PR1_06 4 Java / Exceptions Java / Exceptions Générer (lever) une exception [1] Traiter une exception [1] § Les exceptions peuvent être générées soit : § Les instructions susceptibles de lever des exceptions peuvent être insérées dans un bloc try / catch qui se présente de la manière suivante : • par le système, lors de l'exécution de certaines instructions • par l'exécution de l'instruction throw (dans les classes pré-définies de la plate-forme Java [librairies] ou dans le code que l'on écrit soi-même ) try { // Bloc contenant des instructions pouvant générer des exceptions. § Parmi les instructions qui génèrent des exceptions, on peut citer : } catch (ExceptionType1 e1) { • La division entière par zéro qui génère ArithmeticException // Bloc contenant les instructions qui traitent (capturent) les exceptions // du type ExceptionType1 (ou d'une de ses sous-classes) // On peut référencer l'objet exception à l'aide de la variable e1. • L'indexation d'un tableau hors de ses limites qui génère ArrayIndexOutOfBoundsException • L'utilisation d'un tableau ou objet dont la référence vaut null qui génère NullPointerException • La création d'un tableau avec une taille négative qui génère NegativeArraySizeException } catch (ExceptionType2 e2) { // Bloc contenant les instructions qui traitent (capturent) les exceptions // du type ExceptionType2 (ou d'une de ses sous-classes). // On peut référencer l'objet exception à l'aide de la variable e2. } catch (…) { • La conversion d'un objet dans un type non compatible qui génère ClassCastException … • ... // Et ainsi de suite… } EIA-FR / Jacques Bapst PR1_06 5 Java / Exceptions EIA-FR / Jacques Bapst 7 Java / Exceptions Générer (lever) une exception [2] Traiter une exception [2] § Si une des instructions contenues dans le bloc try, lève une exception, le contrôle est passé au premier bloc catch dont le type d'exception correspond à l'exception qui a été levée (même classe ou classe parente de l'exception levée). § Pour générer explicitement une exception, on utilise l'instruction throw : throw exception_object ; § L'expression qui suit l'instruction throw doit être un objet qui représente une exception (un objet de type Throwable). § Après l'exécution de la dernière instruction du bloc catch considéré, le contrôle est passé à l'instruction qui suit le dernier bloc catch (ou à la clause finally s'il y en a une). § Lors de la création de l'objet exception, on peut généralement lui associer un message (String) qui décrit l'événement. § Si aucun bloc catch ne correspond au type d'exception qui a été levée, l'exception est propagée au niveau supérieur c'est-à-dire que le contrôle est transféré au traitement d'exception de la méthode invoquante ou du bloc englobant. Si aucun traitement n'existe au niveau supérieur pour ce type d'exception, la propagation se poursuit jusqu'à trouver un bloc catch traitant cette exception. Si ce n'est pas le cas, le programme se termine avec un message d'erreur sur la console de sortie (Stack Trace). public static long factorial(int x) throws Exception { long result = 1; if (x<0) throw new Exception("x doit être >= 0"); while (x > 1) { result *= x; x--; } return result; } EIA-FR / Jacques Bapst PR1_06 PR1_06 6 EIA-FR / Jacques Bapst PR1_06 8 Java / Exceptions Java / Exceptions Traiter une exception [3] try { instr1; Une exception a été générée instr2; instr3; } catch (ClockException ce) { instr4; instr5; } catch (NegativeArgExc na) { instr6; instr7; } catch (…) { . . . } Suite des instructions Traiter une exception [5] § Il est possible de traiter plusieurs types d'exceptions dans une seule clause catch en utilisant le symbole '|' comme séparateur. Oui § Les exceptions mentionnées dans une telle liste doivent être "indépendantes" sur le plan de leur relation hiérarchique. Une des exceptions ne peut pas être un ancêtre (super-classe) d'une autre (dans ce cas, mentionner l'exception ancêtre suffit pour traiter toutes celles de sa "famille"). Est-elle du type ClockException ? Non Oui Est-elle du type NegativeArgExc ? try { . . . } catch (UnknownUser | IncorrectPassword loginError) { . . . } Non Oui Est-elle du type . . . ? Non catch (Exception otherError) { . . . } Propagation au niveau supérieur EIA-FR / Jacques Bapst 9 PR1_06 Java / Exceptions EIA-FR / Jacques Bapst Traiter une exception [6] § Les divers types d'exception peuvent appartenir à différentes familles (voir hiérarchie de classes, en fin de chapitre). § Une exception spécialisée (par ex. FileNotFoundException) peut faire partie d'une famille plus vaste (IOException) qui elle-même fait partie d'une famille plus générale (Exception) etc. § Si plusieurs clause catch sont compatibles avec le type d'exception qui a été levée (font partie de la même famille), c'est la première qui capturera l'exception et se chargera du traitement. . (FileNotFoundException notFound) { . (IOException ioErr) { . L'ordre des clauses catch est important ! (Exception genErr) { . EIA-FR / Jacques Bapst 11 Java / Exceptions Traiter une exception [4] try { . . } catch . . } catch . . } catch . . } PR1_06 PR1_06 10 § Lorsqu'un problème a été détecté (une exception a été générée) et que l'on est en mesure de le traiter (au moins partiellement), il y a différentes mesures que l'on peut envisager, selon les cas, pour régler la situation. § Dans un traitement d'exception (bloc catch) on peut par exemple : • Régler le problème et recommencer le traitement (nécessite généralement un boucle ). Idéal mais pas toujours possible. • Faire quelque chose d'autre à la place (algorithme de substitution). • Sortir de l'application (System.exit()) après affichage d'un message (et/ou de la Stack-Trace), écriture dans un fichier log, etc. • Re-générer l'exception (après avoir effectué certaines opérations ). • Générer une nouvelle exception (après avoir éventuellement effectué certaines opérations ). • Retourner une valeur spéciale ou valeur par défaut (pour une fonction). • Terminer la méthode (si elle n'a pas de valeur de retour). • Ne rien faire (ou afficher un message) et continuer (c'est rarement une bonne solution). EIA-FR / Jacques Bapst PR1_06 12 Java / Exceptions Java / Exceptions Propagation des exceptions [1] Propagation des exceptions [3] public class PropEx { § Dans le programme PropEx, lors de la deuxième invocation de la méthode b(), une exception sera levée dans la méthode d() (division par zéro). public static void main(String[] args) { System.out.println("main starts"); b(5); b(0); System.out.println("main ends"); } //--------------------------------------------// b //--------------------------------------------public static void b(int i) { System.out.println("b starts"); try { System.out.println("b step 1"); c(i); System.out.println("b step 2"); } catch (Exception e) { System.out.println("b catches " + e); } System.out.println("b ends"); } EIA-FR / Jacques Bapst § Le déroulement de l'application sera alors altéré et l'exception sera propagée jusqu'à la méthode b() qui dispose d'un bloc catch pour traiter cette exception. b(5) 1ère invocation b(0) 2ème invocation main starts b starts b step 1 c starts d starts d ends c ends b step 2 b ends … … b starts b step 1 c starts d starts b catches ArithmeticException: / by zero b ends main ends // Suite... PR1_06 13 Java / Exceptions EIA-FR / Jacques Bapst PR1_06 15 Java / Exceptions Interprétation de la Stack-Trace [1] Propagation des exceptions [2] // …Suite § Si, dans l'exemple précédent (PropEx), on supprime tout traitement d'exception, l'exception provoquée par la division par zéro (dans la méthode d()) sera propagée jusqu'à la méthode main() qui sera alors interrompue avec affichage du nom de l'exception et de l'état de la pile des appels (Stack-Trace) : //--------------------------------------------// c //--------------------------------------------public static void c(int i) throws Exception { System.out.println("c starts"); d(i); System.out.println("c ends"); } java.lang.ArithmeticException: / by zero at javabasic.PropEx.d(PropEx.java:43) at javabasic.PropEx.c(PropEx.java:37) at javabasic.PropEx.b(PropEx.java:24) at javabasic.PropEx.main(PropEx.java:13) //--------------------------------------------// d //--------------------------------------------public static void d(int i) throws Exception { System.out.println("d starts"); int a=10/i; // Cette instruction peut générer une exception System.out.println("d ends"); } Exception in thread "main" § Il est important de savoir interpréter ces informations de manière à localiser rapidement la cause du problème qui a causé l'interruption de l'application et agir au bon endroit. } § La page suivante décrit comment interpréter cette Stack-Trace. EIA-FR / Jacques Bapst PR1_06 14 EIA-FR / Jacques Bapst PR1_06 16 Java / Exceptions Java / Exceptions Interprétation de la Stack-Trace [2] § La Stack-Trace décrit (dans l'ordre inverse) la séquence (pile) des appels qui ont conduit à l'interruption de l'application. Clause finally [2] § Un bloc finally est généralement utilisé pour effectuer des opérations de conclusion (fermeture de fichiers, de connexion réseau, de base de données, etc.) qui devraient être effectuées dans tous les cas de figure. java.lang.ArithmeticException: / by zero at javabasic.PropEx.d(PropEx.java:43) at javabasic.PropEx.c(PropEx.java:37) at javabasic.PropEx.b(PropEx.java:24) at javabasic.PropEx.main(PropEx.java:13) Exception in thread "main" § La première ligne affichée correspond au type d'exception qui a été générée (avec, éventuellement, le texte du message qui lui est associé). Le bloc finally évite donc de devoir placer ces instructions de conclusion dans le bloc try et dans tous les blocs catch. § On trouve ensuite l'enchaînement des invocations de méthodes où chacune des lignes est structurée de la manière suivante : § L'utilisation des instructions break, continue, return ou throw (dans les blocs try ou catch) n'empêche pas l'exécution préalable du bloc finally. . . . at Package.Classe.Méthode(Fichier_Source:No_de_ligne) . . . § Dans le cas d'une application, on trouvera sur la dernière ligne de la séquence des appels, la méthode main() qui constitue le point d'entrée du programme. EIA-FR / Jacques Bapst PR1_06 17 Java / Exceptions § Le bloc finally est optionnel mais un bloc try doit obligatoirement être accompagné d'au moins un bloc catch ou d'un bloc finally (ou naturellement des deux). EIA-FR / Jacques Bapst 19 Java / Exceptions Clause finally [1] Clause finally [3] § Exemple d'utilisation de la clause finally : § Une clause finally peut être ajoutée à une instruction try / catch. try { openFile(f); content= readFile(f); print(content); } § Cette clause définit un bloc d'instructions qui seront exécutées à la fin de l'instruction try / catch indépendamment du fait que des exceptions aient été levées ou non. Le bloc finally sera exécuté dans tous les cas. catch (InvalidData invdat) { System.out.println("Les données du fichier sont erronées"); } § Si aucune exception n'est levée dans le bloc try , le bloc finally sera exécuté après la dernière instruction du bloc try. catch (PrintError prerr) { System.out.println("Erreur durant l'impression"); } § Si une exception est levée dans le bloc try et est capturée par un bloc catch, le bloc finally sera exécuté après la dernière instruction du bloc catch. finally { closeFile(f); } § Si une exception est levée dans le bloc try et n'est pas capturée par un bloc catch, le bloc finally sera exécuté avant la propagation de l'exception au niveau supérieur. EIA-FR / Jacques Bapst PR1_06 PR1_06 18 EIA-FR / Jacques Bapst //--- Effectué dans tous les cas PR1_06 20 Java / Exceptions Java / Exceptions Objet exception Exceptions contrôlées / non-contrôlées § L'objet exception sert principalement de marqueur pour l'événement qui lui est associé. § Dans un traitement d'exception (bloc catch), l'objet exception est transmis et il peut être utilisé pour obtenir plus d'informations concernant l'événement. § Les exceptions contrôlées (checked) [du type Exception] doivent être soit traitées (dans une clause catch), soit propagées pour être traitées à un niveau supérieur (annoncée avec throws dans l'en-tête). § Elle ne peuvent pas être ignorées (le compilateur génère une erreur dans ce cas). § Quelques méthodes que l'on peut utiliser avec les objets exception : getMessage() : retourne un String contenant le message (texte) associé à l'exception toString() : retourne un String contenant le nom de l'exception suivi du message associé printStackTrace() : affiche sur la console de sortie le nom de l'exception, le message associé ainsi que l'état de la pile des appels qui ont conduit au traitement de l'exception (Stack-Trace) getStackTrace() : retourne les informations de la Stack-Trace sous forme d'un tableau de StackTraceElement EIA-FR / Jacques Bapst PR1_06 § Pour les exceptions non-contrôlées (unchecked) [du type RuntimeException ou Error], le programmeur a le choix de les traiter ou de les ignorer (sans erreur à la compilation). § Si une telle exception survient et qu'elle n'est traitée nulle part, elle sera alors propagée et "remontera" jusqu'à la méthode main() interrompant ainsi brutalement l'application. 21 Java / Exceptions EIA-FR / Jacques Bapst PR1_06 Java / Exceptions Hiérarchie de classes La classe Error § Dans le langage Java, toutes les exceptions sont représentées par des objets qui spécialisent la classe de base Throwable. § La classe Error représente généralement des erreurs sévères qui surviennent dans la machine virtuelle. § Trois sous-classes de Throwable sont pré-définies : Error, Exception et RuntimeException selon l'arborescence suivante : § Le langage n'impose par que les programmeurs traitent ce type d'exceptions car elles ne devraient pas survenir dans un environnement sain (machine virtuelle correctement installée). Throwable Error § On ne traite généralement pas ce type d'exceptions dans une application standard sauf éventuellement au niveau le plus haut pour informer l'utilisateur (si c'est encore possible) et éviter le crash brutal de l'application. Exception § D'autre part, les applications ne devraient pas générer (dans des instructions throw) ce genre d'exceptions. RuntimeException Exceptions non-contrôlées EIA-FR / Jacques Bapst 23 § Exemples : OutOfMemoryError, StackOverflowError, AssertionError (Erreur de conception) Exceptions contrôlées PR1_06 22 EIA-FR / Jacques Bapst PR1_06 24 Java / Exceptions Java / Exceptions Créer de nouveaux types d'exceptions La classe Exception § Un grand nombre de sous-classes de Exception sont pré-définies dans les classes de la plate-forme Java § Exemples : PrintException, IOException § Les exceptions spécifiques à une librairie ou à une application sont généralement représentées par des sous-classes de Exception. § La classe RuntimeException représente une sous-classe particulière de Exception (voir page suivante). § Pour créer ses propres types d'exceptions, il suffit de dériver (spécialiser) une classe de type Throwable (choisir une des classes existantes proche de celle que l'on souhaite créer ou, sinon, dériver la classe générale Exception). § Généralement, dans cette sous-classe, on crée uniquement deux constructeurs : un constructeur sans paramètre et un constructeur qui prend un message (String) en paramètre. § On crée rarement de nouveaux champs ou de nouvelles méthodes. public class ClockException extends Exception { public ClockException() { super(); } § Le langage impose que les exceptions de type Exception (qui ne sont pas une sous-classe de RuntimeException) soient traitées par les applications (en plaçant les instructions critiques dans un bloc try / catch, ou en déclarant l'exception dans la signature de la méthode à l'aide du mot-clé throws déléguant ainsi le traitement au niveau supérieur). EIA-FR / Jacques Bapst PR1_06 25 Java / Exceptions } EIA-FR / Jacques Bapst PR1_06 27 Java / Exceptions Utiliser des exceptions personnalisées La classe RuntimeException § La classe RuntimeException représente une sous-classe particulière (exceptions non-contrôlées) de la classe Exception. § L'utilisation des types d'exceptions que l'on a créés est identique à l'utilisation des types d'exceptions pré-définis. § Elle représente des erreurs qui sont détectées par la machine virtuelle durant l'exécution de l'application. Par exemple, l'exception NullPointerException indique que la référence à un objet n'est pas définie (null). Cette exception peut potentiellement survenir lors de l'exécution de n'importe quelle instruction qui manipule un objet. § De ce fait, le langage n'impose pas que les exceptions de type RuntimeException (et de ses sous-classes) soient traitées par les applications (ce qui serait extrêmement lourd et contraignant). § Si nécessaire, les applications peuvent cependant traiter ce type d'exceptions (dans un bloc try / catch) et prendre ainsi les mesures adéquates. § Exemples : ArithmeticException, ArrayIndexOutOfBoundsException EIA-FR / Jacques Bapst public ClockException(String message) { super(message); } A prendre tel quel pour l'instant (sera étudié ultérieurement) § La classe Exception représente la classe de base de la plupart des exceptions qui sont générées (throw) et traitées (catch) dans les applications (exceptions contrôlées). PR1_06 26 . . . if (hour < 0 || hour > 23) { throw new ClockException("Heure incorrecte"); } . . . . . . try { . . . } catch (ClockException clockErr) { System.out.println(clockErr); . . . } catch (Exception otherErr) { . . . } . . . EIA-FR / Jacques Bapst PR1_06 28 Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Comparaison de chaînes Informatique / Programmation § Comme pour tous les types référence, l'opérateur "==" compare les références et non pas les objets référencés (les chaînes de caractères). § La méthode equals() permet, elle, de comparer deux chaînes de caractères (objets référencés) et retourne true si elles sont égales. § La méthode compareTo() remplace les opérateurs "plus petit", "égal", "plus grand" en utilisant l'ordre lexicographique (ordre du dictionnaire) et en retournant respectivement une valeur (int) négative, 0 ou positive. Programmation orientée objet avec Java 07 : Chaînes de caractères String myString = "abc"; String otherStr = "abcdef"; if (myString == otherStr) System.out.println("Same string object"); Jacques Bapst if (myString.equals(otherStr)) System.out.println("Same contents"); [email protected] if (myString.compareTo(otherStr) < 0) System.out.println(myString + " smaller than " + otherStr); EIA-FR / Jacques Bapst Java / Chaînes de caractères (String / StringBuffer / StringBuilder) PR1_07 3 Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Chaînes de caractères (String) Objets String immuables § Les objets de type String sont immuables : une fois créés ils ne peuvent plus être modifiés. Naturellement, la variable de type référence peut changer de valeur, et pointer vers une nouvelle chaîne (créée à un autre emplacement). § En Java les chaînes de caractères sont représentées par des objets de type String (une classe prédéfinie). § Les chaînes de caractères ne font donc pas partie des types primitifs mais sont des types référence. "Hello" String someText = "Hello"; someText = "Bonjour"; § Une syntaxe particulière est utilisée pour définir des littéraux de type String : on entoure le texte avec des guillemets ("…"). someText "Bonjour" § On peut insérer des séquences d'échappement (identiques à celles définies pour le type char) dans les littéraux de type String. § Les objets immuables ont l'avantage de pouvoir être partagés sans risque et l'on évite les problèmes d'alias (ð optimisations possibles). § La déclaration et création d'une variable de type String s'effectue de la manière suivante : § Par contre, si l'on manipule fréquemment des objets de type String (à l'intérieur de boucles par exemple) cela peut conduire à la création d'un nombre considérable d'objets (temporaires) avec un coût non négligeable (ressources mémoires et temps d'exécution). String someText = "Hello"; "Hello" § Il est préférable dans ce cas de déclarer et utiliser des objets de type StringBuffer ou StringBuilder qui sont eux modifiables (voir pages suivantes). someTexte EIA-FR / Jacques Bapst PR1_07 2 EIA-FR / Jacques Bapst PR1_07 4 Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Opérations sur les chaînes [1] Opérations sur les chaînes [3] § La classe String dispose d'un grand nombre de méthodes pour traiter les chaînes de caractères. Les exemples qui suivent n'en sont qu'une illustration sommaire. § L'opérateur "+" permet de concaténer (mettre bout à bout) des chaînes de caractères. § Si un des deux opérandes de l'opérateur "+" est un String , l'autre opérande sera, si nécessaire, automatiquement converti en un objet de type String (par invocation de la méthode toString()). § La méthode length() permet de connaître la longueur d'une chaîne de caractères (nombre de caractères). § La méthode charAt() permet de retrouver le caractère situé à la position n d'une chaîne (0 £ n £ length() -1). String myText = "Hello"; String String s1 = myText + " Mark"; § La méthode substring() retourne une sous-chaîne d'une chaîne donnée. Les paramètres déterminent la position initiale et la position finale (+1) de la sous-chaîne dans la chaîne originelle. rec = "Hello" + s1 + 3 + ')'; String int char String String Remarque : Les objets de type String étant immuables, la concaténation est implémentée en créant un objet temporaire de type StringBuffer qui est utilisé ensuite pour créer un nouvel objet de type String. C'est donc une opération assez lourde. EIA-FR / Jacques Bapst PR1_07 Complément § Dans les librairies de base, il existe souvent plusieurs méthodes pour effectuer un même type de conversion. d = Double.parseDouble("3.14"); à 3.14 String s = String.valueOf('Z'); à "Z" boolean b = (i > 10); à true String t = (new Boolean(b)).toString(); à "true" String t = Boolean.toString(b); à "true" EIA-FR / Jacques Bapst // Idem 10 'a' "me" "YES" PR1_07 7 § La méthode split() permet de découper une chaîne de caractères sur la base d'un séparateur qui est exprimé sous la forme d'une expression régulière (regex). La méthode retourne le résultat dans un tableau de String qui contient les fragments découpés. § Ces méthodes se trouvent soit dans la classe String, soit dans les classes Wrapper associées aux types primitifs ( Byte, Short, Integer, Long, Float, Double, Character, Boolean ). double = = = = Opérations sur les chaînes [4] § Différentes méthodes permettent de convertir des valeurs de types primitifs en objets de type String et inversement (voir la tabelle qui figure à la fin du premier chapitre du document de cours). à 43 i c s t Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Opérations sur les chaînes [2] i = Integer.parseInt("43"); // // // // EIA-FR / Jacques Bapst 5 Java / Chaînes de caractères (String / StringBuffer / StringBuilder) int name = "James Bond"; i = name.length(); c = name.charAt(1); s = name.substring(2, 4); t = "Yes".toUpperCase(); § La syntaxe à utiliser pour les expressions régulières est définie dans la classe Pattern (java.util.regex). public static void main(String[] args) { String path = "usr/lib/ruby/test.rb" ; String[] elems; String sepPattern1 = "/"; // Slash separator elems = path.split(sepPattern1); elems ® { "usr", "lib", "ruby", "test.rb" } String sepPattern2 = "/|\\."; // Slash or dot separator elems = path.split(sepPattern2); elems ® { "usr", "lib", "ruby", "test", "rb" } } PR1_07 6 EIA-FR / Jacques Bapst PR1_07 8 Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Formatage de chaînes [1] Classe StringBuffer [1] § Contrairement à la classe String, la classe StringBuffer permet de créer des chaînes de caractères modifiables (la taille et le contenu peuvent varier durant l'exécution de l'application). § Cette méthode statique nommée format() retourne une chaîne de caractères formatée et prend en paramètre une chaîne de formatage (qui doit respecter une certaine syntaxe), ainsi qu'un nombre variable d'arguments (de type Object ou de types primitifs qui seront automatiquement convertis par le nouveau mécanisme d'autoboxing). § Exemple : String s = String.format("%.2f", v); dans ce cas, si v = 1.56789f, alors s = "1.57" § La notion de capacité (Capacity) représente la taille du buffer interne qui mémorise la chaîne de caractères (nombre total de Complément Complément § A partir de la version 1.5 du langage, une méthode de formatage assez puissante a été introduite dans la classe String. § La syntaxe de la chaîne de formatage est relativement complexe et sa description se trouve dans l'API de la classe Formatter (du package java.util). caractères disponibles avant de devoir agrandir la taille du buffer interne). § La capacité est automatiquement augmentée lorsque c'est nécessaire. La capacité initiale peut être définie lors de la création d'un objet StringBuffer (paramètre du constructeur, 16 par défaut ). § Ne pas confondre capacité et longueur d'un objet StringBuffer. 0 1 U n 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 t e x t e length() = 8 EIA-FR / Jacques Bapst PR1_07 EIA-FR / Jacques Bapst 9 Java / Chaînes de caractères (String / StringBuffer / StringBuilder) PR1_07 Classe StringBuffer [2] § Cette nouvelle méthode de formatage est largement inspirée de celle de la méthode printf() que l'on trouve dans le langage C (elle n'est pas totalement équivalente mais très fortement compatible). § Pour créer un objet de type StringBuffer, on doit utiliser l'opérateur new(…) (pas de syntaxe simplifiée). § Une méthode printf() a d'ailleurs été ajoutée dans la classe PrintStream et peut donc être invoquée sur les flux de sortie System.out et System.err. § Création en définissant une capacité initiale : Complément StringBuffer someText = new StringBuffer("Hello"); § Exemples : int i = 250; float v = 1.23456f; System.out.printf("v with 3 digits %.3f %n", v); System.out.printf("Hex/Octal %1$x/%1$o %n", i); System.out.printf("%5d%8.3f %n", i, v); // Création d'une chaîne vide avec une capacité de 2000 caracères StringBuffer sb = new StringBuffer(2000); // Ajout d'une chaîne littérale (String) sb.append("Bonjour"); System.out.println(sb.length()); System.out.println(sb.capacity()); // 7 // 2000 sb.append(" à tous !"); v with 3 digits 1.235 Hex/Octal fa/372 250 1.235 EIA-FR / Jacques Bapst 11 Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Formatage de chaînes [2] Complément capacity() = 20 System.out.println(sb.length()); System.out.println(sb.capacity System.out.println(sb.capacity()); PR1_07 10 EIA-FR / Jacques Bapst // 16 // 2000 PR1_07 12 Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Opérations sur les StringBuffer Classe StringBuilder append() Ajoute un élément à la fin de la chaîne de caractères (surcharge pour différents types d'éléments) insert() Insère un élément à une position donnée de la chaîne de caractères (surcharge pour différents types d'éléments) replace() Remplace une partie d'une chaîne de caractères par une autre delete() Efface une portion donnée d'une chaîne de caractères setCharAt() Remplace un caractère à une position donnée deleteCharAt() Efface (supprime) un caractère à une position donnée substring() Extrait une sous-chaîne toString() Convertit le contenu en type String EIA-FR / Jacques Bapst PR1_07 § Dans la librairie standard de la plateforme Java, on trouve également la classe StringBuilder. Complément Complément § Différentes méthodes permettent de manipuler les objets de type StringBuffer : 13 Java / Chaînes de caractères (String / StringBuffer / StringBuilder) Conversions String ó StringBuffer § Pour convertir un objet de type String en un objet de type StringBuffer on utilise le constructeur (opérateur new(…)). String s1 = "Hello"; Complément StringBuffer sBuf = new StringBuffer(s1); § Pour convertir un objet de type StringBuffer en un objet de type String on utilise la méthode toString(…) ou substring(…). StringBuffer vText = new StringBuffer(); vText.append("Un texte"); String s2 = vText.toString(); String s3 = vText.substring(3, 6); EIA-FR / Jacques Bapst // s3 <-- "tex" PR1_07 14 § Son API est compatible avec celle de la classe StringBuffer qu'elle peut remplacer dans toutes les situations où le code s'exécute dans un seul thread (c'est-à-dire dans tous les cas où il n'est pas indispensable que le code soit thread-safe). § La plupart des méthodes de la classe StringBuilder s'exécutent plus rapidement que celles de la classe StringBuffer. § Toutes les indications données dans les pages précédentes pour la classe StringBuffer s'appliquent également à la classe StringBuilder. EIA-FR / Jacques Bapst PR1_07 15 Java / Entrées / Sorties (I/O) Flux (Stream) Informatique / Programmation § La notion de flux (Stream) caractérise un chemin (une voie) de communication entre une source d'information et sa destination. Programmation orientée objet avec Java 08 : Entrées / Sorties (I/O) § L'accès aux informations s'effectue de manière séquentielle. § Les librairies Java distinguent deux genres de flux : Jacques Bapst • Les flux binaires (Byte Stream) qui peuvent représenter des données quelconques (nombres, données structurées, programmes, sons, images, … ) • Les flux de caractères (Character Stream) qui représentent des textes (chaînes de caractères) au format Unicode. [email protected] EIA-FR / Jacques Bapst Java / Entrées / Sorties (I/O) 3 Java / Entrées / Sorties (I/O) Entrées / Sorties (I/O) Utilisation des flux § La communication avec un flux comprend trois phases : § Les entrées/sorties (Input/Output ou I/O) permettent à un programme de communiquer avec certains périphériques. • L'ouverture du flux, en entrée (pour la lecture) ou en sortie (pour l'écriture) § Les entrées/sorties permettent par exemple : • de lire et d'écrire sur la console • La répétition de la lecture ou de l'écriture des données (généralement des bytes ou des caractères) (lecture au clavier et affichage à l'écran en mode 'caractères') • d'accéder aux fichiers et répertoires des disques • de communiquer avec d'autres applications Open Read / Write N End ? Y • La fermeture du flux (localement ou au travers du réseau) § Les librairies de la plate-forme Java offrent un grand nombre de classes dédiées aux entrées/sorties. La plupart des classes se trouvent dans le package java.io (qui comporte plus de 50 classes !). PR1_08 Close § Quelques classes importantes pour utiliser les flux courants : § Au fil des versions, d'autres packages sont venus compléter la librairie. Il y a notamment le package java.nio, le package java.nio.file, etc. EIA-FR / Jacques Bapst PR1_08 2 Flux d'entrée Flux de sortie Flux binaires DataInputStream DataOutputStream Flux de caractères BufferedReader PrintWriter EIA-FR / Jacques Bapst PR1_08 4 Java / Entrées / Sorties (I/O) Java / Entrées / Sorties (I/O) Lecture et écriture des flux Écriture d'un fichier texte § Pour lire un fichier texte il faut ouvrir un flux d'entrée • La classe FileReader constitue une des couches de base et comporte les méthodes élémentaires pour lire le flux depuis le système de fichiers § Pour écrire un fichier texte il faut ouvrir un flux de sortie • La classe FileWriter constitue une des couches de base et comporte les méthodes élémentaires pour écrire le flux sur le système de fichiers printf(String format, Object... args) § La lecture et l'écriture de fichiers textes sont réalisées en enrobant la couche de base avec des outils de plus haut niveau (imbrication) • Mémoire tampon (buffer) pour minimiser les accès au disque • Conversion bytes ó caractères (encodage Unicode) • Découpage en lignes / Insertion des retours à la ligne, etc. § Illustration (pour l'écriture) : PrintWriter BufferedWriter § Pour la syntaxe du texte de formatage, voir description dans l' API de la classe java.util.Formatter • Exemples p.printf("%.2f", resultScore); p.printf("Nombre %d nbItems, File-System (par ex. disque) Prix %ld price, Total %9ld\n", price*nbItems); p.printf("%5d%8.3f %n", i, v); FileWriter EIA-FR / Jacques Bapst § En plus des méthodes print() et println() la classe PrintWriter comporte également la méthode printf() qui a été décrite dans le chapitre précédent (formatage des chaînes de caractères) et qui permet de formater le texte écrit dans le fichier. PR1_08 5 Java / Entrées / Sorties (I/O) EIA-FR / Jacques Bapst PR1_08 7 Java / Entrées / Sorties (I/O) Écriture d'un fichier texte Lecture d'un fichier texte § Pour écrire séquentiellement dans un fichier de texte on peut utiliser la classe PrintWriter de la manière suivante: • Ouverture § Pour lire séquentiellement le contenu d'un fichier de texte on peut utiliser la classe BufferedReader de la manière suivante : • Ouverture String filename = "C:\\Temp\\TestOut.txt"; PrintWriter p = new PrintWriter( new BufferedWriter( new FileWriter(filename))); String filename = "C:\\Temp\\TestIn.txt"; BufferedReader r = new BufferedReader( new FileReader(filename)); • Lecture (ligne par ligne) String s; s = r.readLine(); while (s != null) { //--- Tant qu'il y a encore des lignes... System.out.println(s); s = r.readLine(); } • Écriture p.print("Hello"); p.println(" world" + 3*2); p.println("Fin du fichier " + filename); • Fermeture p.close(); • Fermeture r.close(); EIA-FR / Jacques Bapst PR1_08 6 EIA-FR / Jacques Bapst PR1_08 8 Java / Entrées / Sorties (I/O) Java / Entrées / Sorties (I/O) Lecture d'un fichier texte Lecture au clavier § La classe Scanner (java.util) offre différentes méthodes pour décomposer les données d'un flux de caractères ou d'un String. § La lecture au clavier s'effectue en imbriquant (en enrobant) le flux standard (System.in) dans deux constructeurs de classes qui se chargeront de la conversion des octets en caractères Unicode et de la mise en place d'une mémoire tampon intermédiaire. • Ouverture String filename = "D:\\Foo\\Config\\Init.cfg"; Scanner scf = new Scanner(new BufferedReader( new FileReader(filename))); § Lecture au clavier : • Lecture (ligne par ligne, utilisation d'un 2ème Scanner pour analyser chaque ligne ) String line; while (scf.hasNextLine()) { line = scf.nextLine(); Scanner scs = new Scanner(line); scs.useDelimiter("="); //--- Séparateur (regex pattern) String key = scs.next(); float val = scs.nextFloat(); Length=1.2 . . . Width=5.5 } Depth=0.27 • Fermeture String s = keyboard.readLine(); Remarques : La méthode readLine() attend jusqu'à ce que l'utilisateur introduise des caractères au clavier et termine la saisie en pressant sur la touche Return (Enter). Le caractère fin de ligne n'est pas inclus dans le String retourné. Si l'utilisateur presse uniquement sur Return, le String sera vide (longueur = 0). Contenu du fichier Init.cfg scf.close(); EIA-FR / Jacques Bapst BufferedReader keyboard = new BufferedReader( new InputStreamReader(System.in)); . . . 9 PR1_08 Java / Entrées / Sorties (I/O) EIA-FR / Jacques Bapst Lecture au clavier § Les flux suivants sont prédéfinis dans la classe System. Ces flux sont toujours ouverts et on ne les ferme pas. Entrée standard (lecture du clavier) [ InputStream ] Sortie standard (affichage à l'écran) [ PrintStream ] Sortie des messages d'erreur [ PrintStream ] (souvent identique à out par défaut) § Écriture sur la console : System.out.print("Résultats : "); //--- Reste sur la même ligne System.out.println(5*4 + ", " + 6); System.out.println(5*4 + ", " + 4+2); 11 Java / Entrées / Sorties (I/O) Entrées/Sorties standard (console) • System.in • System.out • System.err PR1_08 § Une autre manière de faire (souvent plus pratique) est d'utiliser la classe Scanner (java.util) qui permet également de saisir mais, en plus, de transformer et d'interpréter ce qui a été saisi au clavier. § Exemple d'utilisation : Scanner scan = new Scanner(System.in); System.out.print("Enter an integer (or 'x' to exit) : "); while (scan.hasNextInt()) { int i = scan.nextInt(); System.out.println("Value = " + i); System.out.print("Enter an integer (or 'x' to exit) : "); } //--- Attention au piège ! Remarques : La classe Scanner offre de nombreuses possibilités pour décomposer et interpréter les informations lues. System.err.println("Erreur : Nom de fichier incorrect"); Pour plus de détail il faut consulter la documentation (API). EIA-FR / Jacques Bapst PR1_08 10 EIA-FR / Jacques Bapst PR1_08 12 Java / Entrées / Sorties (I/O) Java / Entrées / Sorties (I/O) Importation des classes I/O Hiérarchie de la classe IOException § La plupart des classes d'entrées/sorties se trouvent dans la librairie (package) java.io (ou év. dans java.nio). § Tout programme qui utilise des entrées/sorties (sauf s'il se limite aux flux standard) devra préalablement importer toutes les classes externes nécessaires. § On utilisera pour cela la pseudo-instruction : import java.io.*; § Cette instruction doit être placée avant la déclaration de la classe qui utilise les entrées/sorties (juste après package …). § Elle a pour effet de rendre visible toutes les classes contenues dans le paquetage java.io (importation sélective également possible). § Pour pouvoir utiliser la classe Scanner il faut également l'importer : import java.util.Scanner; EIA-FR / Jacques Bapst PR1_08 13 Java / Entrées / Sorties (I/O) java.lang.Throwable +-- java.lang.Exception +-- java.io.IOException +-- ChangedCharSetException +-- CharacterCodingException +-- CharConversionException +-- CloseChannelException +-- EOFException +-- FileNotFoundException +-- FileLockInterruptionException +-- MalformedURLException +-- ProtocolException +-- SocketException +-- UnknownHostException +-- UnsupportedEncodingException +-- ZipException +-- . . . EIA-FR / Jacques Bapst Plusieurs de ces classes comportent elles-mêmes des sous-classes PR1_08 15 Java / Entrées / Sorties (I/O) Traitement des exceptions Lecture séquentielle d'un fichier texte § La plupart des opérations d'entrées/sorties risquent de générer des exceptions du type IOException (une sous-classe d'Exception). § Ces exceptions (contrôlées) sont déclarées dans la signature des méthodes concernées et le compilateur impose donc au programmeur de les traiter ou de déclarer leur propagation. § Il faut éviter de mettre des instructions try / catch à chaque ligne. § Grâce au mécanisme de propagation des exceptions, il est possible de concentrer le traitement à un seul endroit (ou à quelques endroits stratégiques judicieusement choisis). § Dans les applications, il faut trouver le bon compromis et placer les traitements d'exception aux endroits où l'on peut prendre les mesures adéquates et/ou informer correctement l'utilisateur. • Pas de message du genre : "Erreur d'entrée/sortie" • Mais plutôt un message clair : "Le fichier C:\Image\Tx1.gif n'existe pas" EIA-FR / Jacques Bapst § La classe IOException comporte un grand nombre de sousclasses correspondant à différents types d'événements qui peuvent survenir lorsqu'on travaille avec les entrées/sorties. PR1_08 14 import java.io.*; public class Pg1 { public static void main(String [] args) { String l, w, filename; BufferedReader f, keyboard; try { keyboard = new BufferedReader(new InputStreamReader(System.in)); System.out.print("Entrer le nom du fichier : "); filename = keyboard.readLine(); f = new BufferedReader(new FileReader(filename)); l = f.readLine(); while (l != null) { if (l.charAt(0)=='#') System.out.println("----------"); else System.out.println(l); l = f.readLine(); } f.close(); } catch (IOException e) { System.err.println("Erreur: " + e); } } } EIA-FR / Jacques Bapst PR1_08 16 Java / Entrées / Sorties (I/O) Java / Entrées / Sorties (I/O) Décomposition (parsing) des données lues Spécification de Scanner Method Summary (Extract) § Les données enregistrées dans des fichiers textes sont généralement structurées et nécessitent une phase d'analyse et de décomposition (parsing) avant de pouvoir les utiliser. boolean Returns true if there is another line in the input of this scanner. boolean String next() Finds and returns the next complete token from this scanner. Classes StringTokenizer et StreamTokenizer Classe Scanner Méthode split() de la classe String Classes Pattern et Matcher (package java.util.regex) § La classe StringTokenizer est la plus ancienne et, si elle est parfois plus performante, elle est cependant moins flexible que Scanner ou split() qui se basent sur des Expressions régulières (regex pattern). Complément Complément hasNext…() Returns true if the next token in this scanner's input can be interpreted as a … § La plateforme Java offre différents outils pour faciliter ce travail, par exemple : • • • • hasNextLine() String next(String pattern) Returns the next token if it matches the pattern constructed from the specified string. boolean nextBoolean() Scans the next token of the input into a boolean value and returns that value. double nextDouble() Scans the next token of the input as a double. int nextInt() Scans the next token of the input as a int. String nextLine() Advances this scanner past the current line and returns the input that was skipped. … next…() Scans the next token of the input as a … Scanner skip(String pattern) Skips input that matches a pattern constructed from the specified string. Scanner useDelimiter(String pattern) Sets this scanner's delimiting pattern to a pattern constructed from the specified String. EIA-FR / Jacques Bapst PR1_08 EIA-FR / Jacques Bapst 17 Java / Entrées / Sorties (I/O) PR1_08 19 Java / Entrées / Sorties (I/O) Spécification de Scanner Spécification de StringTokenizer Constructor Summary (Extract) Constructor Summary Scanner(InputStream source) StringTokenizer(String str) Constructs a new Scanner that produces values scanned from the specified input stream. Constructs a string tokenizer for the specified string. Scanner(Readable source) StringTokenizer(String str, String delim) Constructs a new Scanner that produces values scanned from the specified source Constructs a string tokenizer for the specified string. Scanner(String source) StringTokenizer(String str, String delim, boolean returnDelims) Constructs a new Scanner that produces values scanned from the specified string. Complément Complément Constructs a string tokenizer for the specified string. Method Summary (Extract) String findInLine(String pattern) Attempts to find the next occurrence of a pattern constructed from the specified string, ignoring delimiters. boolean hasNext() Returns true if this scanner has another token in its input. boolean hasNext(String pattern) Returns true if the next token matches the pattern constructed from the specified string. boolean int Calculates the number of times that this tokenizer's nextToken method can be called (before raising an exception). boolean hasNextDouble() hasNextInt() boolean hasMoreTokens() Object nextElement() String nextToken() String nextToken(String delim) Returns the same value as the nextToken method, except that its declared return value is Object rather than String. PR1_08 Returns the next token from this string tokenizer. Returns the next token in this string tokenizer's string. Returns true if the next token in this scanner's input can be interpreted as an int value in the default radix using the nextInt() method. EIA-FR / Jacques Bapst hasMoreElements() Returns the same value as the hasMoreTokens method. Returns true if the next token in this scanner's input can be interpreted as a double value using the nextDouble() method. boolean countTokens() Tests if there are more tokens available from this tokenizer's string. hasNextBoolean() Returns true if the next token in this scanner's input can be interpreted as a boolean value using a case insensitive pattern created from the string "true|false". boolean Method Summary (Extract) 18 EIA-FR / Jacques Bapst PR1_08 20 Java / Entrées / Sorties (I/O) Java / Entrées / Sorties (I/O) Fichiers à accès direct [1] import java.util.StringTokenizer; import java.io.*; public class Pg2 { public static void main(String [] args) { String l, w, filename; StringTokenizer st; BufferedReader f, keyboard; try { keyboard = new BufferedReader(new InputStreamReader(System.in)); System.out.print("Entrer le nom du fichier : "); filename = keyboard.readLine(); f = new BufferedReader(new FileReader(filename)); l = f.readLine(); while (l != null) { st = new StringTokenizer(l); while (st.hasMoreTokens()) { System.out.print("(" + st.nextToken() + ")"); } System.out.println(); l = f.readLine(); } f.close(); } catch (IOException e) { System.err.println("Erreur: " + e); } } } EIA-FR / Jacques Bapst PR1_08 § La classe RandomAccessFile (assez indépendante des autres classes du package java.io) permet de lire et d'écrire dans un fichier à des positions arbitraire (accès direct, accès aléatoire). Complément Complément Utilisation de StringTokenizer § Un curseur (File Pointer) peut être positionné à un emplacement quelconque et les opérations de lecture/écriture auront lieu à partir de cet endroit (après l'opération, le curseur sera automatiquement positionné après le dernier byte lu ou écrit). § Lors de l'ouverture du fichier, on indique le fichier à traiter ainsi que le mode d'accès : • "r" : lecture uniquement • "rw" : lecture et écriture § Si le fichier n'existe pas, il sera créé à l'emplacement indiqué (selon les paramètres du constructeur). EIA-FR / Jacques Bapst 21 Java / Entrées / Sorties (I/O) Fichiers à accès direct [2] § Pour décomposer une chaîne de caractères, il existe également la méthode split() qui se trouve dans la classe String. § Exemple d'utilisation avec un accès en lecture/écriture dans un fichier binaire existant : RandomAccessFile f = new RandomAccessFile("config.dat", "rw"); Complément Complément § Pour le découpage de la chaîne, cette méthode se base sur une expression régulière dont la syntaxe est définie dans la classe Pattern (java.util.regex). str = "Mots, séparés, par, des, virgules"; String[] words = str.split(", "); for (int i=0; i<words.length; i++) { System.out.println(words[i]); } Mots séparés par des virgules EIA-FR / Jacques Bapst 23 Java / Entrées / Sorties (I/O) Méthode split() String PR1_08 PR1_08 22 f.seek(50); byte[] data = new byte[100]; f.read(data); int i = f.readInt(); f.seek(50); f.writeInt(i); f.write(data); f.close(); EIA-FR / Jacques Bapst // // // // // // // // Déplacement vers le byte 50 du fichier Crée un buffer pour contenir les données Lit 100 bytes du fichier Lit un entier codé sur 4 bytes dans le fichier Retour au byte 50 Écrit d'abord l'entier… puis écrit les 100 bytes Fermeture du fichier PR1_08 24 Java / Entrées / Sorties (I/O) Java / Entrées / Sorties (I/O) Fichiers à accès direct [3] Conversions types primitifs ó String Complément § La classe RandomAccessFile est principalement destinée à la manipulation de données binaires car elle n'inclut pas de méthodes de lecture et d'écriture de chaînes de caractères assurant la conversion au format Unicode (seuls les 8 bits de plus faible poids de chaque caractère sont considérés). § Dans les opérations d'entrées/sorties avec des flux de caractères (notamment en lecture), des conversions entre types primitifs et String sont fréquemment nécessaires. § Rappel des méthodes de conversion : Conversions § La méthode readLine()permet de lire une ligne de texte mais ne fonctionne correctement que pour du texte simple (ASCII / 8 bits). en String de String Type primitif Wrapper § La méthode writeBytes()permet d'écrire du texte (String) mais ne fonctionne correctement que pour du texte simple (ASCII / 8 bits). De plus, il faut s'assurer d'ajouter les caractères de terminaison à la fin de chaque ligne. byte Byte toString(byte b) parseByte(String s) short Short toString(short s) parseShort(String s) int Integer toString(int i) parseInt(String s) long Long toString(long l) parseLong(String s) Remarque : Les caractères de terminaison de lignes (EOL) dépendent de la plate-forme d'exécution : float Float toString(float f) parseFloat(String s) double Double toString(double d) parseDouble(String s) System.getProperty("line.separator") EIA-FR / Jacques Bapst PR1_08 D'autres fonctions de conversion sont également disponibles dans la classe String, notamment la méthode valueOf() qui permet de convertir en String les valeurs de tous les types primitifs. 25 EIA-FR / Jacques Bapst Java / Entrées / Sorties (I/O) Utilisation des fonctions de conversion § En Java, un programme peut recevoir des arguments lors de son lancement (des paramètres d'exécution sur la ligne de commande). § Dans le programme on récupère ces paramètres dans un tableau de chaînes de caractères (String[]) transmis à la méthode main(). § Un exemple d'utilisation : public class GetRuntimeParams { public static void main(String[] args) { int float double boolean public class Echo { public static void main(String[] args) { for (int i=0; i<args.length; i++) { System.out.println(args[i]); } if (args.length == 0) System.out.println("No arguments"); } } EIA-FR / Jacques Bapst 27 Java / Entrées / Sorties (I/O) Arguments de la ligne de commande prompt> java Echo hello 10 world hello 10 world PR1_08 prompt> java Echo no arguments } PR1_08 26 } p1 p2 p3 p4 = = = = 0; 0; 0; false; try { p1 = Integer.parseInt(args[0]); p2 = Float.parseFloat(args[1]); p3 = Double.parseDouble(args[2]); p4 = Boolean.valueOf(args[3]).booleanValue(); } catch (Exception e) { System.err.println("Incorrect Runtime Parameters Types or Values"); System.err.println("Should be : int, float, double, boolean"); return; } . . . . . . EIA-FR / Jacques Bapst PR1_08 28 Java / Classes et objets Classes [2] § Une classe peut être considérée comme un moule (ou un plan) permettant de créer des objets qui ont des propriétés similaires mais qui ont tous leur identité propre (indépendance). Informatique / Programmation new Classe Instances Programmation orientée objet avec Java Objet 1 09 : Classes et objets Objet 2 Jacques Bapst [email protected] Objet 3 EIA-FR / Jacques Bapst Java / Classes et objets 3 Java / Classes et objets Classes [1] Propriétés des objets § Une classe est une collection nommée de • de champs (ou attributs) contenant des valeurs • de constructeurs servant à créer les objets • de méthodes définissant des actions (opérations) § Un objet est principalement caractérisé par : • une identité (son nom); doit être univoque dans le contexte d'utilisation • un type (la classe dont il provient, avec les classes ascendantes) • un état (qui est défini par la valeur actuelle de ses champs); l'état peut évoluer dans le temps (chaque objet vit sa vie) • un comportement (qui est défini par l'ensemble de ses méthodes publiques); ensemble des actions et des opérations possibles § La classe est l'élément structurel le plus fondamental de tout programme Java (on ne peut pas écrire de code sans créer au moins une classe). § Les champs et les méthodes sont collectivement nommés membres de la classe : § La programmation orientée objet met les objets au cœur de la conception des applications (contrairement à la programmation • les champs sont les membres données (data members) • les méthodes sont les membres fonctions (function members) procédurale qui centre la conception sur les traitements appliqués aux données). § Chaque classe définit un nouveau type de données qui permettra de créer (d'instancier) des objets de ce type. § Une application orientée objet est constituée d'un ensemble d'objets qui communiquent en s'échangeant des messages (par l'invocation de leurs méthodes). § Les objets créés sont appelés instances de la classe. EIA-FR / Jacques Bapst PR1_09 PR1_09 2 EIA-FR / Jacques Bapst PR1_09 4 Java / Classes et objets Java / Classes et objets Déclaration de classes [1] Déclaration des champs [1] § La syntaxe de base pour la déclaration d'une classe est la suivante : modificateurs class nom_de_la_classe § Les champs appelés aussi attributs (ou membres données) définissent la valeur (l'état) de l'objet. { § La syntaxe de déclaration des champs est proche de la déclaration d'une variable locale : déclaration_de_champs déclaration_de_constructeurs déclaration_de_méthodes Modificateurs Type Nom_du_champ [ = Expression ] ; } § Les modificateurs (liste de mots-clés) seront vus ultérieurement (public pour l'instant). § Les noms de classes sont habituellement écrits avec la première lettre en majuscule et en utilisant la notation MixedCase / CamelCase (transition minuscules/majuscules pour séparer les éléments du nom). § Les modificateurs sont constitués de mots-clés qui déterminent diverses propriétés, notamment la visibilité (public, protected, private). Il seront étudiés dans un prochain chapitre. § Le type peut être soit un type primitif, un tableau ou le nom d'une classe (rappelons que chaque classe définit un type référence). Exemples : Random, StringReader, FileOutputStream EIA-FR / Jacques Bapst PR1_09 5 Java / Classes et objets EIA-FR / Jacques Bapst Déclaration des champs [2] § Exemple de déclaration de classe : § Une expression d'initialisation peut être mentionnée lors de la déclaration d'un champ (= ...). public class Point { } § En l'absence d'expressions d'initialisation, les champs sont automatiquement initialisés à leurs valeurs par défaut (false et 0 pour les types primitifs, null pour les objets et les tableaux) // Coordonnée x du point (champ) // Coordonnée y du point (champ) public Point() { px = 0.0; py = 0.0; } // Constructeur 1 public Point(double x, double y) { px = x; py = y; } // Constructeur 2 // Définition et initialisation des champs public Point center = new Point(0, 0); public double radius = 0.0; public double distanceOrigin() { return Math.sqrt(px*px + py*py); } // Méthode // Suite de la déclaration de la classe Cercle ... ... EIA-FR / Jacques Bapst 7 Java / Classes et objets Déclaration de classes [2] public double px; public double py; PR1_09 public class Circle { } PR1_09 6 EIA-FR / Jacques Bapst PR1_09 8 Java / Classes et objets Java / Classes et objets Méthodes Constructeurs [1] § Les méthodes ont déjà été étudiées dans un chapitre précédent. § Un constructeur est une sorte de méthode qui sera invoquée lors de la création d'un objet de cette classe (opérateur new). § Elles ne sont pas forcément déclarées avec le modificateur static (c'est même plutôt l'exception en programmation objet). Un prochain chapitre sera consacré à approfondir les notions de champs/méthodes statiques et non-statiques. § Un constructeur doit porter le nom de la classe et ne doit pas comporter de type de retour dans sa déclaration (même pas void). § Les méthodes ont accès aux champs de la classe dans laquelle elles sont déclarées. Ces champs sont accessibles par leurs noms simples ou en utilisant le préfixe this (explication de ce mot réservé dans les pages suivantes). § Le but du constructeur est d'initialiser l'objet (notamment la valeur de ses champs). § Un constructeur retourne implicitement une référence à une instance de la classe (objet). § Les méthodes peuvent invoquer d'autres méthodes définies dans la même classe en utilisant leur nom simple ou en utilisant le préfixe this (comme pour les champs). § Si aucun constructeur n'est défini dans une classe, Java fournit un constructeur par défaut, sans argument, et qui se charge uniquement de créer les champs et de leur attribuer une valeur initiale (valeur par défaut ou valeur de l'expression d'initialisation). EIA-FR / Jacques Bapst PR1_09 § Les constructeurs ne sont pas des méthodes à strictement parler même si, par beaucoup d'aspects, ils leur ressemblent. 9 Java / Classes et objets Déclaration et création d'objets § La classe Point permet de déclarer des objets de ce type : § Comme les méthodes, les constructeurs peuvent être surchargés (en suivant les même règles). Point p1; § Pour créer un objet on utilise l'opérateur new suivi du nom de la classe et d'une liste facultative d'arguments entre parenthèses. § Dans la déclaration de la classe Point, le premier constructeur aurait pu être écrit ainsi : EIA-FR / Jacques Bapst // Déclaration d'un objet de type Point § La déclaration ne crée pas un objet mais uniquement une référence vers un objet du type mentionné (comme pour les tableaux). p1 § Lorsqu'une classe possède des constructeurs multiples (surchargés), il est possible, dans le corps d'un constructeur, d'invoquer un autre constructeur, un utilisant la syntaxe spéciale : this(expr1, expr2, ...) Restriction importante : 11 PR1_09 Java / Classes et objets Constructeurs [2] public Point() { this(0.0, 0.0); } EIA-FR / Jacques Bapst new // Constructeur 1 // Invocation du Constructeur 2 nom_de_la_classe (expr1, expr2 , ... new Point(1.2, 3.4) 1.2 px 3.4 py § L'opérateur new fait appel à l'un des constructeurs de la classe en fonction du profil des paramètres transmis (constructeur par défaut ou l'un des constructeurs définis explicitement dans la classe). L'appel à this(...) doit apparaître comme première instruction dans un constructeur. PR1_09 ) 10 EIA-FR / Jacques Bapst PR1_09 12 Java / Classes et objets Java / Classes et objets Création d'objets Utilisation des objets § La classe Point déclarée précédemment permet de déclarer et de créer des objets de ce type : § L'accès aux membres des objets s'effectue en utilisant l'opérateur point (.) selon la syntaxe suivante : Point p1; // Déclaration d'un objet de type Point p1 = new Point(); // Création d'un objet et assignation // de la référence à p1 Point p2 = new Point(); // Déclaration de p2, création d'un // objet Point et assignation de la // référence à p2 Point p3 = new Point(2.0, 4.5); Nom_de_l'objet.Nom_du_membre § Le membre pouvant être soit un champ soit une méthode : Point p1 = new Point(1.0, 2.0); // Déclare et crée un objet p1 // Idem, mais utilisation du deuxième // constructeur pour créer l'objet double vx, vy, d; // Lit un champ de l'objet p1 § Les objets, comme les tableaux, sont des types référence. vx = p1.px; vy = p1.py; p1.px = p1.px + 1.5; § p1, p2 et p3 ne contiennent donc pas les objets mais sont des références vers les zones mémoires contenant les objets. d = p1.distanceOrigin(); // Exécute une méthode de p1 EIA-FR / Jacques Bapst PR1_09 13 Java / Classes et objets EIA-FR / Jacques Bapst PR1_09 15 Java / Classes et objets Représentation en mémoire Référence Complément // Modifie la valeur du champ Notion d'objet [1] § Objet Objet référencé p1 = new Point(2.5, 4.2); p1 1205 1206 1207 1208 1209 2320 2320 2321 2322 2323 2324 Point 2.5 4.2 Nom classe Champ p1.px Champ p1.py Création de l'espace mémoire pour contenir l'objet et initialisation des champs (constructeur) : Entité typée et nommée qui possède des attributs et des méthodes § Attribut : Case mémoire typée et nommée § Méthode : Procédure ou fonction nommée qui a accès aux attributs de l'objet § Pour accéder aux composants (membres) d'un objet il faut : • détenir une variable qui référence cet objet Notation abstraite Référence p1 = new Point(2.5, 4.2); 2.5 px 4.2 py p1 EIA-FR / Jacques Bapst • connaître le nom de l'attribut ou de la méthode Objet référencé • utiliser l'opérateur "." • utilisation comme une variable, resp. une procédure / fonction Notation à utiliser PR1_09 14 EIA-FR / Jacques Bapst PR1_09 16 Java / Classes et objets Java / Classes et objets Notion d'alias Notion d'objet [2] § Exemple : • Type d'objet Car int float • Attributs • Méthodes year speed void fillTank (int liters) double breakDownRisk(int km) Type primitif Objet / Tableau Affectation a = b Copie de la valeur Copie de la référence L'objet n'est pas modifié Comparaison a == b Test sur la valeur Test sur la référence, pas sur le contenu de l'objet ! Passage de la valeur (copie locale) Passage de la référence (copie) L'objet risque d'être modifié ! Passage en paramètre p(a) § Type primitif Car c = new Car(); c c.year = 1999; System.out.println(c.speed); c.fillTank(30); System.out.println(c.breakDownRisk(50)); 1999 year 80.3 speed § Objet / Tableau : accès à la zone mémoire possible par plusieurs variables Car myCar Car otherCar fillTank() breakDownRisk() System.out.println(myCar.year); EIA-FR / Jacques Bapst PR1_09 17 Java / Classes et objets int Engine Type d'objet Engine Attributs char int EIA-FR / Jacques Bapst !!! EIA-FR / Jacques Bapst PR1_09 19 § Le mot clé this est utilisé pour référencer l'objet à l'intérieur duquel le code est exécuté. Pour la déclaration de variables locales Pour la déclaration d'attributs d'un objet Comme paramètres de méthodes Comme résultat d'une fonction Attributs 2014 Référence à l'objet courant : this § Les types référence sont de vrais types Java qui peuvent être utilisés : Car // Java / Classes et objets Notion d'objet [3] Type d'objet = new Car(); = new Car(); myCar.year = 1999; otherCar = myCar; otherCar.year = 2014; Généralement, les méthodes ne sont pas mentionnées dans la représentation de l'état de la mémoire. • • • • : accès à la zone mémoire par une seule variable year motor type power § On peut considérer que l'objet this est implicitement passé en référence lors de l'invocation de chaque méthode d'instance : Point p1 = new Point(1.0, 2.0); double d = p1.distanceOrigin(); 1999 myCar Dans l'exemple ci-dessus, l'objet p1 est implicitement passé en paramètre (invisible) à la méthode distanceOrigin(). A l'intérieur du corps de la méthode distanceOrigin() on peut, si nécessaire, utiliser le mot-clé this pour référencer l'objet sur lequel la méthode a été invoquée (p1 dans cet exemple). year motor 'K' type 86 power PR1_09 Attention : Il ne faut pas confondre this utilisé pour référencer l'objet courant avec la forme this() qui est utilisée pour invoquer un constructeur depuis un autre constructeur. 18 EIA-FR / Jacques Bapst PR1_09 20 Java / Classes et objets Classe : Notation UML § Dans le langage de modélisation UML (Unified Modeling Language), les classes sont représentées graphiquement sous la forme de rectangles comprenant le nom de la classe et éventuellement le nom des champs, des constructeurs et des méthodes. § Différents niveaux de détails sont possibles : Point Point distanceOrigin() Méthodes Champs px py Point + px : double + py : double + Point(x : double, y : double) + distanceOrigin() : double Indicateurs de visibilité : + : public EIA-FR / Jacques Bapst - : private # : protected PR1_09 21 Java / Classes et objets En bref, écrire une classe c'est… § Choisir un nom pour la classe • Intégrer cette classe dans un paquetage § Écrire la spécification (déclarer la partie publique) • Signature des méthodes avec les exceptions prévues • Explication de l'effet attendu (rôle des paramètres, valeur de retour, effet sur les données, pré- et post-conditions) § Écrire une implémentation : • Définition des attributs (choix des structures de données) • Conception du corps des méthodes (choix des algorithmes) • Codage proprement dit (identificateur, flux d'instructions, lisibilité, méthodes privées) § Tester le bon fonctionnement § Éventuellement • Documenter la classe avec /** … */ (Javadoc) • Affiner, optimiser, … EIA-FR / Jacques Bapst PR1_09 22 Java / Packages, contrôle d'accès, encapsulation Paquetages Informatique / Programmation § Un paquetage (Package) est une collection nommée de classes et/ou d'interfaces. Un paquetage peut comprendre des sous-paquetages. § Les paquetages permettent de grouper des classes apparentées et de définir un espace de désignation (Namespace) pour les classes qu'il contient. Programmation orientée objet avec Java § Les paquetages servent également à gérer les droits d'accès (visibilité) des classes les unes par rapport aux autres. 10 : Packages / Contrôle d'accès / Encapsulation § Les paquetages sont organisés de manière hiérarchique (structure arborescente avec racines multiples). § Dans le nom des paquetages, le point ('.') est utilisé comme séparateur entre les différents niveaux hiérarchiques. Jacques Bapst [email protected] § Exemple : java.lang.String : La classe String se trouve dans le sous-paquetage lang du paquetage java EIA-FR / Jacques Bapst Java / Packages, contrôle d'accès, encapsulation 3 Java / Packages, contrôle d'accès, encapsulation Unités de compilation Déclaration de paquetage [1] § Le mot clé package est utilisé pour indiquer le paquetage auquel appartiennent la ou les classes de l'unité de compilation. § Un programme Java est généralement constitué d'un ensemble de classes. § Le code source de chaque classe publique doit être enregistré dans un fichier séparé portant le nom de la classe avec l'extension ".java". Un tel fichier constitue une unité de compilation. § Le résultat de la compilation (Bytecode) est enregistré dans un fichier portant le nom de la classe avec l'extension ".class". package § Exemple : ; package ch.eiafr.tic; public class Point { ... } • La classe Point déclarée précédemment doit être enregistrée dans un fichier nommé "Point.java" § La classe Point fait partie du paquetage ch.eiafr.tic • Après la compilation, un nouveau fichier "Point.class" sera créé, il contiendra le Bytecode correspondant à la classe Point PR1_10 Nom_du_paquetage § Cette pseudo-instruction, si elle est utilisée, doit être la première (hormis les commentaires) de l'unité de compilation (fichier source). § Exemple : EIA-FR / Jacques Bapst PR1_10 § Le nom complet de la classe est "ch.eiafr.tic.Point" 2 EIA-FR / Jacques Bapst PR1_10 4 Java / Packages, contrôle d'accès, encapsulation Java / Packages, contrôle d'accès, encapsulation Déclaration de paquetage [2] Importation de paquetage [2] § Si aucune instruction package n'apparaît dans un fichier source, (ce qui est déconseillé ) les classes définies dans cette unité de compilation seront attribuées à un paquetage par défaut (qui est anonyme). § L'instruction import doit être placée au début de l'unité de compilation, juste après l'instruction package (s'il y en a une). § L'instruction import peut être répétée à volonté pour importer plusieurs classes et/ou plusieurs paquetages. § Les noms des paquetages sont habituellement écrits en utilisant uniquement des minuscules (convention de codage ). § L'importation d'un paquetage entier (....*) ne rend pas visible le contenu des éventuels sous-paquetages. Si nécessaire, les sous-paquetages doivent être importés explicitement. § Pour faciliter l'échange de code Java (librairies publiques), il est utile d'avoir un espace de désignation globalement univoque (pour éviter les conflits dans les noms de classes). § En cas de conflit (importation de paquetages contenant des classes portant le même nom), le compilateur imposera l'utilisation du nom complet pour accéder aux classes homonymes. § C'est pour cela qu'il est recommandé de créer l'arborescence des paquetages en utilisant comme racine, les noms de domaines internet (en inversant les éléments de l' URL, tout en respectant la syntaxe des identificateurs Java !). Exemple : Nom de domaine Racine des paquetages § Le paquetage java.lang étant considéré comme fondamental, il est implicitement importé et l'on peut donc utiliser ses classes par leurs noms simples (par exemple : String, Math, System, ...) sans avoir à les importer. : eia-fr.ch : ch.eiafr EIA-FR / Jacques Bapst PR1_10 EIA-FR / Jacques Bapst 5 Java / Packages, contrôle d'accès, encapsulation Importation statique § Par défaut, une classe d'un paquetage p peut faire référence à toute autre classe de p par son nom simple (le nom de la classe uniquement, par exemple Point). § Afin d'alléger l'écriture, on peut "importer" les classes d'un paquetage externe en utilisant l'instruction import. § Il existe deux formes : • Importation d'une classe individuelle : import ch.eiafr.tic.Line; : import ch.eiafr.tic.*; Complément § Depuis la version 1.5 du langage, il est possible d'importer sélectivement ou globalement les membres statiques d'une classe en ajoutant le mot-clé static à l'instruction import (les membres statiques seront vus en détail au chapitre suivant). § Pour référencer une classe d'un paquetage différent de celui dans lequel on se trouve, il faut utiliser son nom complet (paquetage + classe, par exemple ch.eiafr.tic.Line). § Cette importation statique permet, dans certains cas, d'alléger l'écriture en évitant de devoir préfixer les champs et les méthodes statiques avec le nom du package et/ou de la classe. § Par exemple : import static java.lang.Math.max; importe la méthode statique max() qui pourra donc être invoquée sans préfixe : r = max(v1, v2); § Pour importer tous les membres statiques de la classe Math : § L'instruction import rend accessible, par leurs noms simples, les classes externes mentionnées. EIA-FR / Jacques Bapst 7 Java / Packages, contrôle d'accès, encapsulation Importation de paquetage [1] • Importation de toutes les classes contenues dans un paquetage PR1_10 PR1_10 import static java.lang.Math.*; a = PI * pow(r, 2); 6 EIA-FR / Jacques Bapst PR1_10 8 Java / Packages, contrôle d'accès, encapsulation Java / Packages, contrôle d'accès, encapsulation Structure des unités de compilation Paquetages et unités de compilation [1] § La hiérarchie des répertoires du disque dans lesquels sont enregistrés les fichiers des classes (.class) doit correspondre à la hiérarchie des paquetages. Paquetages package ch.eiafr.tic; public class Point {...} package ch.eiafr.tic.tools; public class Complex {...} public class Graph {...} package ch.eiafr.tic.test; § En résumé, les unités de compilation (fichiers .java") doivent respecter la structure suivante : Répertoires / Fichiers C: | +-ClassesJava | +-ch | +-eiafr | +-tic | +-test | +-tools public class TestPoint {...} EIA-FR / Jacques Bapst package ...; • zéro, une ou plusieurs instructions import import ...; import ...; import ...; • une ou plusieurs définitions de classes (class), d'interfaces ou de classes-internes (mais une seule classe ou interface publique au premier niveau) Racine des paquetages Point.class TestPoint.class Complex.class Graph.class PR1_10 • (zéro ou) une instruction package 9 Java / Packages, contrôle d'accès, encapsulation public class ... { ... } § Ces éléments (package, import, class) peuvent naturellement être entourés de commentaires, mais ils doivent impérativement apparaître dans l'ordre mentionné ci-dessus. EIA-FR / Jacques Bapst PR1_10 Java / Packages, contrôle d'accès, encapsulation Librairie de classes Java Paquetages et unités de compilation [2] § La hiérarchie des fichiers contenant les classes constitue un sousarbre qui peut être placé n'importe où dans l'arborescence générale du disque. § La plate-forme Java comprend une vaste collection de classes prédéfinies qui peuvent être utilisées indépendamment de l'environnement d'exécution des applications. § Le paramètre (ou la variable d'environnement) classpath indique à la machine virtuelle Java, la liste des répertoires (racines) dans lesquels se trouvent les classes. § Cette librairie de classes est organisée en paquetages (et sous-paquetages) qui rassemblent, par domaine, des classes apparentées. java -classpath .;C:\ClassesJava;G:\Info\Lib ... § L'arborescence des paquetages de la plate-forme Java possède deux racines principales java et javax. § A partir de ces racines (points d'entrée), la machine virtuelle complète le chemin d'accès en y ajoutant les sous-répertoires définis par l'arborescence des paquetages. § Les pages qui suivent donnent un aperçu succinct (quelques paquetages importants) de la plate-forme Java. Conseil : Inutile de réinventer la roue. Avant de coder une fonction particulière, il faut s'assurer qu'elle n'est pas disponible dans la bibliothèque des classes standard. Remarque : L'ensemble des fichiers .class peut également être enregistré dans un seul fichier .jar qui contient toutes les classes et leur arborescence sous forme éventuellement comprimée (format ZIP). EIA-FR / Jacques Bapst 11 PR1_10 10 EIA-FR / Jacques Bapst PR1_10 12 Java / Packages, contrôle d'accès, encapsulation Java / Packages, contrôle d'accès, encapsulation Librairie de classes Java [3] java.lang Le noyau des classes du langage, comprenant notamment String, Math, System, Thread et Exception. Ce package est implicitement importé. java.io Classes et interfaces d'entrée/sorties. Bien que certaines des classes de ce paquetage soient conçues pour travailler directement avec les fichiers, la plupart d'entre-elles permet de travailler avec des flux d'octets ou des flux de caractères. java.math Petit paquetage qui contient des classes destinées à l'arithmétique entière de précision arbitraire et à l'arithmétique décimale. javax.swing Grand paquetage (comportant de nombreux souspaquetages) destiné à étendre AWT pour la création d'interfaces utilisateur graphiques (GUI) sophistiquées. Introduit de nouveaux composants graphiques avec des propriétés beaucoup plus étendues qu'AWT. javax.swing.event Complète et améliore java.awt.event avec des classes spécialisées pour les composants Swing. § Une description de l'ensemble des classes (et interfaces) contenues dans ces paquetages accompagne le JDK (Java Development Kit). La documentation est à télécharger séparément. java.net Classes et interfaces destinées à l'interconnexion avec d'autres systèmes (réseau). java.security Classes et interfaces de contrôle d'accès et d'authentification. Plusieurs sous-paquetages. § Elle peut être consultée en ligne sur le site d'Oracle : http://docs.oracle.com/javase/8/docs/api/ java.util Diverses classes utilitaires y compris un cadre de collections puissant, à utiliser avec les collections d'objets § Les environnements de développement (Eclipse, NetBeans, …) permettent généralement également de consulter la description des API de la plateforme Java (voir documentation correspondante). EIA-FR / Jacques Bapst PR1_10 13 Java / Packages, contrôle d'accès, encapsulation PR1_10 15 Contrôle d'accès aux membres java.applet Définit la classe Applet qui est la super-classe de toutes les applets. javax.accessibility Ensemble de classes et d'interfaces permettant la réalisation d'applications adaptées à des personnes handicapées (par exemple mal-voyants) java.awt Définit le cadre de l'interface utilisateur graphique de base AWT (Abstract Windowing Toolkit). Contient également les classes permettant de gérer des graphiques 2D. java.awt.event Définit les classes et les interfaces utilisées pour la gestion des événements en AWT et Swing. java.awt.image Ensemble de classes et d'interfaces servant à manipuler les images. java.awt.print Ensemble de classes et d'interfaces destinées à l'impression de documents. EIA-FR / Jacques Bapst EIA-FR / Jacques Bapst Java / Packages, contrôle d'accès, encapsulation Librairie de classes Java [2] Complément Complément Complément Librairie de classes Java [1] PR1_10 § Tous les champs et toutes les méthodes d'une classe peuvent être utilisés dans le corps de la classe elle-même en utilisant leurs noms simples. § Cependant, le langage Java permet de spécifier des restrictions d'accès aux membres d'une classe (champs et méthodes) en dehors de cette classe (appelée classe de définition ou classe de déclaration). § Le contrôle d'accès aux membres s'effectue à l'aide de différents mots-clés qui peuvent précéder la déclaration des champs et des méthodes (ces mots-clés font partie d'un groupe de mots réservés appelés modificateurs). § Le mode d'accès par défaut (si l'on n'indique aucun mot-clé de contrôle d'accès dans la déclaration) est appelé "package" ou parfois "friendly" (mais ces termes ne peuvent pas être utilisés comme modificateurs dans le code). 14 EIA-FR / Jacques Bapst PR1_10 16 Java / Packages, contrôle d'accès, encapsulation Java / Packages, contrôle d'accès, encapsulation Modificateurs d'accès [1] Modificateur d'accès private § Si, dans une classe A, on déclare un objet k de type A (ou si une méthode de la classe A reçoit en paramètre un objet k de type A) alors les membres privés de l'objet k sont accessibles à l'intérieur de la classe A. § Les modificateurs qui contrôlent l'accès aux membres ( champs ou méthodes) sont les suivants : Modificateur Description des droits d'accès sur le membre public Accessible à toutes les classes de tous les paquetages protected Accessible aux classes dérivées (sous-classes) ainsi qu'aux classes du même paquetage Sera décrit plus en détail dans le cadre de l'héritage Aucun (ð package) Accessible à toutes les classes du même paquetage private Accessible seulement aux autres membres de la même classe (classe de définition) § Autrement dit, dans une classe A, on peut accéder aux membres privés de tous les objets de type A (pas seulement ceux de this). public class A { ... private int x; private A c; ... public void m1(A k) { k.x = this.x + 2; System.out.println(k.x); } § Le modificateur public peut également être utilisé dans la déclaration des classes. Il indique que la classe est accessible partout où le paquetage est accessible (par import ...). Sans lui, la classe n'est accessible qu'au sein de son paquetage. EIA-FR / Jacques Bapst PR1_10 ... public void m2() { x += c.x; } ... 17 EIA-FR / Jacques Bapst PR1_10 19 Java / Packages, contrôle d'accès, encapsulation Modificateurs d'accès [2] Encapsulation [1] § L'une des techniques essentielles de la programmation orientée objet est l'encapsulation. § Les règles d'accès aux membres peuvent également être présentées sous la forme suivante : § L'encapsulation consiste à masquer les données au sein des classes et à manipuler leur contenu qu'au moyen de méthodes publiques. public protected package private Classe de définition Oui Oui Oui Oui Classe du même paquetage Oui Oui Oui Non Sous-classe dans un paquetage différent Oui Oui Non Non Classe (qui n'est pas une sous-classe) dans un paquetage différent Oui § La mise en place du mécanisme d'encapsulation s'effectue en utilisant judicieusement les contrôles d'accès aux membres des classes. § Avantages de l'encapsulation : Non Non Non • Masquage des détails de l'implémentation (qui peut être changée sans conséquences pour les utilisateurs externes) • Protection de la classe contre des utilisations erronées (contrôles de cohérence dans les méthodes) • Simplification de l'API (les champs et les méthodes internes sont cachés) Par défaut EIA-FR / Jacques Bapst // c.x est accessible } Java / Packages, contrôle d'accès, encapsulation Accessible à : // k.x est accessible PR1_10 18 EIA-FR / Jacques Bapst PR1_10 20 Java / Packages, contrôle d'accès, encapsulation Encapsulation [2] § En règle générale, il est recommandé de masquer les champs en les déclarant private et - seulement si nécessaire - de définir des méthodes publiques pour accéder à ces champs (ces méthodes appelées accesseurs/mutateurs ou getters/setters sont généralement nommées getxxx (), isxxx() et setxxx()). § Version encapsulée de la classe Point : public class Point { private double px; private double py; } // Coordonnée x du point (champ) // Coordonnée y du point (champ) public Point() {...} public Point(double x, double y) {...} // Constructeur 1 // Constructeur 2 public double getPx() {return px;} public double getPy() {return py;} // Get px // Get py public void setPx(double x) {px = x;} public void setPy(double y) {py = y;} // Set px // Set py (nécessaire ?) (nécessaire ?) public double distanceOrigin() {...} EIA-FR / Jacques Bapst PR1_10 21 Java / Membres statiques, initialiseurs, finaliseurs, wrappers Membres de classe [1] Informatique / Programmation § On peut également créer des membres associés à la classe qui sont appelés membres de classe ou membres statiques. § Pour rendre un membre statique, on utilise le mot-clé (modificateur) static devant la déclaration du champ ou de la méthode : public class Point { Programmation orientée objet avec Java public public public public static 11 : Membres statiques, initialiseurs, wrappers … double double int int px; py; pNr; nextNr = 1; // // // // public Point(double x, double y) { Champ Champ Champ Champ d'instance d'instance d'instance statique // Constructeur . . . pNr = nextNr++; Jacques Bapst } public double distanceOrigin() {...} [email protected] } // Méthode d'instance // Méthode statique public static double distance(Point p1, Point p2) {...} EIA-FR / Jacques Bapst Java / Membres statiques, initialiseurs, finaliseurs, wrappers PR1_11 Java / Membres statiques, initialiseurs, finaliseurs, wrappers Membres d'instance Membres de classe [2] § Les membres de classe ne sont pas associés aux instances (objets) mais seulement à la classe. Ils sont accessibles par toutes les instances de la classe (ils sont partagés par toutes les instances). § Par défaut, lors de la création d'un objet (instanciation d'une classe avec l'opérateur new), les membres (champs et méthodes) sont associés à chacune des instances de la classe. On les appelle membres d'instance. Classe Classe new 3 new Instances Instances Point : p1 Point Point : p1 Point px py distanceOrigin() px py pNr nextNr px = 1.0 py = 2.0 distanceOrigin() Point : p2 px = 3.0 py = 1.5 distanceOrigin() distance(Point, Point) PR1_11 distanceOrigin() Point : p2 px = 3.0 py = 1.5 pNr = 2 distanceOrigin() distanceOrigin() EIA-FR / Jacques Bapst px = 1.0 py = 2.0 pNr = 1 2 EIA-FR / Jacques Bapst PR1_11 4 Java / Membres statiques, initialiseurs, finaliseurs, wrappers Java / Membres statiques, initialiseurs, finaliseurs, wrappers Membres de classe [3] Importation statique § Il est possible d'importer sélectivement ou globalement les membres statiques d'une classe en ajoutant le mot-clé static à l'instruction import. § On peut donc déclarer : - des champs statiques (ou champs de classe) - des méthodes statiques (ou méthodes de classe) Complément § Les champs statiques sont enregistrés au niveau de la classe et ils peuvent être accédés et manipulés par toutes les instances (objets) de cette classe. § Indépendamment du nombre d'objets créés, il n'existe qu'un seul exemplaire des données associées aux champs statiques (contrairement aux champs d'instance dont il existe un exemplaire pour chaque objet créé). PR1_11 § Par exemple : import static java.lang.Math.max; importe la méthode statique max() qui pourra donc être invoquée sans préfixe : r = max(v1, v2); § Pour importer tous les membres statiques de la classe Math : § On peut considérer les champs statiques comme étant des variables globales partagées par toutes les instances. Il faut donc les utiliser avec précaution et parcimonie afin d'éviter des couplages inutiles (ou même dangereux) entre objets. EIA-FR / Jacques Bapst § Cette importation statique permet, dans certains cas, d'alléger l'écriture en évitant de devoir préfixer les champs et les méthodes statiques avec le nom du package et/ou de la classe. import static java.lang.Math.*; a = PI * pow(r, 2); 5 Java / Membres statiques, initialiseurs, finaliseurs, wrappers EIA-FR / Jacques Bapst PR1_11 Java / Membres statiques, initialiseurs, finaliseurs, wrappers Représentation en mémoire Membres de classe [4] § Les méthodes statiques sont liées à une classe et non pas à une instance (objet) de la classe. § Les champs statiques existent et sont accessibles même si l'on n'a pas créé d'objets. § Dans une méthode statique, on ne peut pas faire référence à une méthode d'instance sans créer d'objet, car les méthodes statiques ne s'exécutent pas dans le contexte d'un objet (autrement dit, pour les méthodes statiques, il n'existe pas de référence this). § On les représentera donc dans une zone mémoire liée à la classe et commune à toutes les instances de cette classe. p1 = new Point(2.5, 4.2); p2 = new Point(5.0, 6.0); § Pour accéder à un membre statique d'une classe en dehors de cette classe, il faut préfixer le nom du membre (champ ou méthode) avec le nom de la classe (ou utiliser import static…). Point Point int double double p1 p2 n d1 d2 = = = = = new Point(1.0, 2.0); new Point(2.0, 3.0); Point.nextNr; Point.distance(p1, p2); p1.distanceOrigine(); EIA-FR / Jacques Bapst 7 = Point.nextNr; n = p1.nextNr; Bien que déconseillée, cette notation est acceptée par le langage. Le compilateur la remplacera par n = Point.nextNr; // Champ statique // Méthode statique // Méthode d'instance PR1_11 n Le champ statique nextNr est associé à la classe Point et est commun à toutes ses instances 6 EIA-FR / Jacques Bapst p1 p2 Point 2.5 px 4.2 py 1 pNr 5.0 px 6.0 py 2 pNr 3 nextNr PR1_11 8 Java / Membres statiques, initialiseurs, finaliseurs, wrappers Java / Membres statiques, initialiseurs, finaliseurs, wrappers Modificateur final Initialiseur statique § Le modificateur final peut être utilisé lors de la déclaration de variables locales, de paramètres de méthodes et de champs. § Exemple d'initialisation statique : //-------------------------------------------------------// Calcule à l'avance et mémorise certaines valeurs // trigonométriques dans un tableau //-------------------------------------------------------public class Sinusoid { § Il indique que la valeur de la variable ou du champ ne peut plus être modifiée après l'affectation initiale. Cela revient donc à déclarer des constantes. private static int nbPoints = 500; public static double[] valSine = new double[nbPoints]; § Il est très fréquent de déclarer les constantes générales comme champs statiques au niveau de la classe et de les déclarer public. // Initialisation du tableau valSine static { double x = 0.0; double dx = (Math.PI/2)/(nbPoints-1); Exemple : public static final double PI = 3.141592653589793; § Les champs déclarés constants (static final) sont habituellement écrits en majuscules et utilisent le caractère sous-ligné '_' comme séparateur. for(int i=0; i<nbPoints; i++, x+=dx) { valSine[i] = Math.sin(x); } Exemple : private static final double FREQUENCY_MAX = 3.5E9; } ... § Pour les classes et les méthodes, une autre utilisation du mot-clé final sera vue dans le cadre de l'héritage. EIA-FR / Jacques Bapst PR1_11 } EIA-FR / Jacques Bapst 9 Java / Membres statiques, initialiseurs, finaliseurs, wrappers 11 Java / Membres statiques, initialiseurs, finaliseurs, wrappers Initialisation de classes Initialiseur d'instance [1] § Le langage Java permet d'écrire du code d'initialisation de classe qui sera exécuté lors de l'élaboration de la classe (en même temps que l'initialisation des champs statiques). § Les classes peuvent également posséder des initialiseurs d'instance. § Un initialiseur d'instance initialise un objet (contrairement à l'initialiseur statique qui initialise une classe) Complément § Ce code appelé initialiseur statique est simplement constitué d'un bloc d'instructions (entre accolades) précédé du mot-clé static. § Une classe peut posséder un nombre quelconque d'initialiseurs statiques qui peuvent être placés partout où une déclaration de champ ou une déclaration de méthode est permise (ils seront exécutés dans ordre d'écriture du code dans le fichier source ). § Les initialiseurs statiques peuvent être considérés comme des méthodes statiques anonymes. PR1_11 § Un initialiseur d'instance est constitué d'un bloc d'instructions entre accolades (il peut y en avoir plusieurs dans une classe, ils seront exécutés dans l'ordre d'écriture du code dans le fichier source). § Les initialiseurs d'instance sont exécutés après le constructeur de la classe parente et avant le constructeur de la classe dans laquelle ils sont déclarés. Conseil : Il est généralement préférable d'effectuer les initialisations de l'objet dans le constructeur de la classe. § Les restrictions définies pour les méthodes statiques s'appliquent également aux instructions des initialiseurs statiques. EIA-FR / Jacques Bapst PR1_11 10 EIA-FR / Jacques Bapst PR1_11 12 Java / Membres statiques, initialiseurs, finaliseurs, wrappers Java / Membres statiques, initialiseurs, finaliseurs, wrappers Initialiseur d'instance [2] Wrappers [1] § Exemple d'initialiseur d'instance : § En Java, pour des raisons d'efficacité, les types primitifs ne sont pas des objets. Complément public class ... { ... private static int dim = 1000; private int[] intArr = new int[dim]; } § Dans certaines circonstances, il peut être utile de pouvoir traiter les types primitifs comme des objets (par exemple pour pouvoir les enregistrer dans des structures de données abstraites de type liste, pile, arbre, … ne manipulant généralement que des objets). // intArr initialization { for(int i=0; i<dim; i++) { intArr[i] = i; } } ... § Ainsi, il existe pour chaque type primitif, une classe Wrapper (classe d'emballage, classe enveloppe) qui permet de convertir (d'emballer) une variable (ou une valeur littérale) de type primitif en un objet correspondant (une instance d'une des classes d'emballage). § Les classes Wrapper sont déclarées dans le paquetage java.lang (et sont donc accessibles sans importation explicite). § Les classes Wrapper disposent d'un certain nombre de constantes et de méthodes (statiques et non-statiques) permettant d'effectuer diverses conversions. EIA-FR / Jacques Bapst PR1_11 13 Java / Membres statiques, initialiseurs, finaliseurs, wrappers EIA-FR / Jacques Bapst Wrappers [2] § Un finaliseur représente l'opposé d'un constructeur et exécute du code de finalisation de l'objet. § Liste des classes d'emballage (Wrappers) : § Le ramasse-miettes (garbage-collector) se charge de récupérer automatiquement l'espace mémoire utilisé par les objets ( dès que ces derniers ne sont plus référencés). Le programmeur n'a pas à se soucier de cet aspect. Complément 15 Java / Membres statiques, initialiseurs, finaliseurs, wrappers Finaliseur d'objet : finalize § Cependant, si l'objet contient d'autres types de ressources ( fichiers ouverts, connexions réseau, transactions dans une base de données, etc.) il peut être nécessaire d'écrire du code de finalisation qui devra s'exécuter avant que l'objet disparaisse. § Un finaliseur est une méthode d'instance (héritée de Object) - dont le nom est finalize() - qui ne possède aucun paramètre - qui ne retourne aucune valeur (void) PR1_11 Type primitif Classe Wrapper byte Byte short Short int Integer long Long float Float double Double char Character boolean Boolean (Et non pas Int !) (Et non pas Char !) § A deux exceptions près, le nom de la classe Wrapper correspond à celui du type primitif avec une majuscule initiale. § La méthode finalize() (si elle a été définie) sera invoquée automatiquement par la machine virtuelle avant que le ramassemiettes ne détruise l'objet. EIA-FR / Jacques Bapst PR1_11 § Les classes Wrapper créent des objets immuables ! 14 EIA-FR / Jacques Bapst PR1_11 16 Java / Membres statiques, initialiseurs, finaliseurs, wrappers Wrappers [3] § Exemples d'utilisation des classes Wrapper : String str = "1.23"; float primF = 3.456F; Float objF; primF = Float.MAX_VALUE; primF = Float.MIN_VALUE; // Plus grande valeur positive de type float // Plus petite valeur positive de type float objF primF objF objF str str primF // // // // // // // = = = = = = = new Float(primF); objF.floatValue(); new Float("2.34"); Float.valueOf(str); objF.toString(); Float.toString(primF); Float.parseFloat(str); Conversion Conversion Conversion Conversion Conversion Conversion Conversion float Float String String Float float String -> -> -> -> -> -> -> Float float Float Float String String float Ces exemples, donnés pour les types float et Float, s'appliquent par analogie aux autres types primitifs (avec quelques légères différences pour les types boolean et char). EIA-FR / Jacques Bapst PR1_11 17 Java / Membres statiques, initialiseurs, finaliseurs, wrappers Autoboxing / Unboxing § Un mécanisme de conversions automatiques entre types primitifs et classes Wrapper a été introduit dans le langage. Complément § Lorsque c'est possible, le compilateur génère automatiquement le code de conversion entre les variables de types primitifs et les objets des classes Wrapper : Type primitif à Classe Wrapper [ Autoboxing ] Classe Wrapper à Type primitif [ Unboxing ] ... int i = 12, j, r; Integer objA = new Integer(34), objB; objB = i; j = objA; r = ++objA + i; ... EIA-FR / Jacques Bapst // Autoboxing // Unboxing PR1_11 18 Java / Sous-classes, héritage et polymorphisme Arborescence de classes Informatique / Programmation § L'héritage induit une relation arborescente entre les classes. A En notation UML, le symbole signifie "est une sous-classe de" "hérite de" B Programmation orientée objet avec Java 12 : Sous-classes, héritage et polymorphisme C - La classe A - La classe B - La classe B - Les classes Jacques Bapst [email protected] D est la classe parente (super-classe) de B est la classe parente de C et D est une sous-classe de A C et D sont des sous-classes de B B joue plusieurs rôles EIA-FR / Jacques Bapst Java / Sous-classes, héritage et polymorphisme 3 Java / Sous-classes, héritage et polymorphisme Sous-classe et héritage Exemple : classe Vehicule § L'héritage est une propriété essentielle de la programmation orientée objet. § Pour illustrer le concept, prenons l'exemple d'une classe Vehicule permettant de décrire (très sommairement) les propriétés et les comportements d'un véhicule du monde réel. § Ce mécanisme permet d'ajouter des fonctionnalités à une classe (une spécialisation) en créant une sous-classe qui hérite des propriétés de la classe parente et à laquelle on peut ajouter des propriétés nouvelles. § Avec une modélisation très simpliste, la classe pourrait être représentée de la manière suivante : § La classe qui hérite est appelée sous-classe (ou classe dérivée) de la classe parente (qui est également appelée super-classe). Vehicule marque couleur vitesse etat § L'héritage permet à une sous-classe d'étendre les propriétés de la classe parente tout en héritant des champs (attributs) et des méthodes (comportement) de cette classe parente. demarrer() arreter() accelerer() freiner() § En Java, une classe ne peut hériter que d'une seule classe parente. On parle dans ce cas d'héritage simple (par opposition à l'héritage multiple qui permet à une classe d'hériter de plusieurs classes parentes). EIA-FR / Jacques Bapst PR1_12 PR1_12 2 EIA-FR / Jacques Bapst PR1_12 4 Java / Sous-classes, héritage et polymorphisme Java / Sous-classes, héritage et polymorphisme Implémentation classe Vehicule Sous-classes Voiture et Camion public class Vehicule { private private private private String String double int marque; couleur; vitesse; etat; § La classe Voiture est une sous-classe de Vehicule. Elle hérite de tous les attributs et méthodes de Vehicule (marque, couleur, ..., freiner()) en y ajoutant deux nouveaux attributs (modele et nbPortes). // Vitesse actuelle // 0:arrêt, 1:marche, ... public Vehicule(String marque, String couleur) { this.marque = marque; this.couleur = couleur; vitesse = 0.0; etat = 0; } § La classe Camion est également une sous-classe de Vehicule et hérite de tous ses attributs et méthodes. La classe Camion étend la classe parente (Vehicule) en y ajoutant deux nouveaux attributs (chargeMax et poidsChargement) ainsi que deux nouvelles méthodes (charger() et decharger()) . public void demarrer() { etat = 1; } public void arreter() { if (vitesse == 0) etat = 0; } § Dans les sous-classes Voiture et Camion, on peut utiliser les attributs et les méthodes héritées (par exemple couleur ou freiner()) comme si ces membres avaient été déclarés dans chacune des sous-classes (sous réserve, naturellement, que les droits d'accès le permettent). public void accelerer() { if (etat == 1) vitesse += 5.0; } } public void freiner() { if (vitesse >= 5.0) vitesse -= 5.0; else vitesse = 0.0; } EIA-FR / Jacques Bapst PR1_12 5 EIA-FR / Jacques Bapst Java / Sous-classes et héritage Voiture modele nbPortes Déclaration de sous-classe § La déclaration d'une sous-classe s'effectue en utilisant le mot-clé extends suivi du nom de la classe parente : Vehicule marque couleur vitesse etat demarrer() arreter() accelerer() freiner() Dans le langage UML, un tel diagramme est appelé "Diagramme de classes". public class Voiture extends Vehicule { Il montre les relations entre les classes d'une application private String modele; private int nbPortes; // Nouveau champ // Nouveau champ public Voiture(String String String int // Constructeur marque, modele, couleur, nbPortes) { super(marque, couleur); Camion // Appelle le constructeur de la classe parente this.modele = modele; this.nbPortes = nbPortes; chargeMax poidsChargement } charger() decharger() EIA-FR / Jacques Bapst 7 Java / Sous-classes, héritage et polymorphisme Sous-classes de Vehicule A partir de la classe Vehicule on peut, en utilisant l'héritage, créer de nouvelles classes (Voiture et Camion) qui spécialisent la classe Vehicule en y ajoutant de nouvelles propriétés. PR1_12 } PR1_12 6 EIA-FR / Jacques Bapst PR1_12 8 Java / Sous-classes, héritage et polymorphisme Java / Sous-classes, héritage et polymorphisme Relations entre classes [1] Relations entre classes [2] § L'héritage entre une sous-classe et sa classe parente est caractérisé par une relation de type "Est un..." "Est une sorte de..." ou § Il existe une autre relation importante qui peut exister entre deux classes. Il s'agit de la relation de composition (ou agrégation) qui est caractérisée par une relation de type ("Is a...") ("Is kind of...") "A un..." "Possède un..." "Est composé de..." "Contient..." ou ou ou • Une voiture est un véhicule • Un camion est un véhicule Attention : l'inverse n'est pas forcément vrai ! • Une voiture possède un moteur • Une voiture a un propriétaire § Une sous-classe crée une spécialisation de la classe parente. A l'inverse, on parle de généralisation lorsqu'on passe des sousclasses à leur classe parente. ("Has a...") "A un..." ð Composition ð Agrégation § En Java, les relations de composition sont réalisées en créant dans la classe "contenant" une référence vers un objet de la classe "contenu". § Point à retenir lors de la conception d'une application : public class Voiture extends Vehicule { private Personne propriétaire; private FuelMotor moteur; "Est un..." ð Héritage . . . EIA-FR / Jacques Bapst EIA-FR / Jacques Bapst 9 PR1_12 Java / Sous-classes, héritage et polymorphisme PR1_12 11 Java / Sous-classes, héritage et polymorphisme Généralisation / Spécialisation Relations entre classes [3] § En notation UML, la relation "A un", "Possède un", … est représentée de la manière suivante : Voiture Roman Film BD Opera Complément Livre Généralisation Spécialisation Oeuvre Opera-Rock Agrégation Composition 1 4 Propriétaire Roue § La distinction sémantique entre composition et agrégation ne se traduit pas (en Java) par des différences d'implémentation. public class Voiture extends Vehicule { . . . private Personne private Roue[] leProprietaire; lesRoues; . . . } EIA-FR / Jacques Bapst PR1_12 10 EIA-FR / Jacques Bapst PR1_12 12 Java / Sous-classes, héritage et polymorphisme Java / Sous-classes, héritage et polymorphisme Généralisation Classe Object Important ! § La relation d'héritage ("Est un...") permet de traiter les objets des sous-classes comme s'ils étaient des objets de leur classe parente (par généralisation). Camion § En Java, chaque classe que l'on crée possède une classe parente. c1 = new Camion(...); Vehicule v1 = c1; Camion c2 = v1; Camion c3 = (Camion)v1; // Ok, un camion est un véhicule // ERREUR, un véhicule n'est pas forcément un camion ! // Ok, v1 référence effectivement un camion § Une conversion explicite (transtypage, casting) d'un objet de la classe parente vers un objet de la sous-classe (Downcasting) est possible si l'instance à convertir référence effectivement ( au moment de l'exécution) un objet de la sous-classe considérée (sinon, il y aura une erreur ClassCastException à l'exécution). PR1_12 § La classe Object est donc l'ancêtre de toutes les classes Java (c'est la racine unique de l'arbre des classes). § La classe Object est la seule classe Java qui ne possède pas de classe parente. § Si nécessaire, le système effectue donc une conversion élargissante (automatique) de la sous-classe vers la classe parente (Upcasting). EIA-FR / Jacques Bapst § Si l'on ne définit pas explicitement une classe parente (avec extends), la super-classe par défaut est Object (qui est déclarée dans le paquetage java.lang). 13 Java / Sous-classes, héritage et polymorphisme § Toutes les classes héritent donc des méthodes de la classe Object (par exemple toString(), equals(), finalize(), etc). § La classe Object constitue la généralisation ultime : Tous les objets sont des Object ! EIA-FR / Jacques Bapst 15 Java / Sous-classes, héritage et polymorphisme Opérateur instanceof Chaînage des constructeurs § Un constructeur d'une sous-classe peut faire appel à un constructeur de la classe parente en utilisant le mot réservé super selon la syntaxe suivante : super(Expr1, Expr2, ...); § L'opérateur instanceof permet de tester (à l'exécution) l'appartenance d'un objet à une classe (ou une interface) donnée. § Dans l'exemple précédent, on aurait pu écrire : if (v1 instanceof Camion) { c3 = (Camion)v1; } else { ... // v1 ne référence pas un Camion } § Si un constructeur d'une sous-classe invoque explicitement un constructeur de la classe parente, l'instruction super( ...) doit être la première instruction du constructeur. § Si l'on ne fait pas explicitement appel à un constructeur de la classe parente, une invocation du constructeur par défaut de la super-classe (constructeur sans paramètre) sera automatiquement ajoutée (comme première instruction). Si un tel constructeur n'existe pas dans la classe parente, une erreur sera générée à la compilation. Un tel test permet d'éviter une erreur fatale (à l'exécution) si la variable v1 ne référence pas un objet de type Camion. Remarque 1 : (v1 instanceof Vehicule) est également vrai ! Remarque 2 : L'upcasting et le downcasting n'engendrent aucune opération ni changement en mémoire (c'est une autre "vue" de l'objet) EIA-FR / Jacques Bapst PR1_12 PR1_12 14 § Le langage garantit ainsi que tous les éléments des classes parentes aient été élaborés avant la création d'un objet de la classe considérée. EIA-FR / Jacques Bapst PR1_12 16 Java / Sous-classes, héritage et polymorphisme Java / Sous-classes, héritage et polymorphisme Masquage des champs Redéfinition des méthodes [2] § Les classes Polygone et Rectangle définissent toutes deux une méthode area() avec une signature identique. Complément § Si une sous-classe définit un champ avec le même nom qu'un champ de sa classe parente (une pratique à éviter), alors le champ de la super-classe est masqué dans le corps de la sous-classe. § La méthode invoquée par un appel obj.area() dépendra du type de l'objet référencé par la variable obj lors de l'exécution de cette instruction. § Dans ce cas, on peut, dans le corps de la sous-classe accéder au champ de la classe parente en utilisant le mot-clé super suivi d'un point et du nom du champ. § C'est toujours la méthode associée au type effectif de l'objet référencé qui est exécutée, même si l'objet est enregistré dans une variable déclarée avec le type d'une classe parente : § Si les classes A et B définissent toutes deux un champ x et que B est une sous-classe de A alors, dans les méthodes de B, on peut utiliser : x Champ x de la classe B this.x super.x ((A)this).x Champ x de la classe B Champ x de la classe A Champ x de la classe A (par transtypage) Rectangle r = new Rectangle(2.6, 5.3); Polygon La notation super.super.x n'est pas autorisée, seul le casting (((A)c).x) permet d'accéder à un champ masqué d'une classe ancêtre. p = r; double s = p.area(); § Les champs statiques peuvent également être masqués mais ils restent accessibles en les préfixant avec le nom de la classe. EIA-FR / Jacques Bapst PR1_12 17 Java / Sous-classes, héritage et polymorphisme Rectangle EIA-FR / Jacques Bapst EIA-FR / Jacques Bapst PR1_12 19 Redéfinition des méthodes [3] § Lorsqu'une classe définit une méthode d'instance en utilisant la même signature (même nom, type de retour [év. une sous-classe], paramètres, etc.) qu'une méthode de sa classe parente, cette méthode redéfinit (overrides) la méthode de sa super-classe. Polygon // Invoque r.area() et non pas area() de la // classe Polygon car la variable p référence // un objet de type Rectangle Java / Sous-classes, héritage et polymorphisme Redéfinition des méthodes [1] § Par exemple : // Conversion élargissante (Upcasting) § Dans la sous-classe Rectangle, il est possible d'invoquer la méthode area() de la classe parente Polygon en utilisant la notation suivante : super.area() Remarque : La notation super.super.f() n'est pas autorisée et il est impossible d'accéder à une méthode masquée d'une classe ancêtre (autre que la classe parente). public class Polygon { . . . public double area() { . . . // Calcul de la surface d'un polygone quelconque } . . . } § A l'extérieur de la classe de déclaration, il n'est, par contre, pas possible pour un objet de type Rectangle, d'invoquer la méthode area de la classe Polygon. public class Rectangle extends Polygon { . . . public double area() { . . . // Calcul de la surface du rectangle (longueur*largeur) } . . . } PR1_12 § Attention à bien distinguer (et ne pas confondre) les notions de surcharge (overloading) et de redéfinition (overriding) de méthodes. 18 EIA-FR / Jacques Bapst PR1_12 20 Java / Sous-classes, héritage et polymorphisme Java / Sous-classes, héritage et polymorphisme Redéfinition des méthodes [4] Redéfinition des méthodes [6] § L’annotation @Override peut être utilisée pour indiquer explicitement l’intention de redéfinir une méthode. § Si une méthode est redéfinie dans une sous-classe, le type de retour doit être identique à celui de la méthode correspondante de la classe parente. • Si on se trompe en écrivant le nom de la méthode lors de la redéfinition, le compilateur peut alors informer le programmeur de son erreur. Complément Complément § Bien qu’étant facultative, elle apporte deux avantages : • La redéfinition étant explicite, le code est plus clair. @Override public double area() { … } § En redéfinissant une méthode, il est possible d'étendre sa zone de visibilité (private à package à protected à public) mais non de la restreindre. § Une méthode redéfinie ne peut pas générer plus d'exceptions contrôlées que celles qui sont déclarées dans la méthode de la classe parente (mais elle peut en déclarer moins). § Éviter de : • surcharger une méthode qui est redéfinie • redéfinir une partie des méthodes surchargées car cela provoque des risques de confusion et crée des pièges lors des modifications ultérieures des classes. EIA-FR / Jacques Bapst PR1_12 21 Java / Sous-classes, héritage et polymorphisme EIA-FR / Jacques Bapst 23 Java / Sous-classes, héritage et polymorphisme Redéfinition des méthodes [5] Complément PR1_12 Modificateur final § Les méthodes statiques (méthodes de classe) peuvent être masquées, dans des sous-classes, par des méthodes statiques possédant le même nom (*** une pratique à éviter ***). § Dans la déclaration d'une classe, le modificateur final indique que la classe ne peut pas être sous-classée (on ne peut pas créer de classes dérivées). § Ces méthodes masquées restent cependant accessibles en les préfixant avec le nom de la classe dans laquelle elles sont définies (comme il est recommandé de le faire avec toutes les méthodes statiques). § Dans la déclaration d'un champ, le modificateur final indique que la valeur du champ ne peut pas être modifiée après l'affectation initiale (dans la déclaration ou dans le constructeur). Permet de définir des valeurs constantes. § Dans la déclaration d'une méthode, le modificateur final indique que la méthode ne peut pas être redéfinie dans une sous-classe. § On ne peut donc pas spécialiser une méthode statique dans une sous-classe (comme les méthodes statiques ne sont jamais associées à un objet, le polymorphisme de s'applique pas). § Dans la déclaration des paramètres d'une méthode, le modificateur final indique que la valeur de ces paramètres ne peut pas être modifiée (il s'agit de paramètres d'entrée de la méthode). § En outre, une méthode statique ne peut pas masquer une méthode d'instance d'une super-classe (erreur à la compilation). Remarque : L'utilisation du modificateur final permet en outre au compilateur d'effectuer certaines optimisations : mise en ligne de méthodes (inlining), suppression de la recherche dynamique (late binding), etc. EIA-FR / Jacques Bapst PR1_12 22 EIA-FR / Jacques Bapst PR1_12 24 Java / Sous-classes, héritage et polymorphisme Java / Sous-classes, héritage et polymorphisme Modificateur protected Polymorphisme [1] § Le modificateur protected appliqué aux membres (champs ou méthodes) d'une classe indique que ces champs ne sont accessibles que dans la classe de définition, dans les classes du même paquetage et dans les sous-classes de cette classe (indépendamment du paquetage). § Si l'on considère le diagramme de classes suivant : Animal age poids mange() dort() § Il s'agit d'un accès plus restrictif que public mais - contrairement à ce que son nom pourrait laisser croire - moins restrictif que l'accès par défaut (package). § Le modificateur protected devrait être utilisé avec les champs et les méthodes qui ne sont pas requis par les utilisateurs de la classe mais qui pourraient s'avérer utiles à la création de sous-classes dans d'autres paquetages. EIA-FR / Jacques Bapst PR1_12 25 Java / Sous-classes, héritage et polymorphisme Chien nom mange() joue() mange() nage() EIA-FR / Jacques Bapst PR1_12 27 Java / Sous-classes, héritage et polymorphisme Modificateur private Polymorphisme [2] § Le modificateur private, appliqué aux membres (champs ou méthodes) d'une classe, indique que ces champs ne sont accessibles que dans la classe de définition. § On peut déduire du diagramme de classes les considérations suivantes : • Chien et Poisson sont des sous-classes d'Animal • Animal est la classe parente de Chien et de Poisson § Même s'il ne sont pas accessibles dans les sous-classes, les champs privés sont malgré tout hérités dans les sous-classes (une zone mémoire leur est allouée). • Chien hérite des membres d'Animal (age, poids, dort()) • Chien ajoute le champ nom • Chien ajoute la méthode joue() • Chien redéfinit la méthode mange() § Il s'agit de l'accès le plus restrictif. § Le modificateur private devrait être utilisé avec les champs et les méthodes qui ne sont utilisés qu'au sein de la classe de définition et qui devraient être cachés partout ailleurs. Conseil : D'une manière générale il vaut mieux commencer par un accès restrictif aux membres d'une classe ( ð private). Si nécessaire, il est toujours possible de relâcher les restrictions dans des versions ultérieures de la classe. Un relâchement des restrictions préserve la compatibilité ascendante (ce qui n'est pas le cas si l'on restreint les droits d'accès). EIA-FR / Jacques Bapst Poisson PR1_12 26 • Poisson hérite des membres d'Animal (age, poids, dort()) • Poisson ajoute la méthode nage() • Poisson redéfinit la méthode mange() EIA-FR / Jacques Bapst PR1_12 28 Java / Sous-classes, héritage et polymorphisme Java / Sous-classes, héritage et polymorphisme Polymorphisme [3] Polymorphisme [5] § Si l'on implémente les classes Animal, Chien et Poisson et que l'on écrit le code suivant : § En Java, dans la plupart des situations où il y a des relations d'héritage, la détermination de la méthode à invoquer n'est pas effectuée lors de la compilation. Chien milou = new Chien(...); milou.mange(); § C'est seulement à l'exécution que la machine virtuelle déterminera la méthode à invoquer selon le type effectif de l'objet référencé à ce moment là. § La méthode mange() qui sera invoquée est celle qui est définie dans la sous-classe Chien (car milou est du type Chien et la méthode mange() est redéfinie pour cette sous-classe). § Ce mécanisme s'appelle "Recherche dynamique de méthode" (Late Binding ou Dynamic Binding). § Si la sous-classe Chien ne redéfinissait pas la méthode mange(), ce serait alors la méthode mange() d'Animal qui serait invoquée (la classe Chien en hériterait). EIA-FR / Jacques Bapst PR1_12 § Ce mécanisme de recherche dynamique (durant l'exécution de l'application) sert de base à la mise en oeuvre de la propriété appelée polymorphisme. 29 Java / Sous-classes, héritage et polymorphisme EIA-FR / Jacques Bapst PR1_12 31 Java / Sous-classes, héritage et polymorphisme Polymorphisme [4] Polymorphisme [6] § Comme un Chien est un Animal (relation d'héritage) on peut écrire le code suivant : Animal toutou = new Chien(...); toutou.mange(); § On pourrait définir le polymorphisme comme la propriété permettant à un programme de réagir de manière différenciée à l'envoi d'un même message (invocation de méthode) en fonction des objets qui reçoivent ce message. § Quelle méthode mange() sera invoquée dans ce cas ? § Il s'agit donc d'une aptitude d'adaptation dynamique du comportement selon les objets en présence. § C'est toujours la méthode mange() définie dans la sous-classe Chien qui sera invoquée. § Avec l'encapsulation et l'héritage, le polymorphisme est une des propriétés essentielles de la programmation orientée objet. § Même si toutou est une référence de type Animal, c'est le type de l'objet référencé qui détermine la méthode qui sera appelée (comportement décrit sous "Redéfinition des méthodes"). EIA-FR / Jacques Bapst PR1_12 30 Remarque : La surcharge de méthodes peut également être considérée comme une forme de polymorphisme. Le choix de la méthode à invoquer est cependant déterminé à la compilation EIA-FR / Jacques Bapst PR1_12 32 Java / Sous-classes, héritage et polymorphisme Java / Sous-classes, héritage et polymorphisme Exemple de polymorphisme [1] Exemple de polymorphisme [3] § On peut ensuite appeler la méthode nourrir() en lui passant en paramètre la ménagerie précédemment créée : § L'exemple qui suit est destiné à illustrer le principe du polymorphisme. Il se base sur les classes précédemment définies (Animal, Chien et Poisson). nourrir(menagerie); § Si l'on souhaite enregistrer et manipuler une collection d'animaux (une ménagerie) on peut créer et alimenter le tableau suivant : § Sur la base du contenu de menagerie, la méthode nourrir() appellera successivement : mange() de la classe Poisson mange() mange() mange() mange() mange() // Déclaration et création du tableau Animal[] menagerie = new Animal[6]; // Alimentation du tableau menagerie[0] = new Poisson(...); menagerie[1] = new Chien(...); menagerie[2] = new Chien(...); menagerie[3] = new Animal(...); menagerie[4] = new Poisson(...); menagerie[5] = new Chien(...); EIA-FR / Jacques Bapst la la la la la classe classe classe classe classe Chien Chien Animal Poisson Chien § La méthode nourrir() n'a pas besoin de déterminer elle-même quelle méthode doit être appelée pour chaque animal. § Le polymorphisme fera en sorte que le message mange() soit interprété (à l'exécution) de manière appropriée selon les objets qui le reçoivent (ainsi chaque animal mangera selon ses goûts !). PR1_12 33 Java / Sous-classes, héritage et polymorphisme EIA-FR / Jacques Bapst PR1_12 35 Java / Sous-classes, héritage et polymorphisme Polymorphisme – Synthèse du mécanisme Exemple de polymorphisme [2] § Le polymorphisme nous permet d'écrire une méthode nourrir dont la fonction est de donner à manger à chaque animal contenu dans le tableau passé en paramètre en appelant successivement la méthode mange() pour chacun d'eux. //--------------------------------------------------// Appelle la méthode mange() pour chaque animal contenu // dans le tableau passé en paramètre //--------------------------------------------------public static void nourrir(Animal[] tabAnimaux) { § En Java, les variables sont typées. Avec la notion d'héritage, plusieurs «types» peuvent être compatibles avec une déclaration de variable. short s; // Ne peut rien contenir d'autre qu'un short Object o; // Peut contenir une référence vers un Object, // ou un objet d’une sous-classe § Le compilateur s’assure que les instructions sont compatibles avec la déclaration : Object o; o = "ah"; int i=o.length(); // Refusé, car length() Ï Object int i=((String)o).length(); // Ok si o référence un String § A l’exécution, Java « suit toujours la flèche » pour atteindre les méthodes : Animal a; if (tabAnimaux == null) return; for (int i=0; i<tabAnimaux.length; i++) { if (tabAnimaux[i] != null) { tabAnimaux[i].mange(); } } a = new Chien(); a.mange(); // Ce sera bien la méthode redéfinie par la // classe Chien qui sera exécutée public class Animal { void mange() {...} } } EIA-FR / Jacques Bapst de de de de de PR1_12 34 EIA-FR / Jacques Bapst public class Chien extends Animal { void mange() {...} // Redéfinition ! } PR1_12 36 Java / Classes abstraites et interfaces Classes abstraites : métaphore Informatique / Programmation § Comme les autres classes (et les interfaces), une classe abstraite définit un nouveau type qui peut être associé à un identificateur (déclaration de variables, champs, tableaux, paramètres formels, …). § Comme nous l'avons vu dans un des chapitres précédents, les classes peuvent être considérées comme étant des moules (des modèles, des plans, ...) permettant de fabriquer des objets. Programmation orientée objet avec Java § Si l'on poursuit avec cette métaphore, les classes abstraites peuvent être considérées comme étant des moules incomplets (des modèles partiels, des plans non terminés, ...) qui ne peuvent pas être utilisés tels quels pour créer des objets mais qui peuvent être utilisés pour fabriquer d'autres plans plus précis (représentés par des sous-classes) qui seront complétés et qui permettront, eux, de créer des objets (des instances des sousclasses). 13 : Classes abstraites et interfaces Jacques Bapst [email protected] EIA-FR / Jacques Bapst Java / Classes abstraites et interfaces PR1_13 3 Java / Classes abstraites et interfaces Classes et méthodes abstraites Classes abstraites § Une classe abstraite est une classe qui contient une ou plusieurs méthodes abstraites. Elle peut malgré tout contenir d'autres méthodes habituelles (appelée parfois méthodes concrètes). Classe abstraite extends Classe concrète new Instances (Objets) § Une méthode abstraite est une méthode qui ne contient pas de corps. Elle possède simplement une signature de définition suivie du caractère point-virgule (pas de bloc d'instructions). Objet 1 § Une méthode abstraite doit obligatoirement être déclarée avec le modificateur abstract. § Exemple : Objet 2 public abstract double area(); § Une classe possédant une ou plusieurs méthodes abstraites devient obligatoirement une classe abstraite. Cette classe doit également être déclarée avec le modificateur abstract. EIA-FR / Jacques Bapst PR1_13 Objet 3 2 EIA-FR / Jacques Bapst PR1_13 4 Java / Classes abstraites et interfaces Java / Classes abstraites et interfaces Utilité des classes abstraites Classes abstraites : règles [1] § En pratique, les classes abstraites permettent de définir des fonctionnalités (des comportements) que les sous-classes devront impérativement implémenter même si la classe abstraite n'est pas en mesure de fournir elle-même une implémentation pour ces méthodes. § Les règles suivantes s'appliquent aux classes abstraites : • Une classe abstraite ne peut pas être instanciée : on ne peut pas créer d'objet en utilisant l'opérateur new. • Une sous-classe d'une classe abstraite ne peut être instanciée que si elle redéfinit chaque méthode abstraite de sa classe parente et qu'elle fournit une implémentation (un corps) pour chacune des méthodes abstraites. Une telle sous-classe est souvent appelée sous-classe concrète afin de mettre en évidence le fait qu'elle n'est pas abstraite. § Les utilisateurs des sous-classes d'une classe abstraite sont donc assurés de trouver toutes les méthodes définies dans la classe abstraite dans chacune des sous-classes concrètes. § Les classes abstraites constituent donc une sorte de contrat (spécification contraignante) qui garantit que certaines méthodes seront disponibles dans les sous-classes et qui oblige les programmeurs à les implémenter dans toutes les sous-classes concrètes. • Si une sous-classe d'une classe abstraite n'implémente pas toutes les méthodes abstraites dont elle hérite, cette sous-classe est elle-même abstraite (et ne peut donc pas être instanciée). • Les méthodes déclarées avec l'un des modificateurs suivants : static, private ou final ne peuvent pas être abstraites étant donné qu'elles ne peuvent pas être redéfinies dans une sous-classe. EIA-FR / Jacques Bapst PR1_13 5 Java / Classes abstraites et interfaces EIA-FR / Jacques Bapst PR1_13 Java / Classes abstraites et interfaces Classes abstraites : règles [2] Exemple de classe abstraite [1] § Autres règles s'appliquant aux classes abstraites : § Si l'on souhaite, par exemple, créer une hiérarchie de classes qui décrive des formes géométriques à deux dimensions, on pourrait envisager l'arborescence représentée par le diagramme de classes suivant : • Une classe déclarée final ne peut pas contenir de méthodes abstraites car elle ne peut pas être sous-classée. • Une classe peut être déclarée abstract même si elle ne possède pas réellement de méthode abstraite. Shape Cela signifie que son implémentation est incomplète en certains points (p. ex. le corps de certaines méthodes doit être complété en fonction du contexte) et qu'elle est destinée à jouer le rôle de classe parente pour une ou plusieurs sous-classes qui achèveront l'implémentation. Circle Même si elle ne possède pas de méthodes abstraites, une telle classe ne peut pas être instanciée (erreur à la compilation). EIA-FR / Jacques Bapst 7 PR1_13 Rectangle Square 6 EIA-FR / Jacques Bapst Triangle ... ... PR1_13 8 Java / Classes abstraites et interfaces Java / Classes abstraites et interfaces Exemple de classe abstraite [2] Exemple de classe abstraite [4] § Sachant que toutes les formes possèdent les propriétés périmètre et surface, il est judicieux de placer les méthodes définissant ces propriétés (perimeter() et area()) dans la classe qui est à la racine de l'arborescence (Shape). //----------------------------------------------------------// Rectangle //----------------------------------------------------------public class Rectangle extends Shape { protected double length, width; § La classe Shape ne peut cependant pas implémenter ces deux méthodes car on ne peut pas calculer le périmètre et la surface sans connaître le détail de la forme. public Rectangle(double length, double width) { this.length = length; this.width = width; } § On pourrait naturellement implémenter ces méthodes dans chacune des sous-classes de Shape mais il n'y a aucune garantie que toutes les sous-classes les possèdent (ce serait laissé au bon vouloir des programmeurs des sous-classes). public double getLength() { return length; } public double getWidth() { return width; } public double perimetre() { return 2*(length + width); } § Il est possible de résoudre ce problème en déclarant dans la classe Shape deux méthodes abstraites perimeter() et area() ce qui rend la classe Shape elle-même abstraite et impose aux créateurs de sous-classes de les implémenter. EIA-FR / Jacques Bapst PR1_13 } 9 Java / Classes abstraites et interfaces EIA-FR / Jacques Bapst PR1_13 11 Java / Classes abstraites et interfaces Exemple de classe abstraite [3] Exemple de classe abstraite [5] § Les autres sous-classes (Square, Triangle, ...) pourraient être implémentées selon le même modèle. //----------------------------------------------------------// Shape //----------------------------------------------------------public abstract class Shape { // Classe abstraite public abstract double perimeter(); // Méthode abstraite public abstract double area(); // Méthode abstraite } § Même si la classe Shape est abstraite, il est tout de même possible de déclarer des variables de ce type qui pourront recevoir des objets créés à partir des sous-classes concrètes : //----------------------------------------------------------// Circle //----------------------------------------------------------public class Circle extends Shape { public static final double PI = 3.14159265358979; private double radius; Shape[] drawing = new Shape[3]; drawing[0] = new Rectangle(2.5, 6.8); drawing[1] = new Circle(4.66); drawing[2] = new Square(0.125); // Conversion élargissante // Conversion élargissante // Conversion élargissante § Le polymorphisme permet ensuite d'invoquer une méthode commune sur chacun des objets : public Circle(double radius) { this.radius = radius; } double totalArea = 0.0; public double getRadius() { return radius; } for (int i=0; i<drawing.length; i++) { totalArea += drawing[i].area(); } public double perimeter() { return 2*PI*radius; } public double area() public double surface() { return length * width; } { return PI*radius*radius; } // Calcule la somme // des surfaces } EIA-FR / Jacques Bapst PR1_13 10 EIA-FR / Jacques Bapst PR1_13 12 Java / Classes abstraites et interfaces Java / Classes abstraites et interfaces Interface [1] Interface [3] § Comme une classe et une classe abstraite, une interface permet de définir un nouveau type (référence). § Le corps d'une interface peut contenir : • Des constantes qui sont implicitement publiques § Le comportement des objets de ce type (les opérations que l'on peut effectuer) sera déterminé par le contenu (la définition) de cette interface. ü Les modificateurs static et final ne sont pas nécessaires (implicites). • Des méthodes abstraites qui sont obligatoirement publiques ü Le modificateur abstract n'est pas nécessaire (implicite). § Une interface constitue essentiellement une spécification qui laisse de côté les détails d'implémentation. ü Le modificateur public n'est pas nécessaire (implicite). § L'interface définit ainsi une sorte de contrat que les classes qui l'implémenteront s'engagent à respecter. • Des méthodes avec une implémentation par défaut § Les interfaces jouent un rôle important dans la phase de conception (API) d'applications, de librairies ou de systèmes basés sur des ensembles d'objets qui communiquent. • Des méthodes statiques. ü Le modificateur default doit être utilisé. § Par contre • Une interface ne peut pas définir de champs d'instance. § Par certains aspects, la notion d'interface est assez proche de la notion de classe abstraite mais elle comporte cependant un certain nombre de spécificités qui l'en distinguent et qui en étendent le potentiel. EIA-FR / Jacques Bapst PR1_13 • Une interface ne définit pas de constructeur (on ne peut pas l'instancier et elle n'intervient pas dans le chaînage des constructeurs ). 13 Java / Classes abstraites et interfaces EIA-FR / Jacques Bapst Utilisation des interfaces [1] § Du point de vue syntaxique, la définition d'une interface est proche de celle d'une classe abstraite. § En Java, une classe ne peut hériter que d'une et d'une seule classe parente (héritage simple). § Il suffit de remplacer les mots-clés abstract class par le motclé interface : § Une classe peut, par contre, implémenter une ou plusieurs interfaces en utilisant la syntaxe suivante : public interface Printable { public void print(); } implements interface1, interface2, ... § L'implémentation d'une ou de plusieurs interfaces (implements) peut être combinée avec l'héritage simple (extends). La clause implements doit suivre la clause extends. § Une fois l'interface déclarée, il est possible de déclarer des variables de ce (nouveau) type : EIA-FR / Jacques Bapst 15 Java / Classes abstraites et interfaces Interface [2] Printable Printable[] PR1_13 § Exemples : document; printSpool; PR1_13 14 public class Report implements Printable {...} public class Book implements Printable, Zoomable {...} public class Circle extends Shape implements Printable {...} public class Square extends Rectangle implements Printable, Zoomable {...} EIA-FR / Jacques Bapst PR1_13 16 Java / Classes abstraites et interfaces Java / Classes abstraites et interfaces Utilisation des interfaces [2] Exemple d'interface [2] § Tous les objets dont la classe implémente l'interface Comparable pourront ensuite être triés. § Lorsqu'une classe déclare une interface dans sa clause implements, elle indique ainsi qu'elle s'engage à fournir une implémentation (c'est-à-dire un corps) pour chacune des méthodes abstraites de cette interface. § Si une classe implémente une interface mais ne fournit pas d'implémentation pour toutes les méthodes abstraites de l'interface, elle hérite des méthodes (abstraites) non implémentées de l'interface et doit elle-même être déclarée abstract. § Si une classe implémente plus d'une interface, elle doit implémenter toutes les méthodes abstraites de chacune des interfaces mentionnées dans la clause implements (ou alors être déclarée abstract). . . . Person[] population; . . . Tools.sort(population); . . . § Les méthodes de l'interface qui sont déclarées avec une implémentation par défaut peuvent être redéfinies (ou non) dans les classes qui implémentent l'interface. EIA-FR / Jacques Bapst PR1_13 public class Person implements Comparable { . . . private int age; . . . public int compareTo(Object p) { if (p instanceof Person) { if (this.age < ((Person)p).age) return -1; if (this.age > ((Person)p).age) return 1; return 0; } else . . . } . . . } 17 Java / Classes abstraites et interfaces EIA-FR / Jacques Bapst Exemple d'interface [3] § Si l'on souhaite caractériser la fonctionnalité de comparaison qui est commune à tous les objets qui ont une relation d'ordre (plus petit, égal, plus grand), on peut définir l'interface Comparable de la manière suivante : § Supposons que nous voulions que les classes dérivées de la classe Shape disposent toutes d'une méthode print() permettant d'imprimer les formes géométriques. § Il serait naturellement possible d'ajouter une méthode abstraite print() à la classe Shape et ainsi chacune des sous-classes concrètes devrait implémenter cette méthode. L'utilisateur de ces sous-classes serait ainsi sûr de disposer d'une méthode print() lui permettant d'imprimer la forme. public interface Comparable { public int compareTo(Object v); } § Cette interface définit un type de données qui peut ensuite être utilisé dans des traitements qui nécessitent une relation d'ordre, dans une méthode de tri par exemple : § Avec cette solution, le problème serait résolu pour toutes les sous-classes de Shape, mais si d'autres classes (qui n'héritent pas de Shape) souhaitaient également disposer des fonctions d'impression, elles devraient à nouveau déclarer des méthodes abstraites d'impression dans leur arborescence. public class Tools { . . . public static void sort(Comparable[] t) { . . . § L'utilisation d'une interface permet de résoudre ce problème de manière plus élégante en découplant la fonctionnalité d'impression de la hiérarchie des classes. . . . . . . } . . . } EIA-FR / Jacques Bapst 19 Java / Classes abstraites et interfaces Exemple d'interface [1] if (t[i].compareTo(t[j]) > 0) PR1_13 PR1_13 18 EIA-FR / Jacques Bapst PR1_13 20 Java / Classes abstraites et interfaces Java / Classes abstraites et interfaces Exemple d'interface [4] Exemple d'interface [6] § Au lieu d'ajouter une méthode abstraite print() dans la classe Shape nous créons une interface nommée Printable qui définit la méthode abstraite print() : § L'implémentation des sous-classes de Shape s'effectue donc de la même manière que si la méthode print() avait été déclarée comme méthode abstraite dans Shape. § L'avantage de l'interface Printable réside dans le fait qu'elle peut être utilisée avec d'autres classes qui n'ont rien à voir avec la classe Shape : public interface Printable { public void print(); } public class Book extends Document implements Printable { ... } § Et nous déclarons que la classe Shape implémente l'interface Printable (le corps de la classe Shape reste inchangé) : § Un autre avantage est que l'interface Printable peut être utilisée en combinaison avec d'autres interfaces, ce qui n'est pas possible avec les classes ( car une sous-classe ne peut pas avoir plusieurs super-classes). public abstract class Shape implements Printable { public abstract double perimeter(); // Méthode abstraite public abstract double area(); // Méthode abstraite } public class Book extends Document implements Printable, Zoomable { ... } EIA-FR / Jacques Bapst PR1_13 21 Java / Classes abstraites et interfaces EIA-FR / Jacques Bapst Interface fonctionnelle § Une interface fonctionnelle est une interface comprenant exactement une méthode abstraite. § Chaque sous-classe concrète de Shape devra obligatoirement implémenter le corps de la méthode print() (en plus des méthodes abstraites perimeter() et area()) : § Une interface fonctionnelle peut malgré tout comporter des constantes, des méthodes avec une implémentation par défaut, des méthodes redéclarant certaines méthodes de la classe Object ainsi que des méthodes statiques. //---------------------------------------------------------// Circle //---------------------------------------------------------public class Circle extends Shape { public static final double PI = 3.14159265358979; private double radius; § La notion d'interface fonctionnelle joue un rôle important en lien avec l'utilisation des expressions Lambda. § L'annotation @FunctionalInterface peut précéder la déclaration des interfaces fonctionnelles. Ainsi, le compilateur vérifiera que les règles énoncées soient respectées. public Circle(double radius) { this.radius = radius; } public double getRadius() { return radius; } @FunctionalInterface public interface SimpleFuncInterface { public void doWork(); } public double perimeter() { return 2*PI*radius; } { return PI*radius*radius; } public void print() { ... } } EIA-FR / Jacques Bapst 23 Java / Classes abstraites et interfaces Exemple d'interface [5] public double area() PR1_13 PR1_13 22 EIA-FR / Jacques Bapst PR1_13 24 Java / Classes abstraites et interfaces Java / Classes abstraites et interfaces Gestion des exceptions Classe abstraite ou interface ? § Lors de la conception d'une application ou d'une librairie, le choix conceptuel entre une classe abstraite et une interface n'est pas toujours facile. § Les méthodes concrètes ne peuvent pas annoncer plus d'exceptions contrôlées dans leurs clauses throws, que celles qui sont annoncées dans la déclaration des méthodes abstraites qu'elles implémentent (déclaration dans classe abstraite ou interface). § Parmi les points à prendre en considération, on peut rappeler que : public interface Writable { public void write(Object o) throws IOException, MemOverflow; } • Une classe peut implémenter plusieurs interfaces mais ne peut hériter que d'une seule classe abstraite. • Une interface peut être implémentée par une classe sans qu'il y ait de rapports étroits entre les deux. Une sous-classe est liée par une relation plus forte ("Est un ..." ). • Les classes abstraites et les interfaces permettent de définir des implémentation par défaut pour les méthodes. public class Buffer implements Writable { public void write(Object o) throws MemOverflow, ArithmeticException … } } • Les interfaces fonctionnelles (une seule méthode abstraite) peuvent être implémentées par des expressions Lambdas ou des références de méthodes permettant ainsi d'alléger le code. La méthode concrète write() n'a pas l'obligation d'annoncer toutes les exceptions définies dans l'interface Writable (on ne peut pas imposer une liste d'exceptions). { Elle ne peut pas annoncer d'exceptions contrôlées supplémentaires mais peut ajouter des exceptions non-contrôlées (comme ArithmeticException par exemple). EIA-FR / Jacques Bapst PR1_13 25 Java / Classes abstraites et interfaces EIA-FR / Jacques Bapst PR1_13 Java / Classes abstraites et interfaces Extension des interfaces Conflits de noms [1] § Des conflits de noms (collision) peuvent se produire lorsqu'une classe implémente plusieurs interfaces comportant des noms de méthodes ou de constantes identiques. § Les interfaces peuvent avoir des sous-interfaces (tout comme les classes peuvent avoir des sous-classes). Comme pour les classes, le mot-clé extends est utilisé pour créer une sous-interface. § Il faut distinguer plusieurs situations : public interface Zoomable extends Observable {...} • Plusieurs méthodes portent des signatures identiques : § Une sous-interface hérite de toutes les méthodes et de toutes les constantes de son interface parente et peut définir de nouvelles méthodes ainsi que de nouvelles constantes. • Mêmes noms de méthodes mais profils de paramètres différents : § Contrairement aux classes, une interface peut posséder plusieurs interfaces parentes (héritage multiple). • Mêmes noms de méthodes, profils de paramètres identiques, mais types des valeurs de retour différents : ü Pas de problème, la classe doit implémenter cette méthode ü Implémentation de deux ou plusieurs méthodes surchargées public interface Transformable extends Scalable, Translatable, Rotatable {...} ü Pas possible d'implémenter les deux interfaces (cela provoquera une erreur à la compilation : Interface-Collision) • Seulement les listes d'exceptions sont différentes : § Une classe qui implémente une sous-interface doit implémenter les méthodes abstraites définies directement par l'interface ainsi que les méthodes abstraites héritées de toutes les interfaces parentes de la sous-interface. EIA-FR / Jacques Bapst 27 PR1_13 ü La méthode ne peut pas générer plus d'exceptions contrôlées que celles correspondant à l'intersection de l'ensemble des clauses d'exception 26 EIA-FR / Jacques Bapst PR1_13 28 Java / Classes abstraites et interfaces Conflits de noms [2] • Noms de constantes identiques dans plusieurs interfaces : ü Doivent être accédées en utilisant la notation qualifiée (Interface.Constante) • Plusieurs méthodes portent des signatures identiques mais ont des implémentations par défaut différentes : ü Erreur détectée par le compilateur ü Obligation de redéfinir la méthode en conflit dans la classe qui implémente les interfaces ü Les méthodes par défaut peuvent malgré tout être invoquée avec la syntaxe Interface.super.Méthode EIA-FR / Jacques Bapst PR1_13 29 Java / Classes abstraites et interfaces Interface de marquage § Il est parfois utile de définir une interface entièrement vide appelée interface de marquage. § Une classe peut implémenter cette interface en la nommant dans la clause implements sans avoir à implémenter de méthodes. § Dans ce cas, toutes les instances de la classe deviennent des instances valables de l'interface. Il est donc possible de tester si un objet implémente l'interface de marquage à l'aide de l'opérateur instanceof : if (obj instanceof Cloneable) {...} § Une interface de marquage sert à communiquer aux instances qui l'implémentent des informations supplémentaires concernant l'objet, son comportement, son implémentation, … § Les interfaces java.lang.Cloneable et java.io.Serializable constituent deux exemples d'interfaces de marquage de la plateforme Java. EIA-FR / Jacques Bapst PR1_13 30 Java / Classes internes Classes membres statiques [1] Informatique / Programmation § Une classe membre statique est une classe définie comme un membre statique d'une autre classe. § Des interfaces peuvent également être déclarées comme membres statiques d'une classe. Les remarques qui suivent s'appliquent donc également aux interfaces membres statiques. § Par analogie avec les méthodes statiques, on pourrait parler de "classe de classe" (mais cela porte un peu à confusion). Programmation orientée objet avec Java 14 : Classes internes § Une classe membre statique se comporte comme une classe de premier niveau avec en outre la possibilité d'accéder aux membres statiques de la classe qui la contient ( classe englobante). § Une classe membre statique a accès à tous les membres statiques de sa classe englobante, y compris les membres privés ( private). Jacques Bapst [email protected] § L'inverse est également vrai : les méthodes de la classe englobante ont accès à tous les membres ( statiques ou non-statiques) d'une classe membre statique, y compris les membres privés. EIA-FR / Jacques Bapst Java / Classes internes PR1_14 Java / Classes internes Classes internes Classes membres statiques [2] § Jusqu'à présent, les classes (et interfaces) que nous avons vues étaient toutes des classes (et interfaces) de premier niveau (c'està-dire des membres directs des paquetages, sans imbrication). § Une classe membre statique a même accès à tous les membres des autres classes membres statiques ( classes "sœurs"), y compris aux membres déclarés private de ces classes. § Le langage Java permet cependant de définir des classes à l'intérieur d'autres classes. On parle dans ce cas de classes internes (Nested Class). § Une classe membre statique peut être déclarée avec ses propres modificateurs de contrôle d'accès. Ces modificateurs (public, protected, private, aucun) possèdent les mêmes significations que pour les autres membres d'une classe (champs et méthodes). § Il existe quatre types de classes internes : Ø Les classes membres statiques Ø Les classes membres Ø Les classes locales Ø Les classes anonymes § En dehors de la classe englobante, une classe membre statique est nommée en combinant le nom de la classe externe avec le nom de la classe interne. Par exemple : (Static Nested Class) (Inner Class) (Local Inner Class) LinkedStack.Linkable node (Anonymous Inner Class) § Restrictions : • Une classe membre statique ne peut pas posséder le même nom qu'une de ses classes englobantes. • Une classe membre statique ne peut être englobée que dans une classe de premier niveau ou dans une classe membre statique. Attention : D'un auteur à l'autre, le vocabulaire utilisé pour qualifier ces différentes classes internes peut varier sensiblement. EIA-FR / Jacques Bapst 3 PR1_14 2 EIA-FR / Jacques Bapst PR1_14 4 Java / Classes internes Java / Classes internes Exemple d'interface membre statique Classes membres [2] § Restrictions : //===== Une classe qui implémente une pile sous forme de liste chaînée ===== public class LinkedStack { // Interface membre statique qui définit la manière dont les objets sont liés public static interface Linkable { public Linkable getNext(); public void setNext(Linkable node); } • Une classe membre ne peut pas porter le même nom qu'une classe ou qu'un paquetage englobant (une telle restriction ne s'applique pas aux champs et aux méthodes). • Les classes membres ne peuvent pas contenir de champs, de méthodes ou de classe déclarés static (à l'exception des constantes qui sont déclarées à la fois static et final). // La tête de liste est un objet de type Linkable Linkable head; // Les corps des méthodes sont omis public void push(Linkable node) { ... } public Linkable pop() { ... } § Pour instancier une classe membre à l'extérieur de la classe de définition, il faut disposer d'une instance de la classe englobante. } § On utilisera cette instance avec l'opérateur new pour créer un objet de la classe membre. Syntaxe : InstanceExt.new ClasseInterne Exemple : //===== Cette classe implémente l'interface membre statique ===== public class LinkableInteger implements LinkedStack.Linkable { // Les données et le constructeur du noeud int i; public LinkableInteger(int i) { this.i = i; } ExtClName // Données et méthodes requises pour implémenter l'interface LinkedStack.Linkable next; public LinkedStack.Linkable getNext() { return next; } public void setNext(LinkedStack.Linkable node) { next = node; } ExtClName.IntClName iObj = eObj.new IntClName(…); // Objet externe // Objet interne et non pas : ExtClName.IntClName iObj = eObj.new ExtClName.IntClName(…); } EIA-FR / Jacques Bapst eObj = new ExtClName(…); PR1_14 EIA-FR / Jacques Bapst 5 Java / Classes internes PR1_14 Java / Classes internes Classes membres [1] Classes membres [3] § Une syntaxe particulière est utilisée pour accéder explicitement aux champs et méthodes d'instance de son objet englobant. § Les interfaces ne peuvent pas être déclarées comme membres non-statiques d'une classe. § Cette syntaxe n'est nécessaire que pour référencer un membre d'une classe englobante qui est masqué par un membre du même nom dans la classe interne (ce qui est à éviter !) : Nom_Classe_Englobante . this . Nom_Membre § Une instance d'une classe membre est toujours associée à une instance (un objet) de la classe englobante. § Le code d'une classe membre a accès à tous les champs et à toutes les méthodes (autant statiques que non-statiques et même s'ils sont déclarés privés) de sa classe englobante et des classes "sœurs". § Comme tous les autres membres de la classe, une classe membre peut être déclarée avec ses propres modificateurs de contrôle d'accès. Ces modificateurs (public, protected, private, aucun) possèdent les mêmes significations que pour les autres membres de la classe (champs et méthodes). PR1_14 6 Complément § Une classe membre est une classe qui est déclarée comme un membre non-statique d'une classe englobante. EIA-FR / Jacques Bapst 7 LinkedStack.this.head § Il est également possible d'accéder à un champ masqué ou à une méthode redéfinie d'une super-classe de la classe englobante : Nom_Classe_Englobante . super . Nom_Membre Reactor.super.run() § Une classe de premier niveau peut étendre (spécialiser) une classe membre ce qui introduit deux hiérarchies distinctes ( la hiérarchie de classes habituelle + une hiérarchie de confinement) qui affectent les éléments visibles ( portée). ð A éviter ! EIA-FR / Jacques Bapst PR1_14 8 Java / Classes internes Java / Classes internes Exemple de classe membre Classes locales [2] § Les restrictions suivantes s'appliquent aux classes locales : //===== Une classe qui implémente une pile sous forme de liste chaînée ===== public class LinkedStack { // Interface membre statique qui définit la manière dont les objets sont liés public static interface Linkable { ... } // Contenu omis • Une classe locale n'est visible qu'au sein du bloc qui la définit; elle ne peut jamais être utilisée hors de ce bloc. private Linkable head; public void push(Linkable node) { ... } public Linkable pop() { ... } • Les classes locales ne peuvent pas être déclarées public, protected, private ou static. Ces modificateurs sont réservés aux membres de la classe. // Contenu omis // Contenu omis // Cette méthode retourne un objet de type Enumeration pour l'objet LinkedStack courant public java.util.Enumeration enumerate() { return new Enumerator(); } } • Pour les mêmes raisons que les classes membres, les classes locales ne peuvent pas contenir de champs, de méthodes ou de classes statiques (à l'exception des constantes déclarées static final). // Classe membre qui implémente l'interface Enumeration protected class Enumerator implements java.util.Enumeration { Linkable current; // Le constructeur utilise le champ privé head de la classe englobante public Enumerator() { current = head; } public boolean hasMoreElements() { return (current != null); } public Object nextElement() { if (current==null) throw new java.util.NoSuchElementException(); Object value = current; current = current.getNext(); return value; } } EIA-FR / Jacques Bapst • Une classe locale ne peut pas avoir le même nom qu'une de ses classes englobantes. • Une classe locale ne peut accéder aux variables locales et paramètres de méthode qui se trouvent dans sa portée que si ces variables et paramètres sont déclarés avec le modificateur final. Dans ce cas, le compilateur créera une copie de ces variables locales car leur durée de vie peut être plus courte que celle des instances de la classe locale. 9 PR1_14 EIA-FR / Jacques Bapst 11 Java / Classes internes Java / Classes internes Classes locales [1] Exemple de classe locale § Une classe locale est une classe qui est déclarée au sein d'un bloc de code Java (généralement dans une méthode). //===== Un exemple de classe locale utilisant des variables locales ===== public class TestLocalClass { // Interface membre statique public static interface IntHolder { public int getValue(); } § Tout comme une variable locale, une classe locale n'est visible qu'à l'intérieur du bloc dans lequel elle a été déclarée. public static void main(String[] args) { IntHolder[] holders = new IntHolder[10]; § Les classes locales sont déclarée à l'intérieur d'une classe englobante et elles partagent de ce fait plusieurs caractéristiques des classes membres. for (int i=0; i<10; i++) { final int fi = i; // Classe locale pouvant utiliser les variables locales déclarées final class MyIntHolder implements IntHolder { public int getValue() { return fi; } } holders[i] = new MyIntHolder(); // Instancie la classe locale } // La classe locale et la variable fi ne sont plus visibles mais on peut malgré tout utiliser // le tableau d'instance holders et afficher les valeurs 0…9 (1) for (int i=0; i<10; i++) System.out.println(holders[i].getValue()); § Les interfaces ne peuvent pas être déclarées localement. § Le code d'une classe locale peut accéder à tous les membres (y compris aux membres privés) de la classe englobante. § Les classes locales peuvent également accéder à toutes les variables locales et aux paramètres de méthode qui se trouvent dans la portée de la définition locale de la méthode pour autant qu'ils soient déclarés final (car la durée de vie d'une instance d'une classe locale peut être plus longue que l'exécution de la méthode dans laquelle elle est déclarée). EIA-FR / Jacques Bapst PR1_14 PR1_14 10 } } (1) Pour expliquer ce comportement assez surprenant, il faut savoir que chaque instance d'une classe locale possède une copie privée de chaque variable locale déclarée final qu'elle utilise. Ainsi, l'instance mémorise l'état des variables qui étaient dans sa portée au moment où elle a été créée. EIA-FR / Jacques Bapst PR1_14 12 Java / Classes internes Java / Classes internes Classes anonymes [1] Classes anonymes [3] § Une classe anonyme est une classe locale qui ne possède pas de nom. § Les classes anonymes constituent une forme particulière de classes locales et toutes les restrictions mentionnées pour les classes locales s'appliquent également aux classes anonymes. § Une classe anonyme est soit : § En plus, on peut mentionner la restriction suivante : • une sous-classe d'une classe parente existante • Comme les classes anonymes ne possèdent pas de nom, il n'est pas possible de définir explicitement des constructeurs (elles possèdent uniquement le constructeur par défaut ). En remplacement, on peut cependant créer un initialiseur d'instance qui pourra jouer pratiquement le même rôle qu'un constructeur. • une classe qui implémente une interface existante (c'est donc également une sous-classe d'Object) § Une classe anonyme combine la déclaration et l'instanciation de la sous-classe sous la forme d'une expression qui peut apparaître comme fragment d'une expression plus grande (par exemple dans une invocation de méthode). § Quand utiliser une classe anonyme ? • La classe possède un corps très court • Une seule instance de la classe est nécessaire • Le nom de la classe ne facilite pas la compréhension du code § Lorsqu'une sous-classe locale (ou classe implémentant une interface) n'est utilisée qu'une seule fois, on peut envisager d'en faire une classe anonyme qui place la définition et l'utilisation de la classe au même endroit. Cela peut, dans certains cas, favoriser la compacité et la lisibilité du code source. EIA-FR / Jacques Bapst PR1_14 § Il faut toujours privilégier la lisibilité du code source. 13 Java / Classes internes EIA-FR / Jacques Bapst Exemple de classe anonyme § La création d'une classe anonyme fait intervenir une nouvelle syntaxe qui combine la définition et l'instanciation de la classe. //===== Un exemple de classe anonyme pour filtrer des fichiers dans un répertoire ===== public class TestAnonymousClass { // Liste tous les fichiers du répertoire D:\Data possédant l'extension .java public static void main(String[] args) { § Pour la définir plus formellement, il faut distinguer les deux situations possibles. File f = new File("D:\\Data"); § Sous-classe anonyme : Nom_Classe_Parente ( [ Liste_Param ] 15 Java / Classes internes Classes anonymes [2] new PR1_14 // Le répertoire à énumérer // La méthode list() prend en argument un objet de type FilenameFilter // Une instance de la sous-classe anonyme de FilenameFilter est créée // directement dans l'expression d'invocaton de la méthode list() String[] fileList=f.list( new FilenameFilter() { ) { Corps_Sous_Classe } public boolean accept(File dir, String s) { return s.endsWith(".java"); } § Classe anonyme implémentant une interface : new Nom_Interface() { Corps_Classe } }); // N'oubliez pas la parenthèse et le point-virgule qui achèvent l'appel de la méthode // Affichage de la liste des fichiers trouvés for (int i=0; i<fileList.length; i++) { System.out.println("File name : " + fileList[i]); } § Cette nouvelle syntaxe introduit une complication supplémentaire sur le plan de la mise en page (indentation) car la création d'une classe anonyme s'effectue généralement dans le cadre d'une expression (voir exemple). ð A utiliser avec modération ! } } Noter la mise en page pour l'écriture des classes anonymes (conventions de codage Oracle). EIA-FR / Jacques Bapst PR1_14 14 EIA-FR / Jacques Bapst PR1_14 16 Java / Classes internes Remarques sur les classes internes [1] § Les différents types de classes internes offrent des mécanismes supplémentaires pour organiser et structurer le code source. § En créant une classe interne, on limite leur niveau d'exposition en les encapsulant au sein d'autres classes. Pour les classes membres (statiques ou non), on peut limiter leur accès en utilisant les modificateurs public, protected et private. § On peut ainsi retrouver au niveau des classes internes certains bénéfices de l'encapsulation. § Si l'on combine plusieurs niveaux d'imbrication (qui créent une hiérarchie de confinement) avec une hiérarchie de classes complexe, on peut très vite arriver à de grandes complications en terme de portée, visibilité et masquage des identificateurs concernés. § Les classes internes ne doivent être utilisées que si elles apportent des simplifications dans la conception et favorisent la lisibilité et la maintenance du code. Sinon, elles sont plutôt à éviter. EIA-FR / Jacques Bapst PR1_14 17 Java / Classes internes Remarques sur les classes internes [2] § Dans les applications comportant une interface utilisateur graphique (GUI), les classes internes et plus particulièrement les classes anonymes sont fréquemment utilisées pour créer, à la volée, des gestionnaires d'événements (Event Listener). § Sur le plan de l'implémentation, les classes internes sont traitées de la même manière que les classes de premier niveau : chacune donne lieu à un fichier .class séparé (c'est également vrai pour les classes anonymes). § Suivant les types de classes internes, le nom du fichier résultant de la compilation est composé de manière différente mais il comporte généralement le caractère '$' (on trouvera par exemple les fichiers LinkedStack$Linkable.class ou FilenameFilter$1.class). § Lors du déploiement d'une application, il peut être important de connaître ces détails d'implémentation; par contre, pour l'écriture d'une application, cela n'a aucune incidence. EIA-FR / Jacques Bapst PR1_14 18 Java / Conventions de codage Pourquoi des conventions de codage ? Informatique / Programmation § Le respect de certaines conventions générales facilite grandement la lecture du code source : • Reconnaissance plus rapide de la structure du code (on se retrouve en terrain connu) • Identification rapide de certaines entités par leur convention d'écriture (constantes, classes, …) Programmation orientée objet avec Java § Un code source est écrit une fois et généralement lu à de nombreuses reprises (parfois longtemps après son écriture et souvent par des lecteurs différents ). Il vaut donc la peine d'investir un peu de temps au moment de son écriture afin de faciliter sa lecture et sa compréhension ultérieure. 15 : Conventions de codage Jacques Bapst § Certains éditeurs offrent des aides pour la mise en page du code source et le respect de certaines règles de codage. [email protected] § Les conventions proposées par Oracle : www.oracle.com/technetwork/java/codeconventions-150003.pdf EIA-FR / Jacques Bapst Java / Conventions de codage 3 Java / Conventions de codage Pourquoi des conventions de codage ? Mise en page § Les commentaires doivent être alignés au niveau d'indentation du code qui suit (pas de commentaires qui brisent la structure du code en débordant dans la marge d'indentation). § Les fichiers sources d'un programme sont destinés à deux types de lecteurs : l'humain et la machine (le compilateur notamment). • Pour le compilateur : "a.b(c)" vaut bien "myFile.println(currentTime)" § Ne pas dépasser (env.) 80 caractères par lignes (pour éviter le scrolling horizontal et les problèmes d'impression). § Les conventions de codage (Style Guidelines) ont pour but d'améliorer, pour les humains, la lisibilité des fichiers sources en respectant certaines règles qui ne sont pas imposées par le langage (choix des identificateurs, mise en page, …). § Une seule instruction par ligne (sauf exceptions). § Déclarer de préférence les variables locales au début des méthodes. § Décomposer le code des méthodes trop longues (celles qui ne tiennent pas sur une page imprimée ). § Il existe des conventions plus ou moins contraignantes. § TOUJOURS bien indenter ! § Un certain nombre de conventions générales (proposées par Sun) sont adoptées par la très grande majorité des développeurs Java. • Accolade ouvrante placée en fin de ligne • Indentation de 2 ou 4 espaces • Accolade fermante seule sur la ligne et alignée sur le début de la ligne contenant l'accolade ouvrante qui lui correspond § D'autres (généralement plus contraignantes) sont imposées dans le cadre de certains projets afin de garantir une certaine homogénéité des codes sources indépendamment de leur auteur (et assurer une stabilité dans le temps). EIA-FR / Jacques Bapst PR1_15 PR1_15 if (a > b) { nb = 1; } else { nb = -1; ok = false; } § Aligner les instructions lorsque cela a un sens (lisibilité). 2 EIA-FR / Jacques Bapst PR1_15 4 Java / Conventions de codage Java / Conventions de codage Commentaires Choix des identificateurs [1] § Pour les commentaires généraux utiliser de préférence la notation "// texte" (même pour les commentaires qui s'étendent sur plusieurs lignes). Type d'identificateur Convention de nommage § Utiliser la notation "/* texte */" pour inactiver temporairement une portion de code. Packages Uniquement des mots en minuscules séparés com.ibm.javadev par des points (niveaux hiérarchiques). ch.eif.project.i02 Si possible, utilisation d'URL inversé. § Pour les applications (ou librairies) d'une certaine envergure, des commentaires de documentation ("/** texte */") devraient précéder chaque classe et chaque membre public (champs et méthodes) de la classe. Classes Un ou plusieurs noms (substantifs). Notation Mixed-Case (Camel-Case) avec première lettre en majuscule. Raster StringTokenizer Interfaces Un ou plusieurs nom ou adjectifs. Notation Mixed-Case (Camel-Case) avec première lettre en majuscule. Printable ActionListener Méthodes (procédure) Un verbe (év. avec compléments) Notation Mixed-Case (Camel-Case) avec première lettre en minuscule. runProcess() draw() Méthodes (fonctions) Un ou plusieurs noms ou noms qualifiés (éventuellement précédés de get ou is). Notation Mixed-Case (Camel-Case) avec première lettre en minuscule. maxWidth() isVisible() § Pour les exercices, on pourra se contenter d'un en-tête précédant chaque méthode (au minimum une ligne séparatrice). § Inutile d'ajouter des commentaires triviaux : int counter; i++; // Variable compteur // i = i + 1 EIA-FR / Jacques Bapst PR1_15 5 Java / Conventions de codage Exemple EIA-FR / Jacques Bapst PR1_15 7 Java / Conventions de codage Choix des identificateurs [1] Choix des identificateurs [2] § D'une manière générale, les identificateurs doivent être autodescriptifs sans toutefois être trop longs (< ~30 caractères). Type d'identificateur Convention de nommage Exemple § Les variables temporaires peuvent faire exception à cette règle. Par exemple les compteurs de boucle sont fréquemment déclarés avec un seul caractère (traditionnellement i, j, k, m, n pour les entiers). Champs Variables locales Paramètres Un ou plusieurs noms ou noms qualifiés. Notation Mixed-Case (Camel-Case) avec première lettre en minuscule. position maxSpeed § Pour les identificateurs composés de plusieurs mots on adoptera généralement la notation dite Mixed-Case ou Camel-Case où les mots sont écrits en minuscules exceptée la première lettre des mots lors de la transition d'un mot à l'autre (la première lettre du premier mot pouvant être en majuscule ou en minuscule selon les cas). Constantes (static final) Un ou plusieurs noms ou noms qualifiés. Mots en majuscules séparés par le caractère souligné ('_'). PI MAX_LENGTH Énumérations (enum) Un ou plusieurs noms ou noms qualifiés. Mots en majuscules séparés par le caractère souligné ('_'). GREEN ROTATION_MODE Types génériques Une seule lettre, en majuscule. (<…>) aMixedCaseIdentifier SomeOther <K, V> § Dans la notation Mixed-Case on n'utilise généralement pas d'autres caractères de séparation. EIA-FR / Jacques Bapst PR1_15 6 EIA-FR / Jacques Bapst PR1_15 8