--- title : 3. Introduction au langage Java tags : JAVA --- # III. Introduction au langage Java ## Cycle de vie d'un programme Java Le cycle de vie d'un programme java passe par 3 phases : 1. Création des fichiers sources (Ecriture du Code Source) 2. Compilation 3. Exécution (Interprétation) ```graphviz digraph hierarchy { nodesep=1.0 // increases the separation between nodes node [color=Red,fontname=Courier,shape=box] //All nodes will this shape and colour edge [color=Blue, style=dashed] //All the lines look like this {rank=same; "Code Source"-> Compilation -> "Exécution (Interprétation)"} } ``` ### 1. Création des fichiers sources (Code Source) - Un programmeur Java écrit son code source, sous la forme de classes, dans des fichiers texte dont l’extension est **.java**. - pour créer un fichier source, un éditeur de texte (le simple Bloc-notes de windows, Notepad, SublimText, VsCode ..etc) ou un environnement de programmation (IDEs comme Eclipse ou NetBeans) peut être utiliser. ### 2. Compilation du code source - Pour pouvoir exécuter un programme java, il doit être *compilé* c.-à-d. traduit dans un langage que l’ordinateur (le processeur de l'ordinateur) peut comprendre (langage natif). - Un compilateur est un programme qui effectue cette traduction. - En Java, le code source n’est pas traduit directement dans le langage de l’ordinateur; il est d’abord traduit dans un langage intermédiaire appelé « bytecode», langage d’une machine virtuelle (JVM ; Java Virtual Machine) définie par Sun (Oracle); ce dernier est indépendant de l’ordinateur qui va exécuter le programme. - la compilation est possible seulement si le programme est correct syntaxiquement. - Le compilateur appelé ==**javac**== (Java compiler) permet d'effectuer la compilation. Cet outil est disponible au niveau du **JDK** dans **Les outils d’aide au développement (Tools ans Tool APIs)**. <br/> <div style="text-align:center"><img src="https://i.imgur.com/Ag2YJwt.png " /></div> <br/><br/> - Le résultat de la compilation est fichier avec l'extension **.class** appelé « bytecode ». ### 3. Exécution (Interprétation) - Le « bytecode » n’est pas un binaire, il n’est pas exécutable directement par la machine. - Le « bytecode » obtenu par compilation ne peut être exécuté qu’à l’aide de l’interpréteur ==**java**==. Cet outil est disponible au niveau du JDK dans **Les outils d’aide au développement (Tools ans Tool APIs)**. <br/> <div style="text-align:center"><img src="https://i.imgur.com/YMUJqof.png" /></div> <br/><br/> - L’outil ==**java**== a pour rôle de démarrer la **JVM** qui charge le bytecode et le compile à la volée (*compilation JIT* - *Just In Time*) pour obtenir un binaire exécutable directement sur la machine. <br/> <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/uNrgj4q.png" /> Le schéma présente une vue synthétique de ces différentes étapes </div> <br/><br/> ## Le premier programme :::info > :bulb: Il est tentant de créer son premier programme en utilisant Eclipse ou un autre EDI. > [color=#018786] > Cependant, pour bien comprendre les mécanismes de création d’un programme, il est nécessaire de réaliser les différentes étapes manuellement avant de déléguer ce travail à un outil tiers. > *Un simple **Bloc-notes de windows** peut être utilisé pour écrire le code source et une **invite de commandes windows** pour compiler et exécuter.* > [color=#018786] ::: ### Écriture du code - Un programme Java est un ensemble de fichiers texte d’extension ***.java***. Le programme le plus petit contient un seul fichier Java. - Le nom d’un fichier Java correspond au nom de la classe Java qu’il contient. - Le fichier ==**PremierProgramme.java**== contient donc la déclaration d’une *classe* nommée ==**PremierProgramme**==. La structure de ce fichier est la suivante : ```java= public class PremierProgramme { ... } ``` - Un programme Java doit disposer d’une méthode particulière correspondant au point d’entrée du programme. Cette méthode s’appelle ==**main**== et a obligatoirement la structure suivante : ```java= public static void main(String[] args) { ... } ``` - À l’intérieur de cette méthode, il est possible d’écrire le programme. - Le programme le plus classique consiste en l’affichage d’un message. L’instruction pour réaliser cette tâche est la suivante : ```java= System.out.println("Bienvenue au langage java"); ``` - Le premier programme va donc avoir la structure globale suivante : ```java= public class PremierProgramme { public static void main(String[] args) { System.out.println("Bienvenue au langage java"); } } ``` ### Compilation et exécution du code - La compilation est effectuée sur le poste du développeur à l’aide du compilateur javac. - La commande pour compiler un programme composé d’un unique fichier est la suivante : ```javascript javac NOM_DU_FICHIER_JAVA ``` - Exemple : ```javascript javac PremierProgramme.java ``` :::info > :bulb: Cette instruction fonctionne si uniquement l'invite de commandes (Windows) ou le terminal (Linux) est positionnée sur le répertoire contenant le fichier **PremierProgramme.java**. Si ce n’est pas le cas, il faut utiliser la commande ==cd== pour y accéder. > [color=#018786] ::: - En cas de succès, aucun message n’est affiché. En cas de problème, des informations détaillées sont affichées pour vous aider à le résoudre : <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/mdsz8qm.png" /> </div> <br/> *L’exemple indique qu’il manque un point-virgule à la ligne 5 du fichier PremierProgramme.java.* <br/> - Lorsque le fichier est compilé avec succès, l’outil ==javac== génère, par défaut dans le même répertoire, un fichier ==**PremierProgramme.class**==. Ce fichier contient du bytecode, un langage intermédiaire exploitable par la JVM au moment de l’exécution. ### L’exécution - L’exécution est effectuée grâce à l’outil ==java==. Seuls les fichiers **.class** sont utiles à l’exécution. Il n’est pas nécessaire de conserver les fichiers sources (.java) dans l’environnement d’exécution. - La commande est la suivante si le terminal est positionné sur le répertoire contenant le fichier **.class** : ```javascript java NOM_DE_LA_CLASSE ``` > Le nom de la classe correspond au nom de la classe contenant la méthode **main**.[color=#018786] <br/> - Dans l’exemple, la commande est la suivante : ```javascript java PremierProgramme ``` - Le terminal affiche le résultat suivant : ```javascript Bienvenue au langage java ``` <br/><br/> ## **Base du langage Java** :::info > :bulb: La syntaxe du langage Java est à l’origine très inspirée du C et du C++. Ces deux langages de programmation ont également servi de base au C#. Donc si vous connaissez C, C++ ou C# vous retrouverez en Java des structures de langage qui vous sont familières. > [color=#018786] ::: ### **Anatomie d’un programme** - Pour écrire le moindre programme java, il est nécessaire de créer au moins une classe dont la structure doit être la suivante : ```java= public class NomDeLaClasse { //LE CODE DE VOTRE PROGRAMME public static void main(String[] args) { //LE CODE DE VOTRE PROGRAMME } //LE CODE DE VOTRE PROGRAMME } ``` - Le point d’entrée de votre programme est la méthode ==**main**== dont la signature doit être celle présentée dans l’exemple à la ligne 5. Le code de cette méthode est délimité par les accolades ouvrantes et fermantes des lignes 6 et 10. - Cette méthode est incluse dans une classe dont la déclaration est réalisée à la ligne 1. Le code de la classe se situe entre les accolades ouvrantes et fermantes des lignes 2 et 13. <br/> ### **Les instructions** - Java est un langage de programmation impératif. Cela signifie qu’un programme Java se compose d’==*instructions*== (==*statements*==) décrivant les opérations que la machine doit exécuter. - En Java une instruction est délimitée par un point-virgule. ```java= double i = 0.0; i = Math.sqrt(16); ``` <br/> ### **Les variables** #### Introduction - Les variables permettent de mémoriser pendant l’exécution d’une application différentes valeurs utiles à son bon fonctionnement. - Avant d’utiliser des variables dans un programme, il convient de savoir où les déclarer, quel nom leur donner et quel type leur donner. <br/> #### Les emplacements - Les variables en Java peuvent être déclarées n’importe où dans le programme. Il n’y a pas d’emplacements réservés. :::info Une règle à respecter : - les variables doivent être déclarées uniquement entre les accolades délimitant les classes, les interfaces ou les énumérations. Elles ne peuvent pas être en dehors de ces éléments. ::: + Une variable doit obligatoirement être déclarée avant son utilisation dans le code. + En fonction de l’emplacement de sa déclaration, une variable appartiendra à une des catégories suivantes : - Déclarée à l’intérieur d’une classe, la variable est une **variable d’instance**. Elle n’existera que si une instance de la classe est disponible (autrement dit un objet). Chaque instance de classe aura son propre exemplaire de la variable. - Déclarée à l’intérieur d’une classe avec le modificateur **static**, un mot-clé du langage, la variable est une **variable de classe**. Elle est accessible directement par le nom de la classe et n’existe qu’en un seul exemplaire, quel que soit le nombre d’objets créés à partir de cette classe. - Déclarée à l’intérieur d’une méthode, la variable est une **variable locale**. Elle n’existe que pendant l’exécution de la méthode et n’est accessible que par le code de celle-ci. - Les **paramètres** des méthodes peuvent être considérés comme des variables locales. La seule différence réside dans l’initialisation de la variable qui est effectuée lors de l’appel de celle-ci. <br/> ```java= public class EmplacementDesVariables { //VARIABLES D'INSTANCE //VARIABLES DE CLASSE ... uneMethode(/*PARAMETRES*/) { //VARIABLES LOCALES } } ``` <div style="text-align:center; font-style: italic;"> Cet exemple de structure de programme permet de visualiser l’emplacement des différentes catégories de variables </div> <br/><br/> #### Le nom + Le nom des variables doit respecter certaines règles même si une assez grande liberté est accordée au développeur : - Le nom d’une variable commence **obligatoirement par une lettre**. ``` Exemple : Nom ``` - Il peut être constitué de **lettres**, de **chiffres** ou du **caractère souligné (_)**. ``` Exemple : Date_2_Naissance ``` - Il peut contenir un **nombre quelconque de caractères** (en pratique, il vaut mieux se limiter à une taille raisonnable). - Il y a une distinction entre minuscules et majuscules. ``` La variable AGEDUCAPITAINE est différente de la variable ageducapitaine ``` - Les mots-clés du langage ne doivent pas être utilisés comme nom de variable. ``` Exemple de mots-clés : class, static, abstract, return, public ...etc ``` - Par convention, le camelCase est utilisé. - le nom des variables est orthographié en lettres minuscules sauf la première lettre de chaque mot (à partir du second mot) si le nom de la variable en comporte plusieurs. ``` Exemple : DateDeNaissance ``` <br/> #### **Les types** <br/> > #### Présentation > [color=#018786] + En spécifiant un type pour une variable, nous indiquons : + Le type d’informations à stocker dans cette variable. + Et par conséquent les opérations à effectuer dessus. + Deux catégories de types de variables sont disponibles : - *Les types valeurs* : la variable contient réellement les informations. - *Les types références* : la variable contient l’adresse mémoire où se trouvent les informations. <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/9TMGITn.png"/> Le schéma suivant résume ce concept. Il représente la mémoire d’une application et les variables qu’elle utilise. </div> > - La variable nommée **variableDeTypeValeur** est associée à un espace mémoire contenant directement la valeur. Dans l’exemple, la valeur est un numérique entier (55). > - La variable nommée **variableDeTypeReference** est associée à un espace mémoire contenant une adresse d’un autre espace mémoire. Cette adresse est matérialisée par le @. À cette adresse se trouve un espace mémoire contenant lui-même deux informations : une variable **variableA** de type valeur et une variable **variableB** de type référence. *Derrière ce mécanisme se cache la notion de pointeurs largement utilisée par les développeurs C++ notamment. C’est la représentation classique d’un objet*. <br/><br/> > #### **Les types valeurs** > [color=#018786] + Les types valeurs représentent les types de plus bas niveau à partir desquels il est possible de manipuler n’importe quelle information, aussi complexe soit-elle. + Ils sont représentés par les types primitifs. + Java met à disposition **sept types primitifs** regroupés en quatre catégories : + les numériques entiers, + les numériques décimaux, + les caractères + les booléens. > Avec ces quatre catégories, il est possible de manipuler n’importe quelle information de base. <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/bbvR3Gv.png"/> </div> <br/> > #### **Les types références** > [color=#018786] + Lorsque l’information à manipuler est plus complexe, une variable de type référence est plus adaptée. + Une variable de type référence permet de regrouper un ensemble de variables cohérentes entre elles. > Exemple d’une date. > - Une date ne peut pas être représentée par un type primitif, car il faut une **variable pour l’année**, une **variable pour le mois** et une **variable pour le jour**. > - Une date est représentée par l’utilisation de **trois variables de type numérique entier**. <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/u66XBEQ.png"/> </div> <br/><br/> #### **La déclaration, l’initialisation, l’affectation** <br/> > #### **La déclaration** > [color=#018786] - La déclaration d’une variable doit respecter la structure suivante : ```java [modificateur] type nomDeLaVariable; ``` *Après d’éventuels modificateurs, une variable est déclarée en définissant son type suivi de son nom. La déclaration se termine par un point-virgule.* - Il est possible de déclarer plusieurs variables du même type sur la même ligne en séparant le nom des variables par une virgule : ```java [modificateur] type variable1, variable2, ...; ``` <br/> Exemple de déclaration : ```java= int age; String nom; Date aujourdhui, hier; ``` <br/> > #### **L’initialisation** > [color=#018786] + L’initialisation est l’étape par laquelle la première valeur à une variable est fournie . + L’initialisation peut être réalisée au même moment que la déclaration : ```java [modificateur] type nomDeLaVariable = valeur; ``` + L’initialisation peut être réalisée après la déclaration de la variable : ```java [modificateur] type nomDeLaVariable; nomDeLaVariable = valeur; ``` *L’opérateur nécessaire pour initialiser une variable est le **=***. ***valeur**, dans l’exemple, peut prendre différentes formes :* + Une valeur littérale : ```java= age = 50; ``` + Une autre variable : ```java= int agePremierEnfant = 35; int ageSecondEnfant = agePremierEnfant; ``` La représentation de ce cas de figure en mémoire est le suivant : <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/3JkavJY.png"/> </div> <br/> > Observation : > - chaque variable gère sa valeur indépendamment de l’autre. > - Il y a eu une copie de la valeur de la variable *agePremierEnfant* dans l’espace mémoire alloué à la variable *ageSecondEnfant*. > *Si l'age de l'un des enfants est modifié, la valeur n’est pas reportée pour l’autre enfant. C’est le comportement observé pour tous les types valeurs.* <br/><br/> > #### **L’affectation** > [color=#018786] + L’affectation est l’étape par laquelle la valeur d’une variable est changée. + *L’initialisation peut donc être considérée comme une affectation particulière*. + L’affectation d’une variable respecte la déclaration suivante : ```java nomDeLaVariable = valeur; ``` <br/><br/> #### **Les types valeurs** <br/> > #### **Les types numériques entiers** > [color=#018786] + Il existe *quatre types* différents pour manipuler des numériques. + Chacun de ces types utilise un espace de stockage différent permettant de manipuler des valeurs plus ou moins grandes. + Le tableau suivant présente ces quatre types entiers signés avec les valeurs minimales et maximales acceptées et l’espace de stockage utilisé : |Type | Valeur minimale |Valeur maximale | Espace de stockage| |-----|---------------------|----------------------|-------------------| |byte | -128 | 127 | 8 bits | |short| -32768 | 32767 | 16 bits | |int | -2147483648 | 2147483647 | 32 bits | |long | -9223372036854775808| 9223372036854775807| 64 bits | <br/> :::info > :bulb: Lorsque vous choisissez un type pour vos variables entières, vous devez prendre en compte les valeurs minimales et maximales que vous envisagez de stocker dans la variable afin d’optimiser la mémoire utilisée par la variable. > > - Il est en effet inutile d’utiliser un type long pour une variable dont la valeur n’excédera pas 50, un type byte est dans ce cas suffisant. > > - *L’économie de mémoire semble dérisoire pour une variable unique, mais devient appréciable lors de l’utilisation de tableaux de grande dimension.* ::: <br/> Exemple : - La classe **GestionValeurs** a pour objectif d’additionner des valeurs numériques entières. - Deux variables locales de type int et long sont déclarées dans la méthode *main* pour répondre au besoin : ```java= public class GestionValeurs { public static void main(String[] args) { int valeur; long sommeValeur; } } ``` > - La variable **valeur** acceptera successivement des valeurs entre **-2147483648** et **2147483647**. > > - Cette valeur sera ensuite ajoutée à la variable **sommeValeur**. > > - Si l’analyse fonctionnelle indique que la somme peut dépasser la limite d’un numérique entier de type *int*, il convient de déclarer la variable **sommeValeur** avec le type *long*. Si ce n’est pas le cas, le type *int* peut être suffisant. > > - *Tous les types entiers sont signés, c’est-à-dire qu’ils permettent de gérer des valeurs positives et négatives.* > Il est cependant possible de travailler avec des valeurs entières non signées en utilisant les classes **Integer** et **Long**. Ceci permet d’étendre la valeur positive maximale admissible jusqu’à **4294967296** pour un type *int* et jusqu’à **18446744073709551616** pour un type *long*. <br/><br/> > #### **Les types numériques décimaux** > [color=#018786] - Types numériques signés |Type | Valeur minimale positive |Valeur maximale positive|Espace de stockage| |--------|--------------------------|------------------------|------------------| | float | 1.4E-45 | 3.4028235E38 | 32 bits | |double | 4.9E-324 | 1.7976931348623157E308 | 64 bits | <br/> - Tous les types décimaux sont signés et peuvent donc contenir des valeurs positives ou négatives. - Les variables de types *float* et *double* occupent le même espace de stockage que les variables de types *int* et *long*. Cependant, elles sont capables de stocker des valeurs d’une plage beaucoup plus grande. Ceci est possible en sacrifiant la précision. En effet, ces types décimaux ne sont que des approximations. - Le type *float* enregistre au maximum **huit chiffres significatifs** et le type *double* **17 chiffres significatifs**. <br/><br/> > #### **L’utilisation de valeurs littérales** > [color=#018786] Il est très fréquent d’initialiser les variables d’un type numérique avec une valeur littérale : ```java= int populationFrancaise = 66990000; double pourcentageFemme = 51.649; ``` > Le compilateur (javac) interprète les *valeurs numériques entières* comme des valeurs de type **int** et les *valeurs numériques décimales* comme des valeurs de type **double**. > > En conséquence, les deux déclarations ci-dessus compilent. <br/> Dans les déclarations suivantes, une erreur de compilation intervient : ```java= byte b = 153; ``` > Le compilateur indique l’erreur suivante : <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/R33Pc2E.png"/> </div> <br/> > - La valeur 153 est en dehors de la plage de valeurs acceptable par une variable de type byte. > > - Le compilateur indique qu’il est impossible pour lui de convertir cette valeur de type int et en byte. > > - Si la valeur définie est comprise dans la plage du type, le problème disparaît. > > - Le même comportement est observable pour le type short lorsque l’on tente d’initialiser la variable avec une valeur en dehors de la plage acceptable par ce type. <br/> - La même erreur survient lorsque le développeur tente d’initialiser une variable de type **float** avec une valeur littérale décimale (considérée comme un type double). > La différence est que le compilateur lèvera une erreur même si la valeur est dans la plage acceptable du type **float** comme dans l’exemple suivant : ```java= float f = 10.1; ``` > Le compilateur indique l’erreur suivante : <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/g0qd6lC.png"/> </div> <br/> > Pour corriger ce problème, il faut indiquer au compilateur d’interpréter la valeur littérale comme une valeur de type float. > > Pour réaliser cette opération, il suffit de suffixer le nombre par la lettre **F** (pour float) en majuscule ou minuscule : ```java= float f = 10.1F; ``` <br/> - Le deuxième cas de figure où il est possible de rencontrer une erreur est lorsque le développeur souhaite initialiser une variable de type **long** avec une valeur numérique dépassant la plage de valeurs acceptables pour une variable de type **int**. ```java= long l = 10123456789; ``` > Le compilateur interprète les valeurs littérales numériques entières comme des valeurs de type **int**. L’erreur rencontrée avec l’exemple précédent est la suivante : <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/Lsteufr.png"/> </div> <br/> > Pour corriger ce problème, il faut indiquer au compilateur d’interpréter la valeur littérale comme une valeur de type **long**. > > Pour réaliser cette opération, il suffit de suffixer le nombre par la lettre **L** (pour long) en majuscule ou minuscule : ```java= long l = 10123456789L; ``` <br/> - Les valeurs numériques décimales peuvent être exprimées avec la notation décimale ou la notation scientifique. ```java= float surface=2356.8f; float surface=2.3568e3f; ``` - Les caractères **(_)** peuvent être insérés dans les valeurs numériques littérales pour faciliter la lecture. Les deux syntaxes suivantes sont équivalentes. ```java= long quantite=1234876567; long quantite=1_234_876_567; ``` <br/><br/> > #### **Le type caractère** > [color=#018786] Le type **char** est utilisé pour stocker un caractère unique. Une variable de type **char** utilise *deux octets* pour stocker le code *Unicode* du caractère. Dans le jeu de caractères **Unicode**, les 128 premiers caractères sont identiques au jeu de caractères **ASCII**, les caractères suivants, jusqu’à 255, correspondent aux caractères spéciaux de l’alphabet latin (par exemple les caractères accentués), le reste est utilisé pour les symboles ou les caractères d’autres alphabets. ```java= char caractereC = 'c'; ``` Les caractères spécifiques ou ceux ayant une signification particulière pour le langage Java sont représentés par une séquence d’échappement. Elle est constituée du caractère \ suivi d’un autre caractère indiquant la signification de la séquence d’échappement. Le tableau suivant présente la liste des séquences d’échappement et leurs significations. |Séquence | Signification | |---------|-------------------| | \t | Tabulation | | \b | BackSpace | | \n | Saut de ligne | | \r | Retour chariot | | \f | Saut de page | | \’ | Simple quote | | \" | Double quote | | \\ | Antislash | <br/> Les caractères Unicode non accessibles au clavier sont eux aussi représentés par une séquence d’échappement constituée des caractères **\u** suivis de la valeur hexadécimale du code Unicode du caractère. Le symbole **euro** est par exemple représenté par la séquence **\u20AC**. ```java= char euro1 = '€'; char euro2 = '\u20AC'; ``` <br/> :::info Pour acquérir des connaissances sur les *caractères Unicode*, vous pouvez consulter la ressource Wikipedia [**cliquez ici**](https://fr.wikipedia.org/wiki/Table_des_caractères_Unicode) ::: <br/><br/> > #### **Le type boolean** > [color=#018786] Le type boolean permet d’avoir une variable qui peut prendre deux états : **vrai**/**faux**, **oui**/**non**, **on**/**off**. L’affectation se fait directement avec les valeurs **true** ou **false** : ```java= boolean disponible, modifiable; disponible=true; modifiable=false; ``` :::danger **Il est impossible d’affecter une autre valeur à une variable de type *boolean*.** ::: <br/><br/> #### **Les types références** <br/> > #### **. Les chaînes de caractères** > [color=#018786] Pour pouvoir stocker des chaînes de caractères, il faut utiliser le type **String** qui représente une *suite de zéro à n caractères*. Ce type n’est pas un type de base, mais une classe. Cependant, pour faciliter son emploi, il peut être utilisé comme un type de base du langage. Pour affecter une chaîne de caractères à une variable, il faut saisir le contenu de la chaîne entre guillemets : ```java= String nomDuFormateur = "christian"; ``` > La représentation mémoire de cette variable peut être matérialisée ainsi : <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/SVJSAgt.png"/> </div> <br/> ##### Créer une chaîne de caractères La méthode la plus simple pour créer une chaîne de caractères consiste à considérer le type **String** comme un type de base du langage et non comme un type objet. C’est dans ce cas l’affectation d’une valeur à la variable qui va provoquer la création d’une instance de la classe String. *La création d’une chaîne de caractères comme un objet est bien sûr également possible en utilisant l’opérateur **new** et un des nombreux constructeurs disponibles dans la classe String.* ```java= // méthode simple String chaine1="dtaLearning"; // méthode objet String chaine2=new String("dtaLearning"); ``` <br/> ##### Affecter une valeur à une chaîne Pour affecter une valeur à une chaîne, il faut la spécifier entre guillemets. Un problème se pose donc si le caractère **"** fait partie de la chaîne. Pour qu’il ne soit pas interprété comme caractère de début ou de fin de chaîne, il faut le protéger par une séquence d’échappement (en utilisant le caractère \) : ```java= String chaine; chaine=" il a dit : \" ça suffit ! \""; System.out.println(chaine); ``` > Le résultat à l’affichage est le suivant : ``` il a dit : "ça suffit ! " ``` <br/> ##### Extraire un caractère particulier Pour obtenir le caractère présent à une position donnée d’une chaîne de caractères, il faut utiliser la fonction **charAt** en fournissant comme argument l’index du caractère que l’on souhaite obtenir. Le premier caractère est à l’index zéro. Cette fonction retourne un caractère (char). Exemple : ```java= String chaine1 = "l'hiver sera pluvieux"; String chaine2 = "l'hiver sera froid"; System.out.println("le 3ème caractère de la chaine 1 est " + chaine1.charAt(2)); ``` > L’exécution de cette instruction donne le résultat suivant : ``` le 3ème caractère de la chaine 1 est h ``` <br/> ##### Obtenir la longueur d’une chaîne Pour déterminer la longueur d’une chaîne, la fonction **length** de la classe String est disponible. Exemple : ```java= String chaine1 = "l'hiver sera pluvieux"; String chaine2 = "l'hiver sera froid"; System.out.println("la chaine 1 contient " + chaine1.length() + " caractères"); ``` > L’exécution de cette instruction donne le résultat suivant : ``` la chaine 1 contient 21 caractères ``` <br/> ##### Découper une chaîne La fonction **substring** de la classe *String* retourne une portion d’une chaîne de caractères en fonction de la position de départ et de la position de fin qui lui sont passées comme paramètres. La chaîne obtenue commence par le caractère situé à la position de départ et se termine au caractère précédant la position de fin. ```java= String chaine1 = "l'hiver sera pluvieux"; String chaine2 = "l'hiver sera froid"; System.out.println("un morceau de la chaine 1 : " + chaine1.substring(2,7)); ``` > L’exécution de cette instruction donne le résultat suivant : ``` un morceau de la chaine 1 : hiver ``` <br/> ##### Tester l’égalité de deux chaînes Lorsqu’on fait une comparaison de deux chaînes, on est tenté d’utiliser le double égal (**==**), comme pour les types valeurs. Cet opérateur fonctionne correctement sur les types de base, mais il ne faut pas perdre de vue que les chaînes de caractères sont des types objet. L’opérateur **==** *vérifie l’égalité des variables (donc les valeurs pour les types valeurs, mais les adresses pour les types références)*. Il faut donc utiliser les méthodes de la classe String pour effectuer des comparaisons de chaînes de caractères. - La méthode **equals** effectue une comparaison de la chaîne avec celle qui est passée en paramètre. Elle retourne - un booléen égal à *true* si les deux chaînes sont identiques (en valeur) - et bien sûr un booléen égal à *false* dans le cas contraire. :::info - La fonction **equals** fait une distinction entre minuscules et majuscules lors de la comparaison. - La fonction **equalsIgnoreCase** effectue un traitement identique, mais sans tenir compte de cette distinction. ::: Exemple : ```java= String chaine1 = "l'hiver sera pluvieux"; String chaine2 = "l'hiver sera froid"; // Comparaison if(chaine1.equals(chaine2)) { System.out.println("les deux chaines sont identiques"); } else { System.out.println("les deux chaines sont différentes"); } ``` <br/> Dans certains cas, l’opérateur **==** est bien capable de réaliser une comparaison correcte de chaînes de caractères. Exemple : ```java= String s1="toto"; String s2="toto"; if(s1==s2) { System.out.println("chaines identiques"); } else { System.out.println("chaines différentes"); } ``` > Le code fonctionne correctement et fournit bien le résultat attendu en considérant que les deux chaînes sont identiques. > > En fait, pour économiser de l’espace en mémoire, Java n’utilise dans ce cas qu’une seule instance de la classe **String** pour les variables **s1** et **s2** car le contenu des deux chaînes est identique. > > Les deux variables **s1** et **s2** référencent donc la même zone mémoire et l’opérateur **==** constate donc l’égalité (des adresses). <br/> Si en revanche nous utilisons le code suivant qui demande explicitement la création d’une instance de la classe **String** pour chacune des variables **s1** et **s2**, l’opérateur **==** ne constate bien sûr plus l’égalité des chaînes : ```java= //Déclaration String s1=new String("toto"); String s2=new String("toto"); // Comparaison if(s1==s2) { System.out.println("chaines identiques"); } else { System.out.println("chaines différentes"); } ``` <br/><br/> ##### Comparer deux chaînes Pour réaliser une comparaison, la méthode **compareTo** de la classe *String* peut être utilisée. La méthode **compareToIgnoreCase** peut être aussi utilisée si on ne souhaite pas prendre en compte la casse. Ces deux méthodes attendent en paramètres la chaîne à comparer avec l’instance courante sur laquelle on appelle la méthode. Le résultat de la comparaison est retourné sous forme d’un entier : - inférieur à zéro si la chaîne est inférieure à celle reçue comme paramètre, - égal à zéro si les deux chaînes sont identiques, - et supérieur à zéro si la chaîne est supérieure à celle reçue comme paramètre. Exemple : ```java= String chaine1 = "l'hiver sera pluvieux"; String chaine2 = "l'hiver sera froid"; // Comparaison if(chaine1.compareTo(chaine2)>0) { System.out.println("chaine1 est supérieure a chaine2"); } else if(chaine1.compareTo(chaine2)<0) { System.out.println("chaine1 est inférieure a chaine2"); } else { System.out.println("les deux chaines sont identiques"); } ``` <br/> Les fonctions **startsWith** et **endsWith** permettent de tester si la chaîne débute par la chaîne reçue en paramètre ou si la chaîne se termine par la chaîne reçue en paramètre. La fonction **endsWith** peut par exemple être utilisée pour tester l’extension d’un nom de fichier. ```java= String nom="Code.java"; if(nom.endsWith(".java")) { System.out.println("c\'est un fichier source java"); } ``` <br/><br/> ##### Supprimer les espaces La fonction **trim** permet de supprimer les espaces situés avant le premier caractère significatif et après le dernier caractère significatif d’une chaîne. Exemple : ```java= String dta =" dtaLearning "; System.out.println("longueur de la chaine : " + dta.length()); System.out.println("longueur de la chaine nettoyée : " + dta.trim().length()); ``` <br/><br/> ##### Changer la casse Les méthodes toUpperCase et toLowerCase peuvent être utilisées. Exemple : ```java= String chaine1 = "Formation Java avec Dta Learning"; // Tout en majuscules : System.out.println(chaine1.toUpperCase()); // Tout en minuscules : System.out.println(chaine1.toLowerCase()); ``` <br/><br/> ##### Rechercher dans une chaîne La méthode **indexOf** de la classe **String** permet la recherche d’une chaîne à l’intérieur d’une autre. Le paramètre correspond à la chaîne recherchée. La fonction retourne : - un entier indiquant la position à laquelle la chaîne a été trouvée - ou -1 si la chaîne n’a pas été trouvée. Par défaut, la recherche commence au début de la chaîne, sauf si une autre version de la fonction **indexOf** est utilisée, elle, attend deux paramètres : - le premier paramètre étant la chaîne recherchée - le deuxième la position de départ de la recherche. Exemple : ```java= String recherche; int position; recherche = "e"; position = chaine1.indexOf(recherche); while (position >= 0) { System.out.println("chaine trouvée à la position " + position); position = chaine1.indexOf(recherche,position+1); } System.out.println("fin de la recherche"); ``` > Nous obtenons à l’affichage : ``` chaîne trouvée à la position 5 chaîne trouvée à la position 9 chaîne trouvée à la position 18 fin de la recherche ``` <br/><br/> ##### Remplacer une partie d’une chaîne Il est parfois souhaitable de pouvoir rechercher la présence d’une chaîne à l’intérieur d’une autre, comme dans l’exemple précédent, mais également remplacer les portions de chaîne trouvées. La fonction **replace** permet de spécifier une chaîne de substitution pour la chaîne recherchée. Elle attend deux paramètres : - la chaîne recherchée, - la chaîne de remplacement. Exemple : ```java= String chaine1 = "l'hiver sera pluvieux"; String chaine2 = "l'hiver sera froid"; String chaine3; chaine3 = chaine1.replace("hiver", "été"); System.out.println(chaine3); ``` > Nous obtenons à l’affichage : ``` l'été sera pluvieux ``` <br/><br/> ##### Formater une chaîne La méthode **format** de la classe **String** permet d’éviter de longues et fastidieuses opérations de conversion et de concaténation. C’est une méthode de classe et non une méthode d’instance. Le premier paramètre attendu par cette fonction est une chaîne de caractères spécifiant sous quelle forme on souhaite obtenir le résultat. - Cette chaîne contient un ou plusieurs motifs de formatage représentés par le caractère %, suivis d’un caractère spécifique indiquant sous quelle forme doit être présentée l’information. - Il doit ensuite y avoir autant de paramètres qu’il y a de motifs de formatage. - La chaîne renvoyée est construite par le remplacement de chacun des motifs de formatage par la valeur du paramètre correspondant, le remplacement se faisant dans l’ordre d’apparition des motifs. Le tableau suivant présente les principaux motifs de formatage disponibles. |Motif | Description | |-------|---------------------------------------------------------------------| | %b | Insertion d’un booléen | | %s | Insertion d’une chaîne de caractères | | %d | Insertion d’un nombre entier | | %o | Insertion d’un entier affiché en octal | | %x | Insertion d’un entier affiché en hexadécimal | | %f | Insertion d’un nombre décimal | | %e | Insertion d’un nombre décimal affiché au format scientifique | | %n | Insertion d’un saut de ligne. Équivalent à la séquence \n mais ne fonctionne que dans les méthodes de formatage comme format. | Voici un exemple de code utilisant cette fonction : ```java= boolean b=true; String s="chaine"; int i=56; double d=5.5; System.out.println(String.format("boolean : %b %n" + "chaine de caractères : %s %n" + "entier : %d %n" + "entier en hexadécimal : %x %n" + "entier en octal : %o %n" + "décimal : %f %n" + "décimal précis au dixième : %.1f %n" + "décimal au format scientifique : %e %n", b,s,i,i,i,d,d,d)); ``` > Remarquez la séquence **%.1f** pour l’affichage en décimal. > Cette instruction permet d’indiquer le nombre de chiffres significatifs souhaités après la virgule. > L’exécution de ce code donne le résultat suivant : ``` boolean : true chaine de caractères : chaine entier : 56 entier en hexadécimal : 38 entier en octal : 70 décimal : 5,500000 décimal précis au dixième : 5,5 décimal au format scientifique : 5.500000e+00 ``` <br/><br/> #### Les valeurs par défaut L’initialisation des variables n’est pas toujours obligatoire. C’est par exemple le cas pour les variables d’instance et de classes qui sont initialisées avec les valeurs par défaut suivantes. |Type | Valeur par défaut| |-----------------------|------------------| | byte | 0 | | short | 0 | | int | 0 | | long | 0 | | float | 0.0 | | double | 0.0 | | char | \u0000 | | boolean | false | | Les types références | null | <br/> En revanche, les variables locales doivent être initialisées avant d’être utilisées. Le compilateur effectue d’ailleurs une vérification lorsqu’il rencontre l’utilisation d’une variable locale et déclenche une erreur si la variable n’a pas été initialisée. <br/><br/> #### La portée des variables La portée d’une variable est la portion de code à partir de laquelle on peut manipuler cette variable. Elle est fonction de l’emplacement où est située la déclaration. Cette déclaration peut être faite dans : - le bloc de code d’une classe, - le bloc de code d’une fonction, - un bloc de code à l’intérieur d’une fonction. Une variable ne peut être utilisée que dans le bloc où elle est déclarée et dans les sous-blocs. Si le même bloc de code est exécuté plusieurs fois pendant l’exécution de la fonction, cas d’une boucle while par exemple, la variable sera créée à chaque passage dans la boucle. Il ne peut pas y avoir deux variables portant le même nom avec la même portée. Il y a cependant possibilité de déclarer une variable interne à une fonction, ou un paramètre d’une fonction avec le même nom qu’une variable déclarée au niveau de la classe. Le principe de localité est alors mis en œuvre. Ainsi, la variable locale est prioritaire sur la variable déclarée au niveau de la classe. Pour accéder à cette variable, il faut la préfixer par le mot-clé this (dans le cas d’une variable d’instance) ou par le nom de la classe (dans le cas d’une variable de classe). ```java= public class NomDeLaClasse { int maVariableDInstance; static int maVariableDeClasse; ... uneMethode(PARAMETRES) { int maVariableLocale; if(expression booléenne) { int maVariableDeBloc; } } } ``` <br/> > Les variables **maVariableDInstance** et **maVariableDeClasse** sont accessibles dans toute la classe (toutes les méthodes et tous les blocs à l’intérieur des méthodes). > > La variable **maVariableLocale** est accessible depuis sa déclaration jusqu’à la fin de la méthode dans laquelle elle est déclarée (dans le bloc dans lequel elle est déclarée et dans tous les blocs à l’intérieur de la méthode). > > La variable **maVariableDeBloc** est accessible depuis sa déclaration jusqu’à la fin du bloc dans lequel elle est déclarée (elle est aussi accessible dans les sous-blocs de ce bloc). <br/><br/> #### La conversion de type <br/> > #### **. La conversion entre numériques** > [color=#018786] La conversion entre numériques consiste à transformer une variable d’un type dans un autre type. La conversion peut se faire vers un type supérieur ou vers un type inférieur. Si une conversion vers un type inférieur est utilisée, il risque d’y avoir une perte d’information. Par exemple, la conversion d’un type **double** vers un type **long** fera perdre au minimum la partie décimale de la valeur. C’est pour cette raison que le compilateur exige dans ce cas que vous indiquiez explicitement que vous souhaitez effectuer cette opération. Pour cela, vous devez préfixer l’élément que vous souhaitez convertir avec le type que vous voulez obtenir en plaçant celui-ci entre parenthèses. ```java= float surface; surface=2356.8f; //Conversion implicite double grandeSurface=surface; //Conversion explicite int approximation=(int)surface; ``` <br/> > La variable **approximation** contient la valeur 2356. > > La conversion vers un type supérieur est sans risque de perte d’information et peut donc se faire directement par une simple affectation. <br/> <div style="text-align:center; font-style: italic;"> <img src="https://i.imgur.com/NAZcQXs.png"/> </div> > Remarque : > - la présence du type **char**. Pour rappel, un **char** est stocké sous la forme du code Unicode sur deux octets. <br/><br/> > #### **. La conversion vers une chaîne de caractères** > [color=#018786] Les fonctions de conversion vers le type **chaîne de caractères** sont accessibles par l’intermédiaire de la classe **String**. La méthode de classe **valueOf** assure la conversion d’une valeur d’un type de base vers une chaîne de caractères. Dans certaines situations, l’utilisation de ces fonctions est optionnelle, car la conversion est effectuée implicitement. C’est le cas par exemple lorsqu’une variable d’un type de base est concaténée avec une chaîne de caractères. Les deux versions de code suivantes ont le même effet. ```java= // Version 1 //------------------- double prixHt; prixHt=152; String recap; recap="le montant de la commande est : " + prixHt*1.20; // Version 2 //-------------------- double prixHt; prixHt=152; String recap; recap="le montant de la commande est : " + String.valueOf(prixHt*1.20); ``` <br/><br/> > #### **.La conversion depuis une chaîne de caractères** > [color=#018786] Il arrive fréquemment qu’une valeur numérique soit disponible dans une application sous forme d’une chaîne de caractères (saisie de l’utilisateur, lecture d’un fichier...). Pour pouvoir être manipulée par l’application, elle doit être convertie en un type numérique. Ce type de conversion est accessible par l’intermédiaire des classes équivalentes aux types de base. Elles permettent la manipulation de valeurs numériques sous forme d’objets. Chaque type de base possède sa classe associée. |Type de base | Classe correspondante| |-------------|----------------------| | byte | Byte | short | Short | int | Integer | long | Long | float | Float | double | Double | boolean | Boolean | char | Character > Ces classes sont appelées ***classes wrappers***, car elles sont utilisées pour « emballer » dans un objet les types de base du langage. > > Elles peuvent être utilisées comme des classes normales en créant une instance à partir de la méthode de classe **valueof**, le constructeur étant déprécié depuis Java 9. > > Différentes surcharges de la méthode **valueOf** sont disponibles : ```java= Integer entier=Integer.valueOf(10); entier=Integer.valueOf("10"); ``` > Cette solution peut être contournée grâce au mécanisme appelé « autoboxing » du compilateur. > > Ce mécanisme permet l’affectation d’un type de base du langage à une variable du type wrapper correspondant. ```java= Integer entier=10; ``` > Attention, le comportement des instances de ces classes wrappers est identique au type String. Elles sont immuables. > > Le mécanisme inverse, appelé « unboxing », permet la conversion automatique d’un type wrapper vers un type de base. La variable entier de l’exemple précédent peut être affectée à une variable de type int. ```java= int x=entier; ``` > Ces classes fournissent une méthode **parseXxx** acceptant comme paramètre une chaîne de caractères et retournant une valeur dans le type de base associé à la classe. |Classe |Méthode | |---------|----------------------------------------------------| | Byte | public static byte parseByte(String s) | Short | public static short parseShort(String s) | Integer | public static int parseInt(String s) | Long | public static long parseLong(String s) | Float | public static float parseFloat(String s) | Double | public static double parseDouble(String s) | Boolean | public static boolean parseBoolean(String s) > Pour retenir comment procéder pour effectuer une conversion, il faut appliquer le principe suivant : > > la méthode à utiliser se trouve dans la classe correspondante au type de données que l’on souhaite obtenir. <br/><br/> ### **Les opérateurs** Les opérateurs sont des mots-clés du langage permettant l’exécution d’opérations sur le contenu de certains éléments, en général des variables, des constantes, des valeurs littérales, ou des retours de fonctions. La combinaison d’un ou de plusieurs opérateurs et d’éléments sur lesquels les opérateurs vont s’appuyer se nomme une expression. Ces expressions sont évaluées au moment de l’exécution en fonction des opérateurs et des valeurs qui sont associées. Deux types d’opérateurs sont disponibles : - Les opérateurs unaires qui ne travaillent que sur un seul opérande. - Les opérateurs binaires qui nécessitent deux opérandes. Les opérateurs peuvent être répartis en sept catégories. <br/> #### **Les opérateurs unaires** |Opérateur | Action | |-----------|----------------------------------------| | - | Valeur négative | ~ | Complément à 1 (inversion des bits) | ++ | Incrémentation | -- | Décrémentation | ! | Négation <br/> > L’opérateur **!** n’est utilisable que sur des variables de type boolean ou sur des expressions produisant un type boolean (comparaison). > Les opérateurs unaires peuvent être utilisés avec la notation préfixée, dans ce cas l’opérateur est placé avant l’opérande, et avec la notation postfixée, dans ce cas l’opérateur placé après l’opérande. > > La position de l’opérateur détermine le moment où celui-ci est appliqué sur la variable. > > Si l’opérateur est préfixé, il s’applique sur l’opérande avant que celui-ci ne soit utilisé dans l’expression. > > Avec la notation postfixée, l’opérateur n’est appliqué sur la variable qu’après utilisation de celle-ci dans l’expression. Cette distinction peut avoir une influence sur le résultat d’une expression. <br/> Exemple 1 : ```java= int i; i=3; System.out.println(i++); ``` > L’exécution de l’exemple suivant affiche 3 car l’incrémentation est exécutée après utilisation de la variable par l’instruction println : Exemple 2 : ```java= int i; i=3; System.out.println(++i); ``` > L’exécution de l’exemple suivant affiche 4 car l’incrémentation est exécutée avant l’utilisation de la variable par l’instruction println : > Si la variable n’est pas utilisée dans une expression, les deux versions conduisent au même résultat. Les deux lignes de code suivantes sont équivalentes : ```java= i++; ++i; ``` <br/> #### **L’opérateur d’affectation** Le seul opérateur disponible dans cette catégorie est l’opérateur **=**. Il permet d’affecter une valeur à une variable. Le même opérateur est utilisé, quel que soit le type de la variable (numérique, chaîne de caractères...). Cet opérateur peut être combiné avec un opérateur arithmétique, logique ou binaire. La syntaxe suivante sont équivalentes : ```java= x+=2; // est équivalente à : x=x+2; ``` <br/> #### **Les opérateurs arithmétiques** Les opérateurs arithmétiques permettent d’effectuer des calculs sur le contenu des variables. Le type du résultat de l’opération correspond au type le plus grand des deux opérandes. |Opérateur | Opération réalisée | Exemple Résultat | |-----------|-----------------------|------------------| |+ | Addition pour des valeurs numériques ou concaténation pour des chaînes | 6+4 |10 | |- Soustraction| 12-06| 6| |* Multiplication| 3*4 |12| |/ Division | 25/03| 8 ou 8.333...| |% Modulo (reste de la division entière) | 25%3 |1 > Il est à noter que pour la division, il y a une subtilité : > > - Si les deux opérandes sont des numériques entiers, la division opérée est une division entière. > > - Ainsi, le résultat de l’opération 25/3 sera 8. > > - En revanche, si au moins un des deux opérandes est un numérique décimal, alors le résultat de la même opération sera 8.333... La précision du résultat dépend du type le plus grand dans l’opération. <br/><br> #### **Les opérations logiques** <br/> > #### **Les opérations logiques** > [color=#018786] Il existe trois opérations de base à connaître : le **Et logique**, le **Ou logique** et le **Ou exclusif**. <br/> ==**Et**== + Le Et permet de réaliser une opération entre deux bits selon la règle suivante : + si les deux bits valent 1 alors le résultat est 1 sinon, le résultat est 0. + Les différentes possibilités sont résumées dans le tableau suivant : |Et |1 |0 | |---|---|--| |1 |1 |0 | |0 |0 |0 | <br/> ==**Ou**== - Le Ou permet de réaliser une opération entre deux bits selon la règle suivante : - si le deux bits valent 0 alors le résultat est 0 sinon, le résultat est 1. - Les différentes possibilités sont résumées dans le tableau suivant : |Ou |1 |0 | |---|---|--| |1 |1 |1| |0 |1 |0| <br/> ==**Ou exclusif**== - Le Ou exclusif permet de réaliser une opération entre deux bits selon la règle suivante : - si un seul des deux bits vaut 1 alors le résultat est 1 sinon, le résultat est 0. - Les différentes possibilités sont résumées dans le tableau suivant : |Ou exclusif |1 |0 | |---------------|---|---| |1 |0 |1 | |0 |1 |0 | <br/><br/> > #### **Les opérateurs logiques ** > [color=#018786] Les opérateurs logiques permettent de combiner les expressions dans des structures conditionnelles ou des structures de boucle. |Opérateur |Opération |Exemple |Résultat | |-----------|-----------|---------------|---------------------------------------| | & |Et logique |test1 & test2 |true si test1 et test2 valent true | | \| |Ou logique |test1 \| test2 |true si test1 ou test2 vaut true | | ^ |Ou exclusif|test1 ^ test2 |true si test1 ou test2 vaut true mais false |si les deux valent true simultanément | |! |Négation |!test1 |Inverse le résultat du test |&& |Et logique |test1 && test2 |Comme le & mais test2 ne sera évalué que si test1 vaut true |\|\| |Ou logique |test1 \|\| test2 |Comme le \| mais test2 ne sera évalué que si test1 vaut false <br/> > Il faudra être prudent avec les opérateurs **&&** et **\|\|** car l’expression que vous testerez en second (test2 dans notre cas) pourra parfois ne pas être exécutée. > > Si cette deuxième expression modifie une variable, celle-ci ne sera modifiée que dans les cas suivants : > - Si test1 vaut true dans le cas du &&. > - Si test1 vaut false dans le cas du \|\|. > > Il est donc préférable d’éviter l’appel de fonctions modifiant certaines informations dans une expression avec des opérateurs logiques. > > L’intérêt des opérateurs **&&** et **||** étant d’éviter des tests inutiles, ils sont à privilégier par rapport à leurs homologues **&** et **|**. <br/> #### **Les opérateurs de comparaison** Les opérateurs de comparaison sont utilisés dans les structures de contrôle d’une application. Ils renvoient une valeur de type **boolean** en fonction du résultat de la comparaison effectuée. Cette valeur sera ensuite utilisée par la structure de contrôle. | Opérateur | Test réalisé | Exemple | Résultat | |-----------| --------------| ----------|-------------| | == | Egalité | 2 == 5 | false | != | Inégalité | 2 != 5 | true | < | Infériorité | 2 < 5 | true | > | Supériorité | 2 > 5 | false | <= | Infériorité ou égalité | 2 <= 5 | true | >= | Supériorité ou égalité | 2 >= 5 | false | instanceof| Comparaison du type de la variable avec le type indiqué | O1 instanceof Client | true si la variable O1 référence un objet créé à partir de la classe client ou d’une sous-classe <br/><br/> #### **L’opérateur de concaténation** L’opérateur **+**, déjà utilisé pour l’addition, est également utilisé pour la concaténation de chaînes de caractères. Le fonctionnement de l’opérateur est déterminé par le type des opérandes. - Si un des opérandes est du type **String**, alors l’opérateur **+** effectue une concaténation avec éventuellement une conversion implicite de l’autre opérande en chaîne de caractères. Le petit inconvénient de l’opérateur **+** est qu’il ne brille pas par sa rapidité pour les concaténations. En fait, ce n’est pas réellement l’opérateur qui est en cause, mais la technique utilisée par Java pour gérer les chaînes de caractères (elles ne peuvent pas être modifiées après création). - Si vous avez de nombreuses concaténations à exécuter sur une chaîne, il est préférable d’utiliser la classe **StringBuilder** (ou **StringBuffer** uniquement si vous travaillez en environnement multi-thread). Exemple ```java= long duree; String lievre; String tortue=""; long debut, fin; debut = System.currentTimeMillis(); for (int i = 0; i <= 10000; i++) { tortue = tortue + " " + i; } fin = System.currentTimeMillis(); duree = fin-debut; System.out.println("durée pour la tortue : " + duree + "ms"); debut = System.currentTimeMillis(); StringBuilder sb = new StringBuilder(); for (int i = 0; i <= 10000; i++) { sb.append(" "); sb.append(i); } lievre = sb.toString(); fin = System.currentTimeMillis(); duree = fin-debut; System.out.println("durée pour le lièvre : " + duree + "ms"); if (lievre.equals(tortue)) { System.out.println("les deux chaînes sont identiques"); } ``` <br/> > Résultat de la course : ``` durée pour la tortue : 249ms durée pour le lièvre : 10ms les deux chaînes sont identiques ``` > Ce résultat se passe de commentaire ! Remarquez tout de même le nombre d’itérations au niveau de la boucle. Pour quelques concaténations successives, l’opérateur + reste adapté. <br/><br/> #### **Ordre d’évaluation des opérateurs** Lorsque plusieurs opérateurs sont combinés dans une expression, ils sont évalués dans un ordre bien précis. Les incrémentations et décrémentations préfixées sont exécutées en premier, puis les opérations arithmétiques, les opérations de comparaison, les opérateurs logiques et enfin les affectations. Les opérateurs arithmétiques ont entre eux également un ordre d’évaluation dans une expression. L’ordre d’évaluation est le suivant : - 1. Négation (-) - 2. Multiplication et division (*, /) - 3. Modulo (%) - 4. Addition et soustraction (+, -), concaténation de chaînes (+) Si un ordre d’évaluation différent est nécessaire dans votre expression, il faut placer les portions à évaluer en priorité entre parenthèses comme dans l’expression suivante : ```java= x= (z * 4) ^ (y * (a + 2)); ``` > Vous pouvez utiliser autant de niveaux de parenthèses que vous le souhaitez dans une expression. > > Il importe cependant que l’expression contienne autant de parenthèses fermantes que de parenthèses ouvrantes sinon le compilateur générera une erreur. <br/><br/> ### **Les structures de contrôle** Les structures de contrôle permettent de contrôler l’exécution des instructions dans le code. Deux types de structures sont disponibles : - ==*Les structures de décision*== : elles aiguilleront l’exécution du code en fonction des valeurs que pourra prendre une expression de test. - ==*Les structures de boucle*== : elles feront exécuter une portion de code un certain nombre de fois, jusqu’à ce qu’une condition soit remplie ou tant qu’une condition est remplie. <br/> #### **Structures de décision** Deux solutions sont possibles : > #### **Structure if** > [color=#018786] Quatre syntaxes sont utilisables pour l’instruction if. ```java= if (condition) instruction; ``` > Si la condition vaut true alors l’instruction est exécutée. > > La condition doit être une expression qui, une fois évaluée, doit fournir un booléen (true ou false). > > Avec cette syntaxe, seule l’instruction située après le if sera exécutée si la condition vaut true. <br/> Pour exécuter plusieurs instructions en fonction d’une condition, il faut utiliser la syntaxe ci-après. ```java= if (condition) { instruction 1; ... instruction n; } ``` > Dans ce cas, le groupe d’instructions situé entre les accolades sera exécuté si la condition vaut true. <br/> On peut également spécifier une ou plusieurs instructions qui seront exécutées si la condition vaut false en ajoutant un bloc else. ```java= if (condition) { instruction 1; ... instruction n; } else { instruction 1; ... instruction n; } ``` <br/> On peut également imbriquer les conditions avec la syntaxe suivante : ```java= if (condition1) { instruction 1 ... instruction n } else if (condition 2) { instruction 1 ... instruction n } else { instruction 1 ... instruction n } ``` > Dans ce cas, on teste la première condition. > > Si elle est vraie, alors le bloc de code correspondant est exécuté, sinon on teste la suivante et ainsi de suite. > > Si aucune condition n’est vérifiée, le bloc de code spécifié après le else est exécuté. > > L’instruction **else** n’est pas obligatoire dans cette structure. Dans ce cas, il se peut qu’aucune instruction ne soit exécutée si aucune des conditions n’est vraie. <br/><br/> > #### **Structure ternaire** > [color=#018786] Cette structure particulière correspond à une instruction **if...else**. Elle n’est utilisable que pour l’affectation d’une valeur. C’est une structure concise remplaçant avantageusement l’instruction **if...else**. ```java= String message = condition ? "si true" : "si false"; ``` > Le **?** permet de déclencher l’évaluation de la condition. > > Si la condition vaut **true** alors c’est la valeur fournie après le **?** qui est retournée, sinon c’est la valeur fournie après les **:** qui est retournée. > > L’instruction équivalente à un **if...else** est la suivante : ```java= if(condition) message="si true"; else message="si false"; ``` <br/><br/> > #### **Structure switch historique** > [color=#018786] La structure **switch** permet un fonctionnement équivalent, mais offre une meilleure lisibilité du code. La syntaxe historique est la suivante : ```java= switch (expression) { case valeur1: instruction 1 ... instruction n break; case valeur2: instruction 1 ... instruction n break; default: instruction 1 ... instruction n } ``` > La valeur de l’expression est évaluée au début de la structure (par le switch) puis la valeur obtenue est comparée avec la valeur spécifiée dans le premier **case**. > > - Si les deux valeurs sont égales, alors le bloc de code sous-jacent est exécuté. > - Sinon, la valeur obtenue est comparée avec la valeur du **case** suivant. > > - S’il y a correspondance, le bloc de code est exécuté et ainsi de suite jusqu’au dernier case. > > - Si aucune valeur concordante n’est trouvée dans les différents case, alors le bloc de code spécifié par le mot-clé **default** est exécuté. > > Chacun des blocs de code doit se terminer par l’instruction **break**. > - Si ce n’est pas le cas, l’exécution se poursuivra par le bloc de code suivant jusqu’à ce qu’une instruction **break** soit rencontrée ou jusqu’à la fin de la structure **switch**. > Cette solution peut être utilisée pour pouvoir exécuter un même bloc de code pour différentes valeurs testées. > > La valeur à tester peut être contenue dans une variable, mais peut également être le résultat d’un calcul. > - Dans ce cas, le calcul n’est effectué qu’une seule fois au début du **switch**. > > Le type de la valeur testée peut être numérique entière, caractère, chaîne de caractères ou énumération. > > Il faut bien sûr que le type de la variable testée corresponde au type des valeurs dans les différents case. > > Si l’expression est de type **chaîne de caractères**, la méthode **equals** est utilisée pour vérifier l’égalité avec les valeurs des différents **case**. La comparaison fait donc une distinction entre minuscules et majuscules. ```java= System.out.println("Répondez oui, non ou n'importe quoi:"); BufferedReader br; br=new BufferedReader(new InputStreamReader(System.in)); String reponse=""; reponse=br.readLine(); switch (reponse) { case "oui": case "OUI": System.out.println("réponse positive"); break; case "non": case "NON": System.out.println("réponse négative"); break; default: System.out.println("mauvaise réponse"); } ``` <br/><br/> > #### **Structure switch nouvelle génération** > [color=#018786] La structure **switch** historique souffrait de quelques limitations : - Obligation de répéter le mot-clé case pour chaque valeur à évaluer. - Obligation d’utiliser le mot clé break pour sortir du switch. - Impossibilité d’initialiser simplement une variable à partir de cette structure. La nouvelle version du **switch** répond à ces problématiques. Voici la nouvelle syntaxe de la structure (l’ancienne syntaxe est bien sûr toujours compatible) : ```java= [TypeRetour variableRetour=] switch (expression) { case valeur1, valeur2 -> instruction case valeur3 -> { TypeRetour valeurRetour; instruction 1 ... instruction n [yield valeurRetour] } default -> instruction } ``` > Voici les nouveautés : > > - Les valeurs pour lesquelles le traitement est identique peuvent se suivre séparées d’une virgule. > > - L’opérateur séparant les valeurs des instructions à exécuter n’est plus les deux points (**:**) mais l’opérateur arrow (**flèche ->**). Il s’agit d’une ==expression lambda==. > > - S’il y a plusieurs instructions à effectuer, elles doivent être placées dans un bloc de code délimité par des accolades. > > - Le **switch** peut éventuellement retourner une valeur. Cette valeur correspond au résultat des instructions. Si une seule instruction est exécutée, la valeur retournée est implicitement le résultat de l’instruction. Le mot-clé **return** n’est pas utile. S’il y a plusieurs instructions et que le compilateur ne détecte pas le retour implicite, il faut utiliser le mot-clé **yield** pour indiquer la valeur à retourner. Exemple : Enumération JOUR : ```java= public enum Jour { DIMANCHE, LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI } ``` > Le code suivant permet d’avoir une syntaxe concise pour afficher une information en fonction du jour de la semaine : ```java= Jour unJour = Jour.DIMANCHE; switch (unJour) { case LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI -> System.out.println("On est en semaine"); case SAMEDI, DIMANCHE -> System.out.println("On est en week-end"); } ``` <br/> Le code suivant permet d’initialiser simplement une variable en fonction de la valeur passée au **switch**. Dans l’exemple suivant, les instructions retournent implicitement un entier (sans utilisation du mot-clé return) : ```java= Jour unJour = Jour.DIMANCHE; int typeJour = switch(unJour) { case LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI -> 0; case SAMEDI, DIMANCHE -> 1; }; System.out.println(typeJour); ``` <br/> Pour terminer, les instructions peuvent être plus complexes pour retourner la bonne valeur, et dans cette situation, le compilateur peut ne pas savoir quelle est la valeur à retourner. Il est possible de l’aider en utilisant le mot-clé **yield** (dont le rôle est finalement équivalent au mot-clé **return** classique). ```java= Jour unJour = Jour.DIMANCHE; int typeJourSiContientUnM = switch(unJour) { case MARDI, MERCREDI, SAMEDI, DIMANCHE -> { if(unJour==Jour.MARDI || unJour==Jour.MERCREDI) { yield 0; } else { yield 1; } } default -> -1; }; System.out.println(typeJourSiContientUnM); ``` > Dans le cadre du **case** de la structure ci-dessus, une conditionnelle a été ajoutée pour mettre en évidence l’utilisation du mot-clé **yield**. > > *Bien sûr, le code pouvait être écrit différemment pour ne pas se retrouver dans cette situation.* <br/><br/> #### **Les structures de boucle** Trois structures sont à notre disposition : - ==while (condition)== - ==do ... while (condition)== - ==for== > Elles ont toutes pour but de faire exécuter un bloc de code un certain nombre de fois en fonction d’une condition. <br/> > #### **Structure while** > [color=#018786] Cette structure exécute un bloc de façon répétitive tant que la condition est vraie. ```java= while (condition) { instruction 1 ... instruction n } ``` > La condition est évaluée avant le premier passage dans la boucle. > > - Si le résultat est **faux** à cet instant, alors le bloc de code n’est pas exécuté. > - Après chaque exécution du bloc de code, la condition est à nouveau évaluée pour vérifier si une nouvelle exécution du bloc de code est nécessaire. > > Il est recommandé que l’exécution du bloc de code contienne une ou plusieurs instructions susceptibles de faire évoluer la condition. > - Si ce n’est pas le cas, la boucle s’exécutera sans fin. > > Il ne faut surtout pas placer le caractère "**;**" à la fin de la ligne du **while** car dans ce cas, le bloc de code n’est plus associé à la boucle. ```java= int i=0; while (i<=10) //Pas de ; ici. Sinon le bloc sous-jacent n'est pas lié à la boucle { System.out.println(i); i++; } ``` <br/><br/> > #### **Structure do ... while** > [color=#018786] <br/> ```java= do { instruction 1 ... instruction n } while (condition); ``` > Cette structure a un fonctionnement identique à la précédente. > > La différence réside dans le fait que la condition est examinée après l’exécution du bloc de code. > > Elle permet de garantir que le bloc de code est exécuté au moins une fois puisque la condition est évaluée pour la première fois après la première exécution du bloc de code. > > Si la condition est **vraie**, alors le bloc est exécuté une nouvelle fois jusqu’à ce que la condition soit **fausse**. > > *Faites attention à ne pas oublier le point-virgule après le while sinon le compilateur détecte une erreur de syntaxe.* <br/> ```java= do { System.out.println(i); i++; } while(i<=10); ``` <br/><br/> > #### **Structure for** > [color=#018786] Lorsque le nombre d’itérations à réaliser est connue dans une boucle, il est préférable d’utiliser la structure for. Pour pouvoir utiliser cette instruction, une variable de compteur doit être déclarée. - Cette variable peut être déclarée dans la structure **for** ou à l’extérieur, elle doit dans ce cas être déclarée avant la structure **for**. La syntaxe générale est la suivante : ```java= for(initialisation; condition; instruction d'itération) { instruction 1 ... instruction n } ``` > La partie **initialisation** est exécutée une seule fois lors de l’entrée dans la boucle. > > La partie **condition** est évaluée lors de l’entrée dans la boucle puis à chaque itération. > - Le résultat de l’évaluation de la condition détermine si le bloc de code est exécuté. > - Si le résultat est **vrai**, une nouvelle itération de la boucle est effectuée. > > Après l’exécution du bloc de code, l’**instruction d’itération** est à son tour exécutée. > > Puis la condition est à nouveau testée et ainsi de suite tant que la condition est **vraie**. Exemple : Deux boucles **for** en action pour afficher une table de multiplication. ```java= int multiplicateur; for(multiplicateur=1;multiplicateur<=10;multiplicateur++) { for (int table = 1; table <= 10; table++) { System.out.print(table*multiplicateur + "\t"); } System.out.println(); } ``` > Nous obtenons le résultat suivant : ``` 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100 ``` <br/> Une autre syntaxe de la boucle for permet d’exécuter un bloc de code pour chaque élément contenu dans un tableau ou dans une instance de classe implémentant l’interface **Iterable**. C’est la **boucle foreach**. La syntaxe générale de cette instruction est la suivante : ```java= for (type elementDuTableau : tableau){ instruction 1 ... instruction n } ``` > Il n’y a pas de notion de compteur dans cette structure puisqu’elle effectue elle-même les itérations sur tous les éléments présents dans le tableau ou la collection. > > La variable déclarée dans la structure (nommée **elementDuTableau** dans l’exemple) sert à extraire un à un les éléments du tableau ou de la collection pour que le bloc de code puisse les manipuler. > > - Il faut bien sûr que le type de la variable soit compatible avec le type des éléments stockés dans le tableau ou la collection. > > - La variable doit obligatoirement être déclarée dans la structure **for** et non à l’extérieur. > > - Elle ne sera utilisable qu’à l’intérieur de la structure. > > En revanche, vous n’avez pas à vous soucier du nombre d’éléments, car la structure est capable de gérer elle-même le déplacement dans le tableau ou la collection. Exemple : *Avec une boucle classique :* ```java= String[] tablo={"rouge","vert","bleu","blanc"}; for (int cpt = 0; cpt < tablo.length; cpt++){ System.out.println(tablo[cpt]); } ``` *Avec la boucle for d’itération :* ```java= String[] tablo={"rouge","vert","bleu","blanc"}; for (String s : tablo) { System.out.println(s); } ``` > Le code placé à l’intérieur de cette structure **for** ne doit pas modifier le contenu de la collection, car l’élément parcouru est placé en lecture seule. > > Dans le cas d’un tableau ou d’une collection d’objets, il est possible de changer les valeurs des propriétés de ces objets (la variable référençant l’objet n’étant pas modifiée). > > Il est donc interdit d’ajouter ou de supprimer des éléments pendant le parcours de la collection. Le problème ne se pose pas avec un tableau. La taille d’un tableau étant fixe, il est bien impossible d’y ajouter ou d’y supprimer un élément. Le code suivant met en évidence cette limitation lors du parcours d’une **ArrayList**. *L’ajout d’un élément à l’Arraylist en cours d’itération déclenche une exception de type* ***ConcurrentModificationException***. ```java= ArrayList<String> lst; lst=new ArrayList<String>(); lst.add("client 1"); lst.add("client 2"); lst.add("client 3"); lst.add("client 5"); for(String st:lst){ System.out.println(st); if(st.endsWith("3")){ lst.add("client 4"); } } ``` <br/> #### **Interruption d’une structure de boucle** Trois instructions peuvent modifier le fonctionnement normal des structures de boucle. <br/> > #### **break** > [color=#018786] Si cette instruction est placée à l’intérieur du bloc de code d’une structure de boucle, elle provoque la sortie immédiate de ce bloc de code. L’exécution se poursuit par l’instruction placée après le bloc de code. L’instruction **break** doit en général être exécutée de manière conditionnelle, sinon les instructions situées après à l’intérieur de la boucle ne seront jamais exécutées. Dans le cas de boucles imbriquées, il est possible d’utiliser l’instruction **break** associée avec une étiquette. > L’exemple de code suivant effectue le parcours d’un tableau à deux dimensions et s’arrête dès qu’une case contenant la valeur 0 est rencontrée. ```java= int[][] points = { { 10,10}, { 0,10 }, { 45,24 } }; int x=0, y=0; boolean trouve=false; // recherche: for (x = 0; x <points.length; x++){ for (y = 0; y < points[x].length;y++) { if (points[x][y] == 0){ trouve = true; break recherche; } } } if (trouve){ System.out.println("resultat trouvé dans la case " + x + "-" + y); } else { System.out.println("recherche infructueuse"); } ``` <br/><br/> > #### **continue** > [color=#018786] Cette instruction permet d’interrompre l’exécution de l’itération courante d’une boucle et de continuer l’exécution à l’itération suivante après vérification de la condition de sortie de boucle. Comme pour l’instruction **break**, elle doit être exécutée de manière conditionnelle et accepte également l’utilisation d’une étiquette. > Voici un exemple de code permettant de calculer le nombre de nombres pairs consécutifs à partir de la première case dans un tableau trié. ```java= int[] nombres = new int[20]; Random rd = new Random(); for (int i = 0; i < nombres.length; i++) { nombres[i]=rd.nextInt(20); } Arrays.sort(nombres); System.out.println(Arrays.toString(nombres)); int nbPaire; for(nbPaire=0;nbPaire<nombres.length;nbPaire++) { if(nombres[nbPaire]%2==0) { continue; } break; } System.out.println("nombre de paires : " + nbPaire); ``` <br/><br/> > #### **return** > [color=#018786] L’instruction **return** est utilisée pour sortir immédiatement de la méthode en cours d’exécution et poursuivre l’exécution par l’instruction suivant celle qui a appelé cette méthode. Si elle est placée dans une structure de boucle, elle provoque bien sûr la sortie immédiate de la boucle puis de la méthode dans laquelle se trouve la boucle. L’utilisation de cette instruction dans une fonction dont le type de retour est autre que **void** oblige à fournir à l’instruction return une valeur compatible avec le type de retour de la fonction.