Version en ligne

Tutoriel : Apprenez à programmer en Java

Table des matières

Apprenez à programmer en Java
Installer les outils de développement
Installer les outils nécessaires
Votre premier programme
Les variables et les opérateurs
Petit rappel
Les différents types de variables
Les opérateurs arithmétiques
Les conversions, ou « cast »
Depuis Java 7 : le formatage des nombres
Lire les entrées clavier
La classe Scanner
Récupérer ce que vous tapez
Les conditions
La structure if… else
La structure switch
La condition ternaire
Les boucles
La boucle while
La boucle do… while
La boucle for
TP : conversion Celsius - Fahrenheit
Élaboration
Correction
Les tableaux
Tableau à une dimension
Les tableaux multidimensionnels
Utiliser et rechercher dans un tableau
Les méthodes de classe
Quelques méthodes utiles
Créer sa propre méthode
La surcharge de méthode
Votre première classe
Structure de base
Les constructeurs
Accesseurs et mutateurs
Les variables de classes
Le principe d'encapsulation
L'héritage
Le principe de l'héritage
Le polymorphisme
Depuis Java 7 : la classe Objects
Modéliser ses objets grâce à UML
Présentation d'UML
Modéliser ses objets
Modéliser les liens entre les objets
Les packages
Création d'un package
Droits d'accès entre les packages
Les classes abstraites et les interfaces
Les classes abstraites
Les interfaces
Le pattern strategy
Les exceptions
Le bloc try{...} catch{...}
Les exceptions personnalisées
La gestion de plusieurs exceptions
Depuis Java 7 : le multi-catch
Les énumérations
Avant les énumérations
Une solution : les enum
Les collections d'objets
Les différents types de collections
Les objets List
Les objets Map
Les objets Set
La généricité en Java
Principe de base
Généricité et collections
Les flux d'entrée/sortie
Utilisation de java.io
Utilisation de java.nio
Depuis Java 7 : nio II
Le pattern decorator
Java et la réflexivité
L'objet Class
Instanciation dynamique
Notre première fenêtre
L'objet JFrame
L'objet JPanel
Les objets Graphics et Graphics2D
Le fil rouge : une animation
Création de l'animation
Améliorations
Positionner des boutons
Utiliser la classe JButton
Positionner son composant : les layout managers
Interagir avec des boutons
Une classe Bouton personnalisée
Interagir avec son bouton
Être à l'écoute de ses objets : le design pattern Observer
Cadeau : un bouton personnalisé optimisé
TP : une calculatrice
Élaboration
Conception
Correction
Générer un .jar exécutable
Exécuter des tâches simultanément
Une classe héritée de Thread
Utiliser l'interface Runnable
Synchroniser ses threads
Contrôler son animation
Depuis Java 7 : le pattern Fork/Join
Les champs de formulaire
Les listes : l'objet JComboBox
Les cases à cocher : l'objet JCheckBox
Les champs de texte : l'objet JTextField
Contrôle du clavier : l'interface KeyListener
Les menus et boîtes de dialogue
Les boîtes de dialogue
Les menus
TP : l'ardoise magique
Cahier des charges
Prérequis
Correction
Améliorations possibles
Conteneurs, sliders et barres de progression
Autres conteneurs
Enjoliver vos IHM
Les arbres et leur structure
La composition des arbres
Des arbres qui vous parlent
Décorez vos arbres
Modifier le contenu de nos arbres
Les interfaces de tableaux
Premiers pas
Gestion de l'affichage
Interaction avec l'objet JTable
Ajouter des lignes et des colonnes
TP : le pendu
Cahier des charges
Prérequis
Correction
Mieux structurer son code : le pattern MVC
Premiers pas
Le modèle
Le contrôleur
La vue
Le Drag'n Drop
Présentation
Fonctionnement
Créer son propre TransferHandler
Activer le drop sur un JTree
Effet de déplacement
Mieux gérer les interactions avec les composants
Présentation des protagonistes
Utiliser l'EDT
La classe SwingWorker<T, V>
JDBC : la porte d'accès aux bases de données
Rappels sur les bases de données
Préparer la base de données
Se connecter à la base de données
Fouiller dans sa base de données
Le couple Statement - ResultSet
Les requêtes préparées
Modifier des données
Statement, toujours plus fort
Gérer les transactions manuellement
Limiter le nombre de connexions
Pourquoi ne se connecter qu'une seule fois ?
Le pattern singleton
Le singleton dans tous ses états
TP : un testeur de requêtes
Cahier des charges
Quelques captures d'écran
Correction
Lier ses tables avec des objets Java : le pattern DAO
Avant toute chose
Le pattern DAO
Le pattern factory

Apprenez à programmer en Java

Bonjour à tous, amis Zéros ! :D

Bienvenue dans mon cours de programmation en Java. C'est un langage très utilisé, notamment par un grand nombre de programmeurs professionnels, ce qui en fait un langage incontournable actuellement.

Voici les caractéristiques de Java en quelques mots :

Comme vous le voyez, Java permet de réaliser une très grande quantité d'applications différentes ! Mais... comment apprendre un langage si vaste qui offre autant de possibilités ? o_O

Heureusement, ce cours est là pour tout vous apprendre de Java à partir de zéro :) .

Un programme en JavaUn jeu de pendu en JavaUne calculatrice en JavaExemples de programmes développés en Java

Image utilisateurCe cours vous plaît ?
Si vous avez aimé ce cours, vous pouvez retrouver le livre "Apprenez à programmer en Java" du même auteur, en vente sur le Site du Zéro, en librairie et dans les boutiques en ligne. Vous y trouverez ce cours adapté au format papier.

Plus d'informations

N.B. : je tiens à faire une dédicace spéciale à ptipilou, zCorrecteur émérite, sans qui ce tuto n'aurait pas vu le jour !
Un grand merci pour ton travail et ton soutien ! :)

Installer les outils de développement

Installer les outils nécessaires

L'un des principes phares de Java réside dans sa machine virtuelle : celle-ci assure à tous les développeurs Java qu'un programme sera utilisable avec tous les systèmes d'exploitation sur lesquels est installée une machine virtuelle Java.
Lors de la phase de compilation de notre code source, celui-ci prend une forme intermédiaire appelée byte code : c'est le fameux code inintelligible pour votre machine, mais interprétable par la machine virtuelle Java. Cette dernière porte un nom : on parle plus communément de JRE (Java Runtime Environment). Plus besoin de se soucier des spécificités liées à tel ou tel OS (Operating System, soit système d'exploitation). Nous pourrons donc nous consacrer entièrement à notre programme.

Afin de nous simplifier la vie, nous allons utiliser un outil de développement, ou IDE (Integrated Development Environment), pour nous aider à écrire nos futurs codes source… Nous allons donc avoir besoin de différentes choses afin de pouvoir créer des programmes Java : la première est ce fameux JRE !

Installer les outils nécessaires

Installer les outils de développement Votre premier programme

Installer les outils nécessaires

JRE ou JDK

Commencez par télécharger l'environnement Java sur le site d'Oracle, comme le montre la figure suivante. Choisissez la dernière version stable.

Encart de téléchargement
Encart de téléchargement

Vous avez sans doute remarqué qu'on vous propose de télécharger soit le JRE, soit le JDK (Java Development Kit). La différence entre ces deux environnements est écrite, mais pour les personnes fâchées avec l'anglais, sachez que le JRE contient tout le nécessaire pour que vos programmes Java puissent être exécutés sur votre ordinateur ; le JDK, en plus de contenir le JRE, contient tout le nécessaire pour développer, compiler…

L'IDE contenant déjà tout le nécessaire pour le développement et la compilation, nous n'avons besoin que du JRE. Une fois que vous avez cliqué sur Download JRE, vous arrivez sur la page représentée à la figure suivante.

Choix du système d'exploitation
Choix du système d'exploitation

Cochez la case : Accept License Agreement puis cliquez sur le lien correspondant à votre système d'exploitation (x86 pour un système 32 bits et x64 pour un système 64 bits). Une popup de téléchargement doit alors apparaître.

Je vous ai dit que Java permet de développer différents types d'applications ; il y a donc des environnements permettant de créer des programmes pour différentes plates-formes :

Eclipse IDE

Avant toute chose, quelques mots sur le projet Eclipse. « Eclipse IDE » est un environnement de développement libre permettant de créer des programmes dans de nombreux langages de programmation (Java, C++, PHP…). C'est l'outil que nous allons utiliser pour programmer.

Je vous invite donc à télécharger Eclipse IDE. Une fois la page de téléchargement choisissez Eclipse IDE for Java Developers, en choisissant la version d'Eclipse correspondant à votre OS (Operating System = système d'exploitation), comme indiqué à la figure suivante.

Version Windows d'Eclipse IDE
Version Windows d'Eclipse IDE

Sélectionnez maintenant le miroir que vous souhaitez utiliser pour obtenir Eclipse. Voilà, vous n'avez plus qu'à attendre la fin du téléchargement.

Pour ceux qui l'avaient deviné, Eclipse est le petit logiciel qui va nous permettre de développer nos applications ou nos applets, et aussi celui qui va compiler tout ça. Notre logiciel va donc permettre de traduire nos futurs programmes Java en langage byte code, compréhensible uniquement par votre JRE, fraîchement installé.

La spécificité d'Eclipse IDE vient du fait que son architecture est totalement développée autour de la notion de plugin. Cela signifie que toutes ses fonctionnalités sont développées en tant que plugins. Pour faire court, si vous voulez ajouter des fonctionnalités à Eclipse, vous devez :

Vous devez maintenant avoir une archive contenant Eclipse. Décompressez-la où vous voulez, entrez dans ce dossier et lancez Eclipse. Au démarrage, comme le montre la figure suivante, Eclipse vous demande dans quel dossier vous souhaitez enregistrer vos projets ; sachez que rien ne vous empêche de spécifier un autre dossier que celui proposé par défaut. Une fois cette étape effectuée, vous arrivez sur la page d'accueil d'Eclipse. Si vous avez envie d'y jeter un oeil, allez-y !

Vous devez indiquer où enregistrer vos projets
Vous devez indiquer où enregistrer vos projets
Présentation rapide de l'interface

Je vais maintenant vous faire faire un tour rapide de l'interface d'Eclipse. Voici les principaux menus :

La barre d'outils

La barre d'outils ressemble à la figure suivante.

La barre d'outils d'Eclipse
La barre d'outils d'Eclipse

Nous avons dans l'ordre :

  1. nouveau général : cliquer sur ce bouton revient à faire Fichier > Nouveau ;

  2. enregistrer : revient à faire CTRL + S ;

  3. imprimer : ai-je besoin de préciser ?

  4. exécuter la classe ou le projet spécifié : nous verrons ceci plus en détail ;

  5. créer un nouveau projet : revient à faire Fichier > Nouveau > Java Project ;

  6. créer une nouvelle classe : créer un nouveau fichier. Cela revient à faire Fichier > Nouveau > Classe.

Maintenant, je vais vous demander de créer un nouveau projet Java, comme indiqué aux figures suivantes.

Création de projet Java - étape 1
Création de projet Java - étape 1
Création de projet Java - étape 2
Création de projet Java - étape 2

Renseignez le nom de votre projet comme je l'ai fait dans le premier encadré de la deuxième figure. Vous pouvez aussi voir où sera enregistré ce projet. Un peu plus compliqué, maintenant : vous avez un environnement Java sur votre machine, mais dans le cas où vous en auriez plusieurs, vous pouvez aussi spécifier à Eclipse quel JRE utiliser pour ce projet, comme sur le deuxième encadré de la deuxième figure. Vous pourrez changer ceci à tout moment dans Eclipse en allant dans Window > Preferences, en dépliant l'arbre Java dans la fenêtre et en choisissant Installed JRE.

Vous devriez avoir un nouveau projet dans la fenêtre de gauche, comme à la figure suivante.

Explorateur de projet
Explorateur de projet

Pour boucler la boucle, ajoutons dès maintenant une nouvelle classe dans ce projet comme nous avons appris à le faire plus tôt via la barre d'outils. La figure suivante représente la fenêtre sur laquelle vous devriez tomber.

Création d'une classe
Création d'une classe

Dans l'encadré 1, nous pouvons voir où seront enregistrés nos fichiers Java. Dans l'encadré 2, nommez votre classe Java ; moi, j'ai choisi « sdz1 ». Dans l'encadré 3, Eclipse vous demande si cette classe a quelque chose de particulier. Eh bien oui ! Cochez
public static void main(String[] args) (nous reviendrons plus tard sur ce point), puis cliquez sur Finish. La fenêtre principale d'Eclipse se lance, comme à la figure suivante.

Fenêtre principale d'Eclipse
Fenêtre principale d'Eclipse

Avant de commencer à coder, nous allons explorer l'espace de travail. Dans l'encadré de gauche (le vert), vous trouverez le dossier de votre projet ainsi que son contenu. Ici, vous pourrez gérer votre projet comme bon vous semble (ajout, suppression…). Dans l'encadré positionné au centre (le bleu), je pense que vous avez deviné : c'est ici que nous allons écrire nos codes source. Dans l'encadré du bas (le rouge), c'est là que vous verrez apparaître le contenu de vos programmes… ainsi que les erreurs éventuelles ! Et pour finir, c'est dans l'encadré de droite (le violet), dès que nous aurons appris à coder nos propres fonctions et nos objets, que la liste des méthodes et des variables sera affichée.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Installer les outils de développement Votre premier programme

Votre premier programme

Installer les outils nécessaires Les variables et les opérateurs

Votre premier programme

Comme je vous l'ai maintes fois répété, les programmes Java sont, avant d'être utilisés par la machine virtuelle, précompilés en byte code (par votre IDE ou à la main). Ce byte code n'est compréhensible que par une JVM, et c'est celle-ci qui va faire le lien entre ce code et votre machine.

Vous aviez sûrement remarqué que sur la page de téléchargement du JRE, plusieurs liens étaient disponibles :

Ceci, car la machine virtuelle Java se présente différemment selon qu'on se trouve sous Mac, sous Linux ou encore sous Windows. Par contre, le byte code, lui, reste le même quel que soit l'environnement avec lequel a été développé et précompilé votre programme Java. Conséquence directe : quel que soit l'OS sous lequel a été codé un programme Java, n'importe quelle machine pourra l'exécuter si elle dispose d'une JVM !

Tu n'arrêtes pas de nous rabâcher byte code par-ci, byte code par-là… Mais c'est quoi, au juste ?

Eh bien, un byte code (il existe plusieurs types de byte code, mais nous parlons ici de celui créé par Java) n'est rien d'autre qu'un code intermédiaire entre votre code Java et le code machine. Ce code particulier se trouve dans les fichiers précompilés de vos programmes ; en Java, un fichier source a pour extension .java et un fichier précompilé a l'extension .class : c'est dans ce dernier que vous trouverez du byte code. Je vous invite à examiner un fichier .class à la fin de cette partie (vous en aurez au moins un), mais je vous préviens, c'est illisible !

Par contre, vos fichiers .java sont de simples fichiers texte dont l'extension a été changée. Vous pouvez donc les ouvrir, les créer ou encore les mettre à jour avec le Bloc-notes de Windows, par exemple. Cela implique que, si vous le souhaitez, vous pouvez écrire des programmes Java avec le Bloc-notes ou encore avec Notepad++.

Reprenons. Vous devez savoir que tous les programmes Java sont composés d'au moins une classe. Elle doit contenir une méthode appelée main : ce sera le point de démarrage de notre programme.

Une méthode est une suite d'instructions à exécuter. C'est un morceau de logique de notre programme. Une méthode contient :

Je vous avais demandé de créer un projet Java ; ouvrez-le ! Vous voyez la fameuse classe dont je vous parlais ? Ici, elle s'appelle « sdz1 », comme à la figure suivante. Vous pouvez voir que le mot class est précédé du mot public, dont nous verrons la signification lorsque nous programmerons des objets.

Méthode principale
Méthode principale

Pour le moment, ce que vous devez retenir, c'est que votre classe est définie par un mot clé (class), qu'elle a un nom (ici, « sdz1 ») et que son contenu est délimité par des accolades ({ }). Nous écrirons nos codes sources entre les accolades de la méthode main. La syntaxe de cette méthode est toujours la même :

public static void main(String[] args){
  //Contenu de votre classe
}

Excuse-nous, mais… pourquoi as-tu écrit « //Contenu de votre classe » et pas « Contenu de votre classe » ?

Bonne question ! Je vous ai dit précédemment que votre programme Java, avant de pouvoir être exécuté, doit être précompilé en byte code. Eh bien, la possibilité de forcer le compilateur à ignorer certaines instructions existe ! C’est ce qu’on appelle des commentaires, et deux syntaxes sont disponibles pour commenter son texte :

  1. Les commentaires unilignes : introduits par les symboles « // », ils mettent tout ce qui les suit en commentaire, du moment que le texte se trouve sur la même ligne ;

  2. Les commentaires multilignes : ils sont introduits par les symboles « /* » et se terminent par les symboles « */ ».

public static void main(String[] args){
  //Un commentaire
  //Un autre
  //Encore un autre

  /*
  Un commentaire
  Un autre
  Encore un autre
  */

Ceci n'est pas un commentaire !
}

D'accord, mais ça sert à quoi ?

C'est simple : au début, vous ne ferez que de très petits programmes. Mais dès que vous aurez pris de la bouteille, leurs tailles et le nombre de classes qui les composeront vont augmenter. Vous serez contents de trouver quelques lignes de commentaires au début de votre classe pour vous dire à quoi elle sert, ou encore des commentaires dans une méthode qui effectue des choses compliquées afin de savoir où vous en êtes dans vos traitements…

Il existe en fait une troisième syntaxe, mais elle a une utilité particulière. Elle permettra de générer une documentation pour votre programme (on l'appelle « Javadoc » pour « Java Documentation »). Je n'en parlerai que très peu, et pas dans ce chapitre. Nous verrons cela lorsque nous programmerons des objets, mais pour les curieux, je vous conseille le très bon cours de dworkin sur ce sujet disponible sur le Site du Zéro.

À partir de maintenant et jusqu'à ce que nous programmions des interfaces graphiques, nous allons faire ce qu'on appelle des programmes procéduraux. Cela signifie que le programme s'exécutera de façon procédurale, c'est-à-dire qui s'effectue de haut en bas, une ligne après l'autre. Bien sûr, il y a des instructions qui permettent de répéter des morceaux de code, mais le programme en lui-même se terminera une fois parvenu à la fin du code. Cela vient en opposition à la programmation événementielle (ou graphique) qui, elle, est basée sur des événements (clic de souris, choix dans un menu…).

Hello World

Maintenant, essayons de taper le code suivant :

public static void main(String[] args){
  System.out.print("Hello World !");
}

Une fois que vous avez saisi cette ligne de code dans votre méthode main, il vous faut lancer le programme. Si vous vous souvenez bien de la présentation faite précédemment, vous devez cliquer sur la flèche blanche dans un rond vert, comme à la figure suivante.

Bouton de lancement du programme
Bouton de lancement du programme

Si vous regardez dans votre console, dans la fenêtre du bas sous Eclipse, vous devriez voir quelque chose ressemblant à la figure suivante.

La console d'Eclipse
La console d'Eclipse

Expliquons un peu cette ligne de code.

Littéralement, elle signifie « la méthode print() va écrire « Hello World ! » en utilisant l'objet out de la classe System » . Avant que vous arrachiez les cheveux, voici quelques précisions :

Prenons le code suivant :

System.out.print("Hello World !");
System.out.print("My name is");
System.out.print("Cysboy");

Lorsque vous l'exécutez, vous devriez voir des chaînes de caractères qui se suivent sans saut de ligne. Autrement dit, ceci s'affichera dans votre console :

Hello World !My name isCysboy

Je me doute que vous souhaiteriez insérer un retour à la ligne pour que votre texte soit plus lisible… Pour cela, vous avez plusieurs solutions :

Donc, si nous reprenons notre code précédent et que nous appliquons cela, voici ce que ça donnerait :

System.out.print("Hello World ! \n");
System.out.println("My name is");
System.out.println("\nCysboy");

Avec pour résultat :

Hello World !
My name is
 
Cysboy

Vous pouvez voir que :

J'en profite au passage pour vous mentionner deux autres caractères d'échappement :

  1. va insérer un retour chariot, parfois utilisé aussi pour les retours à la ligne ;

  2. va faire une tabulation.

Je vous propose maintenant de passer un peu de temps sur la compilation de vos programmes en ligne de commande. Cette partie n'est pas obligatoire, loin de là, mais elle ne peut être qu'enrichissante.

Compilation en ligne de commande (Windows)

Bienvenue donc aux plus curieux ! Avant de vous apprendre à compiler et à exécuter un programme en ligne de commande, il va vous falloir le JDK (Java SE Development Kit). C'est avec celui-ci que nous aurons de quoi compiler nos programmes. Le nécessaire à l'exécution des programmes est dans le JRE… mais il est également inclus dans le JDK. Je vous invite donc à retourner sur le site d'Oracle et à télécharger ce dernier. Une fois cette opération effectuée, il est conseillé de mettre à jour votre variable d'environnement %PATH%.

Euh… quoi ?

Votre « variable d'environnement ». C'est grâce à elle que Windows trouve des exécutables sans qu'il soit nécessaire de lui spécifier le chemin d'accès complet. Vous — enfin, Windows — en a plusieurs, mais nous ne nous intéresserons qu'à une seule. En gros, cette variable contient le chemin d'accès à certains programmes.

Par exemple, si vous spécifiez le chemin d'accès à un programme X dans votre variable d'environnement et que, par un malheureux hasard, vous n'avez plus aucun raccourci vers X, vous l'avez définitivement perdu dans les méandres de votre PC. Eh bien vous pourrez le lancer en faisant Démarrer > Exécuter et en tapant la commande X.exe (en partant du principe que le nom de l'exécutable est « X.exe »</minicode>).

D'accord, mais comment fait-on ? Et pourquoi doit-on faire ça pour le JDK ?

J'y arrive. Une fois votre JDK installé, ouvrez le répertoire bin de celui-ci, ainsi que celui de votre JRE. Nous allons nous attarder sur deux fichiers.

Dans le répertoire bin de votre JRE, vous devez avoir un fichier nommé java.exe, que vous retrouvez aussi dans le répertoire bin de votre JDK. C'est grâce à ce fichier que votre ordinateur peut lancer vos programmes par le biais de la JVM. Le deuxième ne se trouve que dans le répertoire bin de votre JDK, il s'agit de javac.exe (Java compiler). C'est celui-ci qui va précompiler vos programmes Java en byte code.

Alors, pourquoi mettre à jour la variable d'environnement pour le JDK ? Eh bien, compiler et exécuter en ligne de commande revient à utiliser ces deux fichiers en leur précisant où se trouvent les fichiers à traiter. Cela veut dire que si l'on ne met pas à jour la variable d'environnement de Windows, il nous faudrait :

Avec notre variable d'environnement mise à jour, nous n'aurons plus qu'à :

Allez dans le panneau de configuration de votre PC ; de là, cliquez sur l'icône Système ; choisissez l'onglet Avancé et vous devriez voir en bas un bouton nommé Variables d'environnement : cliquez dessus. Une nouvelle fenêtre s'ouvre. Dans la partie inférieure intitulée Variables système, cherchez la variable Path. Une fois sélectionnée, cliquez sur Modifier. Encore une fois, une fenêtre, plus petite celle-ci, s'ouvre devant vous. Elle contient le nom de la variable et sa valeur.

Pour ce faire, allez jusqu'au bout de la valeur de la variable, ajoutez-y un point-virgule s'il n'y en a pas et ajoutez le chemin d'accès au répertoire bin de votre JDK, en terminant celui-ci par un point-virgule ! Chez moi, ça donne ceci : C:\Sun\SDK\jdk\bin.

Auparavant, ma variable d'environnement contenait, avant mon ajout :

%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;

Et maintenant :

%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;C:\Sun\SDK\jdk\bin;

Validez les changements : vous êtes maintenant prêts à compiler en ligne de commande.

Pour bien faire, allez dans le répertoire de votre premier programme et effacez le .class. Ensuite, faites Démarrer > Exécuter (ou encore touche Windows + R et tapez « cmd » .

Par exemple, lorsque j'ouvre la console, je me trouve dans le dossier C: oto iti et mon application se trouve dans le dossier C:\sdz, je fais donc :

cd ..
cd ..
cd sdz

Après de la première instruction, je me retrouve dans le dossier C: oto. Grâce à la deuxième instruction, j'arrive à la racine de mon disque. Via la troisième instruction, je me retrouve dans le dossier C:\sdz. Nous sommes maintenant dans le dossier contenant notre fichier Java ! Cela dit, nous pouvions condenser cela en :

cd ../../sdz

Maintenant, vous pouvez créer votre fichier .class en exécutant la commande suivante :

javac <nomDeFichier.java>

Si, dans votre dossier, vous avez un fichier test.java, compilez-le en faisant :

javac test.java

Et si vous n'avez aucun message d'erreur, vous pouvez vérifier que le fichier test.class est présent en utilisant l'instruction dir qui liste le contenu d'un répertoire. Cette étape franchie, vous pouvez lancer votre programme Java en faisant ce qui suit :

java <nomFichierClassSansExtension>

Ce qui nous donne :

java test

Et normalement, le résultat de votre programme Java s'affiche sous vos yeux ébahis !

Voilà : vous avez compilé et exécuté un programme Java en ligne de commande… Vous avez pu voir qu'il n'y a rien de vraiment compliqué et, qui sait, vous en aurez peut-être besoin un jour.

En résumé
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Installer les outils nécessaires Les variables et les opérateurs

Les variables et les opérateurs

Votre premier programme Petit rappel

Nous commençons maintenant sérieusement la programmation. Dans ce chapitre, nous allons découvrir les variables. On les retrouve dans la quasi-totalité des langages de programmation. Une variable est un élément qui stocke des informations de toute sorte en mémoire : des chiffres, des résultats de calcul, des tableaux, des renseignements fournis par l'utilisateur…

Vous ne pourrez pas programmer sans variables. Il est donc indispensable que je vous les présente !

Petit rappel

Les variables et les opérateurs Les différents types de variables

Petit rappel

Avant de commencer, je vous propose un petit rappel sur le fonctionnement d'un ordinateur et particulièrement sur la façon dont ce dernier interprète notre façon de voir le monde…

Vous n'êtes pas sans savoir que votre ordinateur ne parle qu'une seule langue : le binaire ! Le langage binaire est une simple suite de 0 et de 1. Vous devez vous rendre compte qu'il nous serait très difficile, en tant qu'êtres humains, d'écrire des programmes informatiques pour expliquer à nos ordinateurs ce qu'ils doivent faire, entièrement en binaire… Vous imaginez ! Des millions de 0 et de 1 qui se suivent ! Non, ce n'était pas possible ! De ce fait, des langages de programmation ont été créés afin que nous ayons à disposition des instructions claires pour créer nos programmes. Ces programmes sont ensuite compilés pour que nos instructions humainement compréhensibles soient, après coup, compréhensible par votre machine.

Le langage binaire est donc une suite de 0 et de 1 qu'on appelle bit. Si vous êtes habitués à la manipulation de fichiers (audio, vidéos, etc.) vous devez savoir qu'il existe plusieurs catégories de poids de programme (Ko, Mo, Go, etc.). Tous ces poids correspondent au système métrique informatique. Le tableau suivant présente les poids les plus fréquemment rencontrés :

Raccourcis

Traduction

Correspondance

b

Bit

C'est la plus petite valeur informatique : soit 0 soit 1

o

Octet

regroupement de 8 bits, par exemple : 01011101

Ko

Kilo Octet

regroupement de 1024 octets

Mo

Mega Octet

regroupement de 1024 ko

Go

Giga Octet

regroupement de 1024 Mo

To

Tera Octet

regroupement de 1024 Go

Pourquoi 1024 ? Pourquoi pas 1000 ?

Si vous vous posez cette question c'est parce que vous ne savez pas encore compter comme un ordinateur et que vous êtes trop habitués à utiliser un système en base 10. Je sais, c'est un peu confus… Pour comprendre pourquoi ce découpage est fait de la sorte, vous devez comprendre que votre façon de compter n'est pas identique à celle de votre machine. En effet, vous avez l'habitude de compter ainsi :

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ... 254, 255, 256 ... 12345678, 12345679, 12345680

Cette façon de compter repose sur une base 10, car elle se décompose en utilisant des puissances de 10. Ainsi, le nombre 1024 peut se décomposer de cette façon : 1*1000 + 0*100 + 2*10 + 4*1

Pour bien comprendre ce qui suit, vous devez aussi savoir que tout nombre élevé à la puissance 0 vaut 1, donc 10^0 = 1. Partant de ce postulat, nous pouvons donc réécrire la décomposition du nombre 1024 ainsi : 1*10^3 + 0*10^2 + 2*10^1 + 4*10^0. Nous multiplions donc la base utilisée, ordonnée par puissance, par un nombre compris entre 0 et cette base moins 1 (de 0 à 9).

Sauf que votre machine parle en binaire, elle compte donc en base 2. Cela revient donc à appliquer la décomposition précédente en remplaçant les 10 par des 2. Par contre, vous n'aurez que deux multiplicateurs possibles : 0 ou 1 (et oui, vous êtes en base 2). De ce fait, en base 2, nous pourrions avoir ce genre de chose : 1*2^3 + 1*2^2 + 0*2^1 + 1*2^0, qui peut se traduire de la sorte : 1*8 + 1*4 + 0*2 + 1*1 donc 8 + 4 + 0 + 1 soit 13.

Donc, 1101 en base 2 s'écrit 13 en base 10. Et donc pourquoi des paquets de 1024 comme délimiteur de poids ? Car ce nombre correspond à une puissance de 2 : 1024 = 2^1^0.

Dans le monde de l'informatique, il existe une autre façon de compter très répandue : l'hexadécimal. Dans ce cas, nous comptons en base 16 :

1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1A, 1B, 1C.... 5E, 5F, 60, ... A53A, A53B, A53C...

C'est tordu ! À quoi ça peut bien servir ?

Le côté pratique de cette notation c'est qu'elle se base sur une subdivision d'un octet. Pour représenter un nombre de 0 à 15 (donc les seize premiers nombres), 4 bits sont nécessaires : 0*2^3 + 0*2^2 + 0*2^1 + 0*2^0 = 0 et 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 = 15. En fait, vous savez maintenant qu'un octet est un regroupement de 8 bits. Utiliser l'hexadécimal permet de simplifier la notation binaire car, si vous regroupez votre octet de bits en deux paquets de 4 bits, vous pouvez représenter chaque paquet avec un caractère hexadécimal. Voici un exemple :

10110100 -> 1011 0100
1011 (en base 2) = 11 (base 10) = B (en base 16)
0100 (en base 2) = 4 (base 10) = 4 (en base 16)
Donc 10110100 -> 1011 0100 -> B4

La figure suivante représente un nombre binaire plus conséquent :

Un nombre binaire conséquent
Un nombre binaire conséquent
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les variables et les opérateurs Les différents types de variables

Les différents types de variables

Petit rappel Les opérateurs arithmétiques

Les différents types de variables

Nous allons commencer par découvrir comment créer des variables dans la mémoire. Pour cela, il faut les déclarer. Une déclaration de variable se fait comme ceci :

<Type de la variable> <Nom de la variable> ;

Cette opération se termine toujours par un point-virgule « ; » (comme toutes les instructions de ce langage). Ensuite, on l'initialise en entrant une valeur.

En Java, nous avons deux types de variables :

  1. des variables de type simple ou « primitif » ;

  2. des variables de type complexe ou des « objets ».

Ce qu'on appelle des types simples ou types primitifs, en Java, ce sont tout bonnement des nombres entiers, des nombres réels, des booléens ou encore des caractères, et vous allez voir qu'il y a plusieurs façons de déclarer certains de ces types.

Les variables de type numérique

Le type byte (1 octet) peut contenir les entiers entre -128 et +127.

byte temperature;
temperature = 64;

Le type short (2 octets) contient les entiers compris entre -32768 et +32767.

short vitesseMax;
vitesseMax = 32000;

Le type int (4 octets) va de -2*109 à 2*109 (2 et 9 zéros derrière… ce qui fait déjà un joli nombre).

int temperatureSoleil;
temperatureSoleil = 15600000; //La température est exprimée en kelvins

Le type long (8 octets) peut aller de -9*10^18 à 9*10^18 (encore plus gros…).

long anneeLumiere;
anneeLumiere = 9460700000000000L;

Le type float (4 octets) est utilisé pour les nombres avec une virgule flottante.

float pi;
pi = 3.141592653f;

Ou encore :

float nombre;
nombre = 2.0f;

Le type double (8 octets) est identique à float, si ce n'est qu'il contient plus de chiffres derrière la virgule et qu'il n'a pas de suffixe.

double division;
division = 0.333333333333333333333333333333333333333333334d;
Des variables stockant un caractère

Le type char contient un caractère stocké entre apostrophes (« ' ' »), comme ceci :

char caractere;
caractere = 'A';
Des variables de type booléen

Le type boolean, lui, ne peut contenir que deux valeurs  : true (vrai) ou false (faux), sans guillemets (ces valeurs sont natives dans le langage, il les comprend directement et sait les interpréter).

boolean question;
question = true;
Et aussi le type String

Le type String permet de gérer les chaînes de caractères, c'est-à-dire le stockage de texte.

Il s'agit d'une variable d'un type plus complexe que l'on appelle objet. Vous verrez que celle-ci s'utilise un peu différemment des variables précédentes :

//Première méthode de déclaration
String phrase;
phrase = "Titi et Grosminet";

//Deuxième méthode de déclaration
String str = new String();
str = "Une autre chaîne de caractères";

//Troisième méthode de déclaration
String string = "Une autre chaîne";

//Quatrième méthode de déclaration
String chaine = new String("Et une de plus !");

Cela a été mentionné plus haut : String n'est pas un type de variable, mais un objet. Notre variable est un objet, on parle aussi d'une instance : ici, une instance de la classe String. Nous y reviendrons lorsque nous aborderons les objets.

On te croit sur parole, mais pourquoi String commence par une majuscule et pas les autres ?

C'est simple : il s'agit d'une convention de nommage. En fait, c'est une façon d'appeler nos classes, nos variables, etc. Il faut que vous essayiez de la respecter au maximum. Cette convention, la voici :

Je sais que la première classe que je vous ai demandé de créer ne respecte pas cette convention, mais je ne voulais pas vous en parler à ce moment-là… Donc, à présent, je vous demanderai de ne pas oublier ces règles !

Voici quelques exemples de noms de classes et de variables :

public class Toto{}
public class Nombre{}
public class TotoEtTiti{}
String chaine;
String chaineDeCaracteres;
int nombre;
int nombrePlusGrand;

Donc, pour en revenir au pourquoi du comment, je vous ai dit que les variables de type String sont des objets. Les objets sont définis par une ossature (un squelette) qui est en fait une classe. Ici, nous utilisons un objet String défini par une classe qui s'appelle « String » ; c'est pourquoi String a une majuscule et pas int, float, etc., qui eux ne sont pas définis par une classe.

Faites donc bien attention lors de vos déclarations de variables… Une petite astuce quand même (enfin deux, plutôt) : on peut très bien compacter les phases de déclaration et d'initialisation en une seule phase ! Comme ceci :

int entier = 32;
float pi = 3.1416f;
char carac = 'z';
String mot = new String("Coucou");

Et lorsque nous avons plusieurs variables d'un même type, nous pouvons résumer tout ceci à une déclaration :

int nbre1 = 2, nbre2 = 3, nbre3 = 0;

Ici, toutes les variables sont des entiers, et toutes sont initialisées.

Avant de nous lancer dans la programmation, nous allons faire un peu de mathématiques avec nos variables.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Petit rappel Les opérateurs arithmétiques

Les opérateurs arithmétiques

Les différents types de variables Les conversions, ou « cast »

Les opérateurs arithmétiques

Les opérateurs arithmétiques sont ceux que l'on apprend à l'école primaire… ou presque :

Quelques exemples de calcul
int nbre1, nbre2, nbre3;  //Déclaration des variables
 
nbre1 = 1 + 3;            //nbre1 vaut 4
nbre2 = 2 * 6;            //nbre2 vaut 12
nbre3 = nbre2 / nbre1;    //nbre3 vaut 3
nbre1 = 5 % 2;            //nbre1 vaut 1, car 5 = 2 * 2 + 1
nbre2 = 99 % 8;           //nbre2 vaut 3, car 99 = 8 * 12 + 3
nbre3 = 6 % 3;            //là, nbre3 vaut 0, car il n'y a pas de reste

Ici, nous voyons bien que nous pouvons affecter des résultats d'opérations sur des nombres à nos variables, mais aussi affecter des résultats d'opérations sur des variables de même type.

Maintenant, voici quelque chose que les personnes qui n'ont jamais programmé ont du mal à intégrer. Je garde la même déclaration de variables que ci-dessus.

int nbre1, nbre2, nbre3;    //Déclaration des variables
nbre1 = nbre2 = nbre3 = 0;  //Initialisation
 
nbre1 = nbre1 + 1;     //nbre1 = lui-même, donc 0 + 1 => nbre1 = 1
nbre1 = nbre1 + 1;     //nbre1 = 1 (cf. ci-dessus), maintenant, nbre1 = 1 + 1 = 2
nbre2 = nbre1;         //nbre2 = nbre1 = 2
nbre2 = nbre2 * 2;     //nbre2 = 2 => nbre2 = 2 * 2 = 4
nbre3 = nbre2;         //nbre3 = nbre2 = 4
nbre3 = nbre3 / nbre3; //nbre3 = 4 / 4 = 1
nbre1 = nbre3;         //nbre1 = nbre3 = 1
nbre1 = nbre1 - 1;     //nbre1 = 1 - 1 = 0

Et là aussi, il existe une syntaxe qui raccourcit l'écriture de ce genre d'opérations. Regardez bien :

nbre1 = nbre1 + 1;
nbre1 += 1;
nbre1++;
++nbre1;

Les trois premières syntaxes correspondent exactement à la même opération. La troisième sera certainement celle que vous utiliserez le plus, mais elle ne fonctionne que pour augmenter d'une unité la valeur de nbre1 ! Si vous voulez augmenter de 2 la valeur d'une variable, utilisez les deux syntaxes précédentes. On appelle cela l'incrémentation. La dernière fait la même chose que la troisième, mais il y a une subtilité dont nous reparlerons dans le chapitre sur les boucles.

Pour la soustraction, la syntaxe est identique :

nbre1 = nbre1 - 1;
nbre1 -= 1;
nbre1--;
--nbre1;

Même commentaire que pour l'addition, sauf qu'ici, la troisième syntaxe s'appelle la décrémentation.

Les raccourcis pour la multiplication fonctionnent de la même manière ; regardez plutôt :

nbre1 = nbre1 * 2;
nbre1 *= 2;
nbre1 = nbre1 / 2;
nbre1 /= 2;

Voici les raisons de ma mise en garde : comme je vous l'ai dit précédemment, chaque type de variable a une capacité différente et, pour faire simple, nous allons comparer nos variables à différents récipients. Une variable de type :

À partir de là, ce n'est plus qu'une question de bon sens. Vous devez facilement constater qu'il est possible de mettre le contenu d'un dé à coudre dans un verre ou un baril. Par contre, si vous versez le contenu d'un baril dans un verre… il y en a plein par terre !
Ainsi, si nous affectons le résultat d'une opération sur deux variables de type double dans une variable de type int, le résultat sera de type int et ne sera donc pas un réel mais un entier.

Pour afficher le contenu d'une variable dans la console, appelez l'instruction System.out.println(maVariable);, ou encore System.out.print(maVariable);.

Je suppose que vous voudriez aussi mettre du texte en même temps que vos variables… Eh bien sachez que l'opérateur « + » sert aussi d'opérateur de concaténation, c'est-à-dire qu'il permet de mélanger du texte brut et des variables. Voici un exemple d'affichage avec une perte de précision :

double nbre1 = 10, nbre2 = 3;
int resultat = (int)(nbre1 / nbre2);
System.out.println("Le résultat est = " + resultat);

Cependant, pour le bien de ce chapitre, nous n'allons pas utiliser cette méthode. Vous allez constater que le résultat affiché est 3 au lieu de 3.33333333333333… Et je pense que ceci vous intrigue :

int resultat = (int)(nbre1 / nbre2);

.

Avant que je ne vous explique, remplacez la ligne citée précédemment par :

int resultat = nbre1 / nbre2;

.

Vous allez voir qu'Eclipse n'aime pas du tout ! Pour comprendre cela, nous allons voir les conversions.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les différents types de variables Les conversions, ou « cast »

Les conversions, ou « cast »

Les opérateurs arithmétiques Depuis Java 7 : le formatage des nombres

Les conversions, ou « cast »

Comme expliqué précédemment, les variables de type double contiennent plus d'informations que les variables de type int. Ici, il va falloir écouter comme il faut… heu, pardon : lire comme il faut ! Nous allons voir un truc super important en Java. Ne vous en déplaise, vous serez amenés à convertir des variables.

D'un type int en type float :

int i = 123;
float j = (float)i;

D'un type int en double :

int i = 123;
double j = (double)i;

Et inversement :

double i = 1.23;
double j = 2.9999999;
int k = (int)i;        //k vaut 1
k = (int)j;            //k vaut 2

Ce type de conversion s'appelle une « conversion d'ajustement », ou cast de variable.

Vous l'avez vu : nous pouvons passer directement d'un type int à un type double. L'inverse, cependant, ne se déroulera pas sans une perte de précision. En effet, comme vous avez pu le constater, lorsque nous castons un double en int, la valeur de ce double est tronquée, ce qui signifie que l'int en question ne prendra que la valeur entière du double, quelle que soit celle des décimales.

Pour en revenir à notre problème de tout à l’heure, il est aussi possible de caster le résultat d'une opération mathématique en la mettant entre « ( ) » et en la précédant du type de cast souhaité. Donc :

double nbre1 = 10, nbre2 = 3;
int resultat = (int)(nbre1 / nbre2);
System.out.println("Le résultat est = " + resultat);

Voilà qui fonctionne parfaitement. Pour bien faire, vous devriez mettre le résultat de l'opération en type double. Et si on fait l'inverse : si nous déclarons deux entiers et que nous mettons le résultat dans un double ? Voici une possibilité :

int nbre1 = 3, nbre2 = 2;
double resultat = nbre1 / nbre2;
System.out.println("Le résultat est = " + resultat);

Vous aurez « 1 » comme résultat. Je ne caste pas ici, car un double peut contenir un int.

En voici une autre :

int nbre1 = 3, nbre2 = 2;
double resultat = (double)(nbre1 / nbre2);
System.out.println("Le résultat est = " + resultat);

Idem… Afin de comprendre pourquoi, vous devez savoir qu'en Java, comme dans d'autres langages d'ailleurs, il y a la notion de priorité d'opération ; et là, nous en avons un très bon exemple !

Dans le cas qui nous intéresse, il y a trois opérations :

Eh bien, Java exécute notre ligne dans cet ordre ! Il fait le calcul (ici 3/2), il caste le résultat en double, puis il l'affecte dans notre variable resultat.

Vous vous demandez sûrement pourquoi vous n'avez pas 1.5… C'est simple : lors de la première opération de Java, la JVM voit un cast à effectuer, mais sur un résultat de calcul. La JVM fait ce calcul (division de deux int qui, ici, nous donne 1), puis le cast (toujours 1), et affecte la valeur à la variable (encore et toujours 1).
Donc, pour avoir un résultat correct, il faudrait caster chaque nombre avant de faire l'opération, comme ceci :

int nbre1 = 3, nbre2 = 2;
double resultat = (double)(nbre1) / (double)(nbre2);
System.out.println("Le résultat est = " + resultat); 
//affiche : Le résultat est = 1.5

Je ne vais pas trop détailler ce qui suit (vous verrez cela plus en détail dans la partie sur la programmation orientée objet) ; mais vous allez maintenant apprendre à transformer l'argument d'un type donné, int par exemple, en String.

int i = 12;
String j = new String();
j = j.valueOf(i);

j est donc une variable de type String contenant la chaîne de caractères 12. Sachez que ceci fonctionne aussi avec les autres types numériques. Voyons maintenant comment faire marche arrière en partant de ce que nous venons de faire.

int i = 12;
String j = new String();
j = j.valueOf(i);
int k = Integer.valueOf(j).intValue();

Maintenant, la variable k de type int contient le nombre « 12 ».

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les opérateurs arithmétiques Depuis Java 7 : le formatage des nombres

Depuis Java 7 : le formatage des nombres

Les conversions, ou « cast » Lire les entrées clavier

Depuis Java 7 : le formatage des nombres

Comme vous le savez sûrement, le langage Java est en perpétuelle évolution. Les concepteurs ne cessent d'ajouter de nouvelles fonctionnalités qui simplifient la vie des développeurs. Ainsi dans la version 7 de Java, vous avez la possibilité de formater vos variables de types numériques avec un séparateur, l'underscore (_), ce qui peut s'avérer très pratique pour de grands nombres qui peuvent être difficiles à lire. Voici quelques exemples :

double nombre = 1000000000000;
//Peut s'écrire ainsi
double nombre = 1____000____000___000_000;
//Le nombre d'underscore n'a pas d'importance, vous pouvez en mettre autant que vous le souhaitez

//Voici quelques autres exemple d'utilisation
int entier = 32_000;
double monDouble = 12_34_56_78_89_10;
double monDouble2 = 1234_5678_8910;

Les underscore doivent être placés entre deux caractères numériques : ils ne peuvent donc pas être utilisés en début ou en fin de déclaration ni avant ou après un séparateur de décimal. Ainsi, ces déclarations ne sont pas valides :

double d = 123_.159;
int entier = _123;
int entier2 = 123_;

Avant Java 7, il était possible de déclarer des expressions numériques en hexadécimal, en utilisant le préfixe « 0x » :

int entier = 255; //Peut s'écrire « int entier = 0xFF; »
int entier = 20; //Peut s'écrire « int entier = 0x14; »
int entier = 5112; //Peut s'écrire « int entier = 0x13_F8; »

Depuis java 7, vous avez aussi la possibilité d'utiliser la notation binaire, en utilisant le préfixe « 0b » :

int entier = 0b1111_1111; //Est équivalent à : « int entier = 255; »
int entier = 0b1000_0000_0000; //Est équivalent à : « int entier = 2048; »
int entier = 0b100000000000; //Est équivalent à : « int entier = 2048; »

Certains programmes Java travaillent directement sur les bits, il peut donc être plus pratique de les représenter ainsi avant de les manipuler.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les conversions, ou « cast » Lire les entrées clavier

Lire les entrées clavier

Depuis Java 7 : le formatage des nombres La classe Scanner

Après la lecture de ce chapitre, vous pourrez saisir des informations et les stocker dans des variables afin de pouvoir les utiliser a posteriori.

En fait, jusqu'à ce que nous voyions les interfaces graphiques, nous travaillerons en mode console. Donc, afin de rendre nos programmes plus ludiques, il est de bon ton de pouvoir interagir avec ceux-ci. Par contre, ceci peut engendrer des erreurs (on parlera d'exceptions, mais ce sera traité plus loin). Afin de ne pas surcharger le chapitre, nous survolerons ce point sans voir les différents cas d'erreurs que cela peut engendrer.

La classe Scanner

Lire les entrées clavier Récupérer ce que vous tapez

La classe Scanner

Je me doute qu'il vous tardait de pouvoir communiquer avec votre application… Le moment est enfin venu ! Mais je vous préviens, la méthode que je vais vous donner présente des failles. Je vous fais confiance pour ne pas rentrer n'importe quoi n'importe quand !

Je vous ai dit que vos variables de type String sont en réalité des objets de type String. Pour que Java puisse lire ce que vous tapez au clavier, vous allez devoir utiliser un objet de type Scanner. Cet objet peut prendre différents paramètres, mais ici nous n'en utiliserons qu'un : celui qui correspond à l'entrée standard en Java. Lorsque vous faites System.out.println();, je vous rappelle que vous appliquez la méthode println() sur la sortie standard ; ici, nous allons utiliser l'entrée standard System.in. Donc, avant d'indiquer à Java qu'il faut lire ce que nous allons taper au clavier, nous devrons instancier un objet Scanner. Avant de vous expliquer ceci, créez une nouvelle classe et tapez cette ligne de code dans votre méthode main :

Scanner sc = new Scanner(System.in);

Vous devez avoir une jolie vague rouge sous le mot Scanner. Cliquez sur la croix rouge sur la gauche et faites un double-clic sur Import 'Scanner' java.util (figure suivante). Et là, l'erreur disparaît !

Importer la classe Scanner
Importer la classe Scanner

Maintenant, regardez au-dessus de la déclaration de votre classe, vous devriez voir cette ligne :

import java.util.Scanner;

Voilà ce que nous avons fait. Je vous ai dit qu'il fallait indiquer à Java où se trouve la classe Scanner. Pour faire ceci, nous devons importer la classe Scanner grâce à l'instruction import. La classe que nous voulons se trouve dans le package java.util.

Les classes qui se trouvent dans les packages autres que java.lang (package automatiquement importé par Java, on y trouve entre autres la classe System) sont à importer à la main dans vos classes Java pour pouvoir vous en servir. La façon dont nous avons importé la classe java.util.Scanner dans Eclipse est très commode. Vous pouvez aussi le faire manuellement :

//Ceci importe la classe Scanner du package java.util
import java.util.Scanner; 
//Ceci importe toutes les classes du package java.util
import java.util.*;
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Lire les entrées clavier Récupérer ce que vous tapez

Récupérer ce que vous tapez

La classe Scanner Les conditions

Récupérer ce que vous tapez

Voici l'instruction pour permettre à Java de récupérer ce que vous avez saisi pour ensuite l'afficher :

Scanner sc = new Scanner(System.in);
System.out.println("Veuillez saisir un mot :");
String str = sc.nextLine();
System.out.println("Vous avez saisi : " + str);

Une fois l'application lancée, le message que vous avez écrit auparavant s'affiche dans la console, en bas d'Eclipse. Pensez à cliquer dans la console afin que ce que vous saisissez y soit écrit et que Java puisse récupérer ce que vous avez inscrit (figure suivante) !

Saisie utilisateur dans la console
Saisie utilisateur dans la console

Si vous remplacez la ligne de code qui récupère une chaîne de caractères comme suit :

Scanner sc = new Scanner(System.in);
System.out.println("Veuillez saisir un nombre :");
int str = sc.nextInt();
System.out.println("Vous avez saisi le nombre : " + str);

… vous devriez constater que lorsque vous introduisez votre variable de type Scanner et que vous introduisez le point permettant d'appeler des méthodes de l'objet, Eclipse vous propose une liste de méthodes associées à cet objet (ceci s'appelle l'autocomplétion) ; de plus, lorsque vous commencez à taper le début de la méthode nextInt(), le choix se restreint jusqu'à ne laisser que cette seule méthode.

Exécutez et testez ce programme : vous verrez qu'il fonctionne à la perfection. Sauf… si vous saisissez autre chose qu'un nombre entier !

Vous savez maintenant que pour lire un int, vous devez utiliser nextInt(). De façon générale, dites-vous que pour récupérer un type de variable, il vous suffit d'appeler next<Type de variable commençant par une majuscule> (rappelez-vous de la convention de nommage Java).

Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
double d = sc.nextDouble();
long l = sc.nextLong();
byte b = sc.nextByte();
//Etc.

Voici comment on pourrait récupérer un caractère :

System.out.println("Saisissez une lettre :");
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
char carac = str.charAt(0);
System.out.println("Vous avez saisi le caractère : " + carac);

Qu'avons-nous fait ici ? Nous avons récupéré une chaîne de caractères, puis utilisé une méthode de l'objet String (ici, charAt(0) ) afin de récupérer le premier caractère saisi. Même si vous tapez une longue chaîne de caractères, l'instruction charAt(0) ne renverra que le premier caractère.

Vous devez vous demander pourquoi charAt(0) et non charAt(1) : nous aborderons ce point lorsque nous verrons les tableaux… Jusqu'à ce qu'on aborde les exceptions, je vous demanderai d'être rigoureux et de faire attention à ce que vous attendez comme type de données afin d'utiliser la méthode correspondante.

Une précision s'impose, toutefois : la méthode nextLine() récupère le contenu de toute la ligne saisie et replace la « tête de lecture » au début d'une autre ligne. Par contre, si vous avez invoqué une méthode comme nextInt(), nextDouble() et que vous invoquez directement après la méthode nextLine(), celle-ci ne vous invitera pas à saisir une chaîne de caractères : elle videra la ligne commencée par les autres instructions. En effet, celles-ci ne repositionnent pas la tête de lecture, l'instruction nextLine() le fait à leur place. Pour faire simple, ceci :

import java.util.Scanner;

public class Main {
  public static void main(String[] args){
    Scanner sc = new Scanner(System.in);
    System.out.println("Saisissez un entier : ");
    int i = sc.nextInt();
    System.out.println("Saisissez une chaîne : ");
    String str = sc.nextLine();      
    System.out.println("FIN ! ");
   }
}

… ne vous demandera pas de saisir une chaîne et affichera directement « Fin ». Pour pallier ce problème, il suffit de vider la ligne après les instructions ne le faisant pas automatiquement :

import java.util.Scanner;

public class Main {
  public static void main(String[] args){
    Scanner sc = new Scanner(System.in);
    System.out.println("Saisissez un entier : ");
    int i = sc.nextInt();
    System.out.println("Saisissez une chaîne : ");
    //On vide la ligne avant d'en lire une autre
    sc.nextLine();
    String str = sc.nextLine();      
    System.out.println("FIN ! ");
  }
}
En résumé
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

La classe Scanner Les conditions

Les conditions

Récupérer ce que vous tapez La structure if… else

Nous abordons ici l'un des chapitres les plus importants : les conditions sont une autre notion fondamentale de la programmation. En effet, ce qui va être développé ici s'applique à énormément de langages de programmation, et pas seulement à Java.

Dans une classe, la lecture et l'exécution se font de façon séquentielle, c'est-à-dire ligne par ligne. Avec les conditions, nous allons pouvoir gérer différents cas de figure sans pour autant lire tout le code. Vous vous rendrez vite compte que tous vos projets ne sont que des enchaînements et des imbrications de conditions et de boucles (notion que l'on abordera au chapitre suivant).

Assez de belles paroles ! Entrons tout de suite dans le vif du sujet.

La structure if… else

Les conditions La structure switch

La structure if… else

Avant de pouvoir créer et évaluer des conditions, vous devez savoir que pour y parvenir, nous allons utiliser ce qu'on appelle des « opérateurs logiques ». Ceux-ci sont surtout utilisés lors de conditions (si [test] alors [faire ceci]) pour évaluer différents cas possibles. Voici les différents opérateurs à connaître :

Comme je vous l'ai dit dans le chapitre précédent, les opérations en Java sont soumises à des priorités. Tous ces opérateurs se plient à cette règle, de la même manière que les opérateurs arithmétiques…

Imaginons un programme qui demande à un utilisateur d'entrer un nombre entier relatif (qui peut être soit négatif, soit nul, soit positif). Les structures conditionnelles vont nous permettre de gérer ces trois cas de figure. La structure de ces conditions ressemble à ça :

if(//condition)
{
  //Exécution des instructions si la condition est remplie
}
else
{
  //Exécution des instructions si la condition n'est pas remplie
}

Cela peut se traduire par « si… sinon… ».

Le résultat de l'expression évaluée par l'instruction if sera un boolean, donc soit true, soit false. La portion de code du bloc if ne sera exécutée que si la condition est remplie. Dans le cas contraire, c'est le bloc de l'instruction else qui le sera. Mettons notre petit exemple en pratique :

int i = 10;
 
if (i < 0)
  System.out.println("le nombre est négatif");
else
  System.out.println("le nombre est positif");

Essayez ce petit code, et vous verrez comment il fonctionne. Dans ce cas, notre classe affiche « le nombre est positif ». Expliquons un peu ce qui se passe.

Attends un peu ! Lorsque tu nous as présenté la structure des conditions, tu as mis des accolades et là, tu n'en mets pas. Pourquoi ?

Bien observé. En fait, les accolades sont présentes dans la structure « normale » des conditions, mais lorsque le code à l'intérieur de l'une d'entre elles n'est composé que d'une seule ligne, les accolades deviennent facultatives.

Comme nous avons l'esprit perfectionniste, nous voulons que notre programme affiche « le nombre est nul » lorsque i est égal à 0 ; nous allons donc ajouter une condition. Comment faire ? La condition du if est remplie si le nombre est strictement négatif, ce qui n'est pas le cas ici puisque nous allons le mettre à 0. Le code contenu dans la clause else est donc exécuté si le nombre est égal ou strictement supérieur à 0. Il nous suffit d'ajouter une condition à l'intérieur de la clause else, comme ceci :

int i = 0;
if (i < 0)
{
  System.out.println("Ce nombre est négatif !");
} 
else
{
  if(i == 0)
    System.out.println("Ce nombre est nul !");
 
  else
    System.out.println("Ce nombre est positif !");
 
}

Maintenant que vous avez tout compris, je vais vous présenter une autre façon d'écrire ce code, avec le même résultat : on ajoute juste un petit « sinon si… ».

int i = 0;
if (i < 0)
  System.out.println("Ce nombre est négatif !");      
  
else if(i > 0)
  System.out.println("Ce nombre est positif !");           
 
else  
  System.out.println("Ce nombre est nul !");

Alors ? Explicite, n'est-ce pas ?

Ici, je vais très fortement insister sur un point : regardez l'affichage du code et remarquez le petit décalage entre le test et le code à exécuter. On appelle cela l'indentation !

Pour vous repérer dans vos futurs programmes, cela sera très utile. Imaginez deux secondes que vous avez un programme de 700 lignes avec 150 conditions, et que tout est écrit le long du bord gauche. Il sera difficile de distinguer les tests du code. Vous n'êtes pas obligés de le faire, mais je vous assure que vous y viendrez.

Les conditions multiples

Derrière ce nom barbare se cachent simplement plusieurs tests dans une instruction if (ou else if). Nous allons maintenant utiliser les opérateurs logiques que nous avons vus au début en vérifiant si un nombre donné appartient à un intervalle connu. Par exemple, on va vérifier si un entier est compris entre 50 et 100.

int i = 58;
if(i < 100 && i > 50) 
  System.out.println("Le nombre est bien dans l'intervalle.");
else
  System.out.println("Le nombre n'est pas dans l'intervalle.");

Nous avons utilisé l'opérateur &&. La condition de notre if est devenue : « si i est inférieur à 100 ET supérieur à 50 ».

Cet opérateur vous initie à la notion d'intersection d'ensembles. Ici, nous avons deux conditions qui définissent un ensemble chacune :

L'opérateur « && » permet de faire l'intersection de ces ensembles. La condition regroupe donc les nombres qui appartiennent à ces deux ensembles, c’est-à-dire les nombres de 51 à 99 inclus. Réfléchissez bien à l'intervalle que vous voulez définir. Voyez ce code :

int i = 58;
if(i < 100 && i > 100) 
  System.out.println("Le nombre est bien dans l'intervalle.");
else
  System.out.println("Le nombre n'est pas dans l'intervalle.");

Ici, la condition ne sera jamais remplie, car je ne connais aucun nombre qui soit à la fois plus petit et plus grand que 100 ! Reprenez le code précédent et remplacez l'opérateur « && » par « || » (petit rappel, il s'agit du OU). À l'exécution du programme et après plusieurs tests de valeur pour i, vous pourrez vous apercevoir que tous les nombres remplissent cette condition, sauf 100.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les conditions La structure switch

La structure switch

La structure if… else La condition ternaire

La structure switch

Le switch est surtout utilisé lorsque nous voulons des conditions « à la carte ». Prenons l'exemple d'une interrogation comportant deux questions : pour chacune d'elles, on peut obtenir uniquement 0 ou 10 points, ce qui nous donne au final trois notes et donc trois appréciations possibles, comme ceci :

Dans ce genre de cas, on utilise un switch pour éviter des else if à répétition et pour alléger un peu le code. Je vais vous montrer comment se construit une instruction switch ; puis nous allons l'utiliser tout de suite après.

Syntaxe

switch (/*Variable*/)
{
  case /*Argument*/:
    /*Action*/;
    break;        
  default:
    /*Action*/;             
}

Voici les opérations qu'effectue cette expression :

Notez bien la présence de l'instruction break;. Elle permet de sortir du switch si une languette correspond. Pour mieux juger de l'utilité de cette instruction, enlevez tous les break; et compilez votre programme. Vous verrez le résultat… Voici un exemple de switch que vous pouvez essayer :

int note = 10; //On imagine que la note maximale est 20

switch (note)
{
  case 0:
    System.out.println("Ouch !");
    break;
  case 10:
    System.out.println("Vous avez juste la moyenne.");
    break;
  case 20:
    System.out.println("Parfait !");
    break;
  default:
    System.out.println("Il faut davantage travailler.");
}

Si vous avez essayé ce programme en supprimant l'instruction break;, vous avez dû vous rendre compte que le switch exécute le code contenu dans le case 10:, mais aussi dans tous ceux qui suivent ! L'instruction break; permet de sortir de l'opération en cours. Dans notre cas, on sort de l'instruction switch, mais nous verrons une autre utilité à break; dans le chapitre suivant.

Depuis la version 7 de Java, l'instruction switch accepte les objets de type String en paramètre. De ce fait, cette instruction est donc valide :

String chaine = "Bonjour";

switch(chaine) {
  case "Bonjour":
    System.out.println("Bonjour monsieur !");
    break;
  case "Bonsoir":
    System.out.println("Bonsoir monsieur !");
    break;
  default:
    System.out.println("Bonjoir ! :p");
}
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

La structure if… else La condition ternaire

La condition ternaire

La structure switch Les boucles

La condition ternaire

Les conditions ternaires sont assez complexes et relativement peu utilisées. Je vous les présente ici à titre indicatif. La particularité de ces conditions réside dans le fait que trois opérandes (c'est-à-dire des variables ou des constantes) sont mis en jeu, mais aussi que ces conditions sont employées pour affecter des données à une variable. Voici à quoi ressemble la structure de ce type de condition :

int x = 10, y = 20;
int max = (x < y) ? y : x ; //Maintenant, max vaut 20

Décortiquons ce qu'il se passe :

Vous pouvez également faire des calculs (par exemple) avant d'affecter les valeurs :

int x = 10, y = 20;
int max = (x < y) ? y * 2 : x * 2 ; //Ici, max vaut 2 * 20 donc 40

N'oubliez pas que la valeur que vous allez affecter à votre variable doit être du même type que votre variable. Sachez aussi que rien ne vous empêche d'insérer une condition ternaire dans une autre condition ternaire :

int x = 10, y = 20;

int max = (x < y) ? (y < 10) ? y % 10 : y * 2 : x ; //Max vaut 40

//Pas très facile à lire…
//Vous pouvez entourer votre deuxième instruction ternaire par des parenthèses pour mieux voir :

max = (x < y) ? ((y < 10) ? y % 10 : y * 2) : x ; //Max vaut 40
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

La structure switch Les boucles

Les boucles

La condition ternaire La boucle while

Le rôle des boucles est de répéter un certain nombre de fois les mêmes opérations. Tous les programmes, ou presque, ont besoin de ce type de fonctionnalité. Nous utiliserons les boucles pour permettre à un programme de recommencer depuis le début, pour attendre une action précise de l'utilisateur, parcourir une série de données, etc.

Une boucle s'exécute tant qu'une condition est remplie. Nous réutiliserons donc des notions du chapitre précédent !

La boucle while

Les boucles La boucle do… while

La boucle while

Pour décortiquer précisément ce qui se passe dans une boucle, nous allons voir comment elle se construit ! Une boucle commence par une déclaration : ici while. Cela veut dire, à peu de chose près, « tant que ». Puis nous avons une condition : c'est elle qui permet à la boucle de s'arrêter. Une boucle n'est utile que lorsque nous pouvons la contrôler, et donc lui faire répéter une instruction un certain nombre de fois. C'est à ça que servent les conditions. Ensuite nous avons une ou plusieurs instructions : c'est ce que va répéter notre boucle (il peut même y avoir des boucles dans une boucle !

while (/* Condition */)
{
  //Instructions à répéter
}

Nous allons travailler sur un exemple concret mais d'abord, réfléchissons à « comment notre boucle va travailler ». Pour cela, il faut déterminer notre exemple.

Nous allons afficher « Bonjour, <un prénom> », prénom qu'il faudra taper au clavier ; puis nous demanderons si l'on veut recommencer. Pour cela, il nous faut une variable qui va recevoir le prénom, donc dont le type sera String, ainsi qu'une variable pour récupérer la réponse. Et là, plusieurs choix s'offrent à nous : soit un caractère, soit une chaîne de caractères, soit un entier. Ici, nous prendrons une variable de type char. C'est parti !

//Une variable vide
String prenom;
//On initialise celle-ci à O pour oui
char reponse = 'O';
//Notre objet Scanner, n'oubliez pas l'import de java.util.Scanner !
Scanner sc = new Scanner(System.in);
//Tant que la réponse donnée est égale à oui…
while (reponse == 'O')
{
  //On affiche une instruction
  System.out.println("Donnez un prénom : ");
  //On récupère le prénom saisi
  prenom = sc.nextLine();
  //On affiche notre phrase avec le prénom
  System.out.println("Bonjour " +prenom+ ", comment vas-tu ?");
  //On demande si la personne veut faire un autre essai
  System.out.println("Voulez-vous réessayer ? (O/N)");
  //On récupère la réponse de l'utilisateur
  reponse = sc.nextLine().charAt(0);
}
 
System.out.println("Au revoir…");
//Fin de la boucle

Vous avez dû cligner des yeux en lisant reponse = sc.nextLine().charAt(0);. Rappelez-vous comment on récupère un char avec l'objet Scanner : nous devons récupérer un objet String et ensuite prendre le premier caractère de celui-ci ! Eh bien cette syntaxe est une contraction de ce que je vous avais fait voir auparavant.

Détaillons un peu ce qu'il se passe. Dans un premier temps, nous avons déclaré et initialisé nos variables. Ensuite, la boucle évalue la condition qui nous dit : tant que la variable reponse contient « O », on exécute la boucle. Celle-ci contient bien le caractère « O », donc nous entrons dans la boucle. Puis l'exécution des instructions suivant l'ordre dans lequel elles apparaissent dans la boucle a lieu. À la fin, c'est-à-dire à l'accolade fermante de la boucle, le compilateur nous ramène au début de la boucle.

Voilà. C'est pas mal, mais il faudrait forcer l'utilisateur à ne taper que « O » ou « N ». Comment faire ? C'est très simple : avec une boucle ! Il suffit de forcer l'utilisateur à entrer soit « N » soit « O » avec un while ! Attention, il nous faudra réinitialiser la variable reponse à « ' ' » (caractère vide). Il faudra donc répéter la phase « Voulez-vous réessayer ? » tant que la réponse donnée n'est pas « O » ou « N ».

Voici notre programme dans son intégralité :

String prenom;
char reponse = 'O';
Scanner sc = new Scanner(System.in);
while (reponse == 'O')
{
  System.out.println("Donnez un prénom : ");
  prenom = sc.nextLine();
  System.out.println("Bonjour " +prenom+ ", comment vas-tu ?");                        
  //Sans ça, nous n'entrerions pas dans la deuxième boucle
  reponse = ' ';
                        
  //Tant que la réponse n'est pas O ou N, on repose la question
  while(reponse != 'O' && reponse != 'N')
  {
    //On demande si la personne veut faire un autre essai
    System.out.println("Voulez-vous réessayer ? (O/N)");
    reponse = sc.nextLine().charAt(0);
  }
}
System.out.println("Au revoir…");

Vous pouvez tester ce code (c'est d'ailleurs vivement conseillé) : vous verrez que si vous n'entrez pas la bonne lettre, le programme vous posera sans cesse sa question, comme à la figure suivante !

Les instructions dans la boucle se répètent
Les instructions dans la boucle se répètent

Attention à écrire correctement vos conditions et à bien vérifier vos variables dans vos while, et dans toutes vos boucles en général. Sinon c'est le drame ! Essayez d'exécuter le programme précédent sans la réinitialisation de la variable reponse, et vous verrez le résultat ! On n'entre jamais dans la deuxième boucle, car reponse = 'O' (puisque initialisée ainsi au début du programme). Là, vous ne pourrez jamais changer sa valeur… Le programme ne s'arrêtera donc jamais ! On appelle ça une « boucle infinie ». En voici un autre exemple.

int a = 1, b = 15;
while (a < b)
{
  System.out.println("coucou " +a+ " fois !!");
}

Si vous lancez ce programme, vous allez voir une quantité astronomique de « coucou 1 fois !! ». Nous aurions dû ajouter une instruction dans le bloc d'instructions de notre while pour changer la valeur de a à chaque tour de boucle, comme ceci :

int a = 1, b = 15;
while (a < b)
{
  System.out.println("coucou " +a+ " fois !!");
  a++;
}

Ce qui nous donnerait comme résultat la figure suivante.

Correction de la boucle infinie
Correction de la boucle infinie

Vous auriez aussi pu utiliser cette syntaxe :

int a = 1, b = 15;
while (a++ < b)
   System.out.println("coucou " +a+ " fois !!");

Souvenez-vous de ce dont je vous parlais au chapitre précédent sur la priorité des opérateurs. Ici, l'opérateur « < » a la priorité sur l'opérateur d'incrémentation « ++ ». Pour faire court, la boucle while teste la condition et ensuite incrémente la variable a. Par contre, essayez ce code :

int a = 1, b = 15;
while (++a < b)
  System.out.println("coucou " +a+ " fois !!");

Vous devez remarquer qu'il y a un tour de boucle en moins ! Eh bien avec cette syntaxe, l'opérateur d'incrémentation est prioritaire sur l'opérateur d'inégalité (ou d'égalité), c'est-à-dire que la boucle incrémente la variable a, et ce n'est qu'après l'avoir fait qu'elle teste la condition !

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les boucles La boucle do… while

La boucle do… while

La boucle while La boucle for

La boucle do… while

Puisque je viens de vous expliquer comment fonctionne une boucle while, je ne vais pas vraiment m'attarder sur la boucle do… while. En effet, ces deux boucles ne sont pas cousines, mais plutôt sœurs. Leur fonctionnement est identique à deux détails près.

do{
  //Instructions
}while(a < b);
Première différence

La boucle do… while s'exécutera au moins une fois, contrairement à sa sœur. C'est-à-dire que la phase de test de la condition se fait à la fin, car la condition se met après le while.

Deuxième différence

C'est une différence de syntaxe, qui se situe après la condition du while. Vous voyez la différence ? Oui ? Non ? Il y a un « ;» après le while. C'est tout ! Ne l'oubliez cependant pas, sinon le programme ne compilera pas.

Mis à part ces deux éléments, ces boucles fonctionnent exactement de la même manière. D'ailleurs, refaisons notre programme précédent avec une boucle do… while.

String prenom = new String();
//Pas besoin d'initialiser : on entre au moins une fois dans la boucle !
char reponse = ' ';
 
Scanner sc = new Scanner(System.in);
 
do{
  System.out.println("Donnez un prénom : ");
  prenom = sc.nextLine();
  System.out.println("Bonjour " +prenom+ ", comment vas-tu ?");
       
  do{
    System.out.println("Voulez-vous réessayer ? (O/N)");
    reponse = sc.nextLine().charAt(0);
  }while(reponse != 'O' && reponse != 'N');
        
}while (reponse == 'O');
 
System.out.println("Au revoir…");

Vous voyez donc que ce code ressemble beaucoup à celui utilisé avec la boucle while, mais il comporte une petite subtilité : ici, plus besoin de réinitialiser la variable reponse, puisque de toute manière, la boucle s'exécutera au moins une fois !

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

La boucle while La boucle for

La boucle for

La boucle do… while TP : conversion Celsius - Fahrenheit

La boucle for

Cette boucle est un peu particulière puisqu'elle prend tous ses attributs dans sa condition et agit en conséquence. Je m'explique : jusqu'ici, nous avions fait des boucles avec :

Eh bien on met tout ça dans la condition de la boucle for, et c'est tout. Il existe une autre syntaxe pour la boucle for depuis le JDK 1.5. Nous la verrons lorsque nous aborderons les tableaux. Mais je sais bien qu'un long discours ne vaut pas un exemple, alors voici une boucle for sous vos yeux ébahis :

for(int i = 1; i <= 10; i++)
{
  System.out.println("Voici la ligne "+i);
}

Ce qui donne la figure suivante.

Test de boucle for
Test de boucle for

Vous aurez sûrement remarqué la présence des « ; » dans la condition pour la séparation des champs. Ne les oubliez surtout pas, sinon le programme ne compilera pas.

Nous pouvons aussi inverser le sens de la boucle, c'est-à-dire qu'au lieu de partir de 0 pour aller à 10, nous allons commencer à 10 pour atteindre 0 :

for(int i = 10; i >= 0; i--)
  System.out.println("Il reste "+i+" ligne(s) à écrire");

On obtient la figure suivante.

Boucle for avec décrémentation
Boucle for avec décrémentation

Pour simplifier, la boucle for est un peu le condensé d'une boucle while dont le nombre de tours se détermine via un incrément. Nous avons un nombre de départ, une condition qui doit être remplie pour exécuter une nouvelle fois la boucle et une instruction de fin de boucle qui incrémente notre nombre de départ. Remarquez que rien ne nous empêche de cumuler les déclarations, les conditions et les instructions de fin de boucle :

for(int i = 0, j = 2; (i < 10 && j < 6); i++, j+=2){
  System.out.println("i = " + i + ", j = " + j);
}

Ici, cette boucle n'effectuera que deux tours puisque la condition (i < 10 && j < 6) est remplie dès le deuxième tour, la variable j commençant à 2 et étant incrémentée de deux à chaque tour de boucle.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

La boucle do… while TP : conversion Celsius - Fahrenheit

TP : conversion Celsius - Fahrenheit

La boucle for Élaboration

Voilà un petit TP qui va vous permettre de mettre en œuvre toutes les notions que vous avez vues jusqu'ici :

Accrochez-vous, car je vais vous demander de penser à des tonnes de choses, et vous serez tout seuls. Lâchés dans la nature… Mais non je plaisante, je vais vous guider un peu. ;)

Élaboration

TP : conversion Celsius - Fahrenheit Correction

Élaboration

Voici les caractéristiques du programme que nous allons devoir réaliser :

Avant de vous lancer dans la programmation à proprement parler, je vous conseille fortement de réfléchir à votre code… sur papier. Réfléchissez à ce qu'il vous faut comme nombre de variables, les types de variables, comment va se dérouler le programme, les conditions et les boucles utilisées.

À toutes fins utiles, voici la formule de conversion pour passer des degrés Celsius en degrés Fahrenheit : F = \frac{9}{5} imes C + 32 ; pour l'opération inverse, c'est comme ceci : C = \frac{(F - 32) imes 5 }{9}.

La figure suivante est un aperçu de ce que je vous demande.

Rendu du TP
Rendu du TP

Je vais également vous donner une fonction toute faite qui vous permettra éventuellement d'arrondir vos résultats. Je vous expliquerai le fonctionnement des fonctions dans deux chapitres. Vous pouvez très bien ne pas vous en servir. Pour ceux qui souhaitent tout de même l'utiliser, la voici :

public static double arrondi(double A, int B) {
  return (double) ( (int) (A * Math.pow(10, B) + .5)) / Math.pow(10, B);
}

Elle est à placer entre les deux accolades fermantes de votre classe, comme à la figure suivante.

Emplacement de la fonction
Emplacement de la fonction

Voici comment utiliser cette fonction : imaginez que vous avez la variable faren à arrondir, et que le résultat obtenu est enregistré dans une variable arrondFaren ; vous procéderez comme suit :

arrondFaren = arrondi(faren,1); //Pour un chiffre après la virgule
arrondFaren = arrondi(faren, 2);//Pour deux chiffres après la virgule, etc.

Quelques dernières recommandations : essayez de bien indenter votre code ! Prenez votre temps. Essayez de penser à tous les cas de figure. Maintenant à vos papiers, crayons, neurones, claviers… et bon courage !

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

TP : conversion Celsius - Fahrenheit Correction

Correction

Élaboration Les tableaux

Correction

STOP ! C'est fini ! Il est temps de passer à la correction de ce premier TP. Ça va ? Pas trop mal à la tête ? Je me doute qu'il a dû y avoir quelques tubes d'aspirine vidés. Mais vous allez voir qu'en définitive, ce TP n'était pas si compliqué que ça.

Surtout, n'allez pas croire que ma correction est parole d'évangile. Il y avait différentes manières d'obtenir le même résultat. Voici tout de même une des solutions possibles.

import java.util.Scanner;

class Sdz1 {
  public static void main(String[] args) {
    //Notre objet Scanner
    Scanner sc = new Scanner(System.in);

    //initialisation des variables
    double aConvertir, convertit=0;
    char reponse=' ', mode = ' ';

    System.out.println("CONVERTISSEUR DEGRÉS CELSIUS ET DEGRÉS FAHRENHEIT");
    System.out.println("-------------------------------------------------");
    
    do{//tant que reponse = O //boucle principale
       
      do{//tant que reponse n'est pas O ou N
        mode = ' ';
        System.out.println("Choisissez le mode de conversion : ");
        System.out.println("1 - Convertisseur Celsius - Fahrenheit");
        System.out.println("2 - Convertisseur Fahrenheit - Celsius ");
        mode = sc.nextLine().charAt(0);
       
        if(mode != '1' && mode != '2')
          System.out.println("Mode inconnu, veuillez réitérer votre choix.");

      }while (mode != '1' && mode != '2');
       
      //saisie de la température à convertir
      System.out.println("Température à convertir :");
      aConvertir = sc.nextDouble();
      //Pensez à vider la ligne lue
      sc.nextLine();
     
      //Selon le mode, on calcule différemment et on affiche le résultat
      if(mode == '1'){
        convertit = ((9.0/5.0) * aConvertir) + 32.0;
        System.out.print(aConvertir + " °C correspond à : ");
        System.out.println(arrondi(convertit, 2) + " °F.");
      }
      else{
        convertit = ((aConvertir - 32) * 5) / 9;
        System.out.print(aConvertir + " °F correspond à : ");
        System.out.println(arrondi(convertit, 2) + " °C.");
      }
     
      //On invite l'utilisateur à recommencer ou à quitter
      do{       
        System.out.println("Souhaitez-vous convertir une autre température ?(O/N)");
        reponse = sc.nextLine().charAt(0);
       
      }while(reponse != 'O' && reponse != 'N');
     
    }while(reponse == 'O');
    
  System.out.println("Au revoir !");
    
  //Fin de programme
  }
    
  public static double arrondi(double A, int B) {
    return (double) ( (int) (A * Math.pow(10, B) + .5)) / Math.pow(10, B);
  } 
}
Explications concernant ce code

Ce programme n'est pas parfait, loin de là. La vocation de celui-ci était de vous faire utiliser ce que vous avez appris, et je pense qu'il remplit bien sa fonction. J'espère que vous avez apprécié ce TP. Je sais qu'il n'était pas facile, mais avouez-le : il vous a bien fait utiliser tout ce que vous avez vu jusqu'ici !

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Élaboration Les tableaux

Les tableaux

Correction Tableau à une dimension

Comme tout langage de programmation qui se respecte, Java travaille avec des tableaux. Vous verrez que ceux-ci s'avèrent bien pratiques.

Vous vous doutez (je suppose) que les tableaux dont nous parlons n'ont pas grand-chose à voir avec ceux que vous connaissez ! En programmation, un tableau n'est rien d'autre qu'une variable un peu particulière. Nous allons en effet pouvoir lui affecter plusieurs valeurs ordonnées séquentiellement que nous pourrons appeler au moyen d'un indice (ou d'un compteur, si vous préférez). Il nous suffira d'introduire l'emplacement du contenu désiré dans notre variable tableau pour la sortir, travailler avec, l'afficher…

Assez bavardé : mettons-nous joyeusement au travail !

Tableau à une dimension

Les tableaux Les tableaux multidimensionnels

Tableau à une dimension

Je viens de vous expliquer grosso modo ce qu'est un tableau en programmation. Si maintenant, je vous disais qu'il y a autant de types de tableaux que de types de variables ? Je crois voir quelques gouttes de sueur perler sur vos fronts…

Pas de panique ! C'est très logique : comme nous l'avons vu auparavant, une variable d'un type donné ne peut contenir que des éléments de ce type : une variable de type int ne peut pas recevoir une chaîne de caractères. Il en va de même pour les tableaux. Voyons tout de suite comment ils se déclarent :

<type du tableau> <nom du tableau> [] = { <contenu du tableau>};

La déclaration ressemble beaucoup à celle d'une variable quelconque, si ce n'est la présence de crochets « [ ] » après le nom de notre tableau et d'accolades « { } » encadrant l'initialisation de celui-ci. Dans la pratique, ça nous donnerait quelque chose comme ceci :

int tableauEntier[] = {0,1,2,3,4,5,6,7,8,9};
double tableauDouble[] = {0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0};
char tableauCaractere[] = {'a','b','c','d','e','f','g'};
String tableauChaine[] = {"chaine1", "chaine2", "chaine3" , "chaine4"};

Vous remarquez bien que la déclaration et l'initialisation d'un tableau se font comme avec une variable ordinaire : il faut utiliser des « ' ' » pour initialiser un tableau de caractères, des « " " » pour initialiser un tableau de String, etc. Vous pouvez aussi déclarer un tableau vide, mais celui-ci devra impérativement contenir un nombre de cases bien défini. Par exemple, si vous voulez un tableau vide de six entiers :

int tableauEntier[] = new int[6];
//Ou encore
int[] tableauEntier2 = new int[6];

Cette opération est très simple, car vraiment ressemblante à ce que vous faisiez avec vos variables ; je vous propose donc tout de suite de nous pencher sur une belle variante de ces tableaux…

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les tableaux Les tableaux multidimensionnels

Les tableaux multidimensionnels

Tableau à une dimension Utiliser et rechercher dans un tableau

Les tableaux multidimensionnels

Ici, les choses se compliquent un peu, car un tableau multidimensionnel n'est rien d'autre qu'un tableau contenant au minimum deux tableaux… Je me doute bien que cette notion doit en effrayer plus d'un, mais en réalité, elle n'est pas si difficile que ça à appréhender. Comme tout ce que je vous apprends en général !

Je ne vais pas vous faire de grand laïus sur ce type de tableau, puisque je pense sincèrement qu'un exemple vous en fera beaucoup mieux comprendre le concept. Imaginez un tableau avec deux lignes : la première contiendra les premiers nombres pairs, et le deuxième contiendra les premiers nombres impairs.

Ce tableau s'appellera premiersNombres. Voilà ce que cela donnerait :

int premiersNombres[][] = { {0,2,4,6,8},{1,3,5,7,9} };

Nous voyons bien ici les deux lignes de notre tableau symbolisées par les doubles crochets [ ][ ]. Et comme je l'ai dit plus haut, ce genre de tableau est composé de plusieurs tableaux. Ainsi, pour passer d'une ligne à l'autre, nous jouerons avec la valeur du premier crochet. Exemple : premiersNombres[0][0] correspondra au premier élément de la ligne paire, et premiersNombres[1][0] correspondra au premier élément de la ligne impaire.

La figure suivante représente un petit schéma en guise de synthèse.

Comprendre un tableau bidimensionnel
Comprendre un tableau bidimensionnel

Maintenant, je vais vous proposer de vous amuser un peu avec les tableaux…

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Tableau à une dimension Utiliser et rechercher dans un tableau

Utiliser et rechercher dans un tableau

Les tableaux multidimensionnels Les méthodes de classe

Utiliser et rechercher dans un tableau

Avant d'attaquer, je dois vous dire quelque chose de primordial : un tableau débute toujours à l'indice 0 ! Je m'explique : prenons l'exemple du tableau de caractères contenant les lettres de l'alphabet dans l'ordre qui a été donné plus haut. Si vous voulez afficher la lettre « a » à l'écran, vous devrez taper cette ligne de code :

System.out.println(tableauCaractere[0]);

Cela implique qu'un tableau contenant 4 éléments aura comme indices possibles 0, 1, 2 ou 3. Le 0 correspond au premier élément, le 1 correspond au 2e élément, le 2 correspond au 3e élément et le 3 correspond au 4e élément.

Ce que je vous propose, c'est tout simplement d'afficher un des tableaux présentés ci-dessus dans son intégralité. Sachez qu'il existe une instruction qui retourne la taille d'un tableau : grâce à elle, nous pourrons arrêter notre boucle (car oui, nous allons utiliser une boucle). Il s'agit de l'instruction <mon tableau>.length. Notre boucle for pourrait donc ressembler à ceci :

char tableauCaractere[] = {'a','b','c','d','e','f','g'};
       
for(int i = 0; i < tableauCaractere.length; i++)
{
  System.out.println("À l'emplacement " + i +" du tableau nous avons = " + tableauCaractere[i]);
}

Cela affichera :

À l'emplacement 0 du tableau nous avons = a
À l'emplacement 1 du tableau nous avons = b
À l'emplacement 2 du tableau nous avons = c
À l'emplacement 3 du tableau nous avons = d
À l'emplacement 4 du tableau nous avons = e
À l'emplacement 5 du tableau nous avons = f
À l'emplacement 6 du tableau nous avons = g

Maintenant, nous allons essayer de faire une recherche dans un de ces tableaux. En gros, il va falloir effectuer une saisie clavier et regarder si celle-ci est présente dans le tableau… Gardez la partie de code permettant de faire plusieurs fois la même action ; ensuite, faites une boucle de recherche incluant la saisie clavier, un message si la saisie est trouvée dans le tableau, et un autre message si celle-ci n'est pas trouvée. Ce qui nous donne :

char tableauCaractere[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
int i = 0;
char reponse = ' ',carac = ' ';
Scanner sc = new Scanner(System.in);
         
do {//Boucle principale
  do {//On répète cette boucle tant que l'utilisateur n'a pas rentré une lettre figurant dans le tableau
    i = 0;
    System.out.println("Rentrez une lettre en minuscule, SVP ");
                
    carac = sc.nextLine().charAt(0);
    //Boucle de recherche dans le tableau
    while(i < tableauCaractere.length && carac != tableauCaractere[i])
      i++;
         
    //Si i < 7 c'est que la boucle n'a pas dépassé le nombre de cases du tableau 
    if (i < tableauCaractere.length)
      System.out.println(" La lettre " +carac+ " se trouve bien dans le tableau !");
    else //Sinon
      System.out.println(" La lettre " +carac+ " ne se trouve pas dans le tableau !");
         
  }while(i >= tableauCaractere.length);

  //Tant que la lettre de l'utilisateur ne correspond pas à une lettre du tableau    
  do{
    System.out.println("Voulez-vous essayer à nouveau ? (O/N)");
    reponse = sc.nextLine().charAt(0);
  }while(reponse != 'N' && reponse != 'O');      
}while (reponse == 'O');
                
System.out.println("Au revoir !");

Le résultat de ce code se trouve à la figure suivante.

Résultat de la recherche
Résultat de la recherche
Explications sur la recherche

Dans notre while, il y a deux conditions.

La première correspond au compteur : tant que celui-ci est inférieur ou égal au nombre d'éléments du tableau, on l'incrémente pour regarder la valeur suivante. Nous passons ainsi en revue tout ce qui se trouve dans notre tableau. Si nous n'avions mis que cette condition, la boucle n'aurait fait que parcourir le tableau, sans voir si le caractère saisi correspond bien à un caractère de notre tableau, d'où la deuxième condition.

La deuxième correspond à la comparaison entre le caractère saisi et la recherche dans le tableau. Grâce à elle, si le caractère saisi se trouve dans le tableau, la boucle prend fin, et donc i a une valeur inférieure à 7.

À ce stade, notre recherche est terminée. Après cela, les conditions coulent de source ! Si nous avons trouvé une correspondance entre le caractère saisi et notre tableau, i prendra une valeur inférieure à 7 (vu qu'il y a 7 éléments dans notre tableau, l'indice maximum étant 7-1, soit 6). Dans ce cas, nous affichons un message confirmant la présence de l’élément recherché. Dans le cas contraire, c'est l'instruction du else qui s'exécutera.

En travaillant avec les tableaux, vous serez confrontés, un jour ou l'autre, au message suivant :

java.lang.ArrayIndexOutOfBoundsException

Ceci signifie qu'une erreur a été rencontrée, car vous avez essayé de lire (ou d'écrire dans) une case qui n'a pas été définie dans votre tableau ! Voici un exemple (nous verrons les exceptions lorsque nous aborderons la programmation orientée objet) :

String[] str = new String[10];
//L'instruction suivante va déclencher une exception
//Car vous essayez d'écrire à la case 11 de votre tableau 
//Alors que celui-ci n'en contient que 10 (ça commence à 0 !)
str[10] = "Une exception";

Nous allons maintenant travailler sur le tableau bidimensionnel mentionné précédemment. Le principe est vraiment identique à celui d'un tableau simple, sauf qu'ici, il y a deux compteurs. Nous allons travailler sur un code permettant d'afficher les données par ligne, c'est-à-dire l'intégralité du sous-tableau de nombres pairs, puis le sous-tableau de nombres impairs.

Avec une boucle while
int premiersNombres[][] = { {0,2,4,6,8},{1,3,5,7,9} }, i = 0, j = 0;
 
while (i < 2)
{
  j = 0;
  while(j < 5)
  {
    System.out.print(premiersNombres[i][j]);
    j++;
  }
  System.out.println("");
  i++;
}

Le résultat se trouve à la figure suivante.

Affichage du tableau
Affichage du tableau

Détaillons un peu ce code :

Le même résultat avec une boucle for
int premiersNombres[][] = { {0,2,4,6,8},{1,3,5,7,9} };
 
for(int i = 0; i < 2; i++)
{    
  for(int j = 0; j < 5; j++)
  {
    System.out.print(premiersNombres[i][j]);       
  }
  System.out.println("");     
}

Je vous avais parlé d'une nouvelle syntaxe pour cette boucle, la voici :

String tab[] = {"toto", "titi", "tutu", "tete", "tata"};
 
for(String str : tab)
  System.out.println(str);

Ceci signifie qu'à chaque tour de boucle, la valeur courante du tableau est mise dans la variable str. Vous constaterez que cette forme de boucle for est particulièrement adaptée aux parcours de tableaux !

Attention cependant, il faut impérativement que la variable passée en premier paramètre de la boucle for soit de même type que la valeur de retour du tableau (une variable de type String pour un tableau de String, un int pour un tableau d'int etc.).

Concernant les tableaux à deux dimensions, que va retourner l'instruction de la première boucle for ? Un tableau ! Nous devrons donc faire une deuxième boucle afin de parcourir ce dernier !

Voici un code qui permet d'afficher un tableau à deux dimensions de façon conventionnelle et selon la version du JDK 1.5 (cette syntaxe ne fonctionnera pas sur les versions antérieures au JDK 1.5) :

String tab[][]={{"toto", "titi", "tutu", "tete", "tata"}, {"1", "2", "3", "4"}};
int i = 0, j = 0;
 
for(String sousTab[] : tab)
{
  i = 0;
  for(String str : sousTab)
  {     
    System.out.println("La valeur de la nouvelle boucle est  : " + str);
    System.out.println("La valeur du tableau à l'indice ["+j+"]["+i+"] est : " + tab[j][i]);
    i++;
  }
  j++;
}

Je vous laisse le soin d'essayer ce code. Vous pourrez voir que nous récupérons un tableau au cours de la première boucle et parcourons ce même tableau afin de récupérer les valeurs de celui-ci dans la deuxième. Simple, non ? En tout cas, je préfère nettement cette syntaxe ! Après, c'est à vous de voir…

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les tableaux multidimensionnels Les méthodes de classe

Les méthodes de classe

Utiliser et rechercher dans un tableau Quelques méthodes utiles

Maintenant que vous commencez à écrire de vrais programmes, vous vous rendez sûrement compte qu'il y a certaines choses que vous effectuez souvent. Plutôt que de recopier sans arrêt les mêmes morceaux de code, vous pouvez écrire une méthode…

Ce chapitre aura pour but de vous faire découvrir la notion de méthode (on l'appelle « fonction » dans d'autres langages). Vous en avez peut-être déjà utilisé une lors du premier TP, vous vous en souvenez ? Vous avez pu voir qu'au lieu de retaper le code permettant d'arrondir un nombre décimal, vous pouviez l'inclure dans une méthode et appeler celle-ci.

Le principal avantage des méthodes est de pouvoir factoriser le code : grâce à elles, vous n'avez qu'un seul endroit où effectuer des modifications lorsqu'elles sont nécessaires. J'espère que vous comprenez mieux l'intérêt de tout cela, car c'est ce que nous allons aborder ici. Cependant, ce chapitre ne serait pas drôle si nous ne nous amusions pas à créer une ou deux méthodes pour le plaisir. Et là, vous aurez beaucoup de choses à retenir !

Quelques méthodes utiles

Les méthodes de classe Créer sa propre méthode

Quelques méthodes utiles

Vous l'aurez compris, il existe énormément de méthodes dans le langage Java, présentes dans des objets comme String : vous devrez les utiliser tout au long de cet ouvrage (et serez même amenés à en modifier le comportement). À ce point du livre, vous pouvez catégoriser les méthodes en deux « familles » : les natives et les vôtres.

Des méthodes concernant les chaînes de caractères

La méthode toLowerCase() permet de transformer tout caractère alphabétique en son équivalent minuscule. Elle n'a aucun effet sur les chiffres : ce ne sont pas des caractères alphabétiques. Vous pouvez donc l'utiliser sans problème sur une chaîne de caractères comportant des nombres.
Elle s'emploie comme ceci :

String chaine = new String("COUCOU TOUT LE MONDE !"), chaine2 = new String();
chaine2 = chaine.toLowerCase();   //Donne "coucou tout le monde !"

la méthode toUpperCase() est simple, puisqu'il s'agit de l'opposé de la précédente. Elle transforme donc une chaîne de caractères en capitales, et s'utilise comme suit :

String chaine = new String("coucou coucou"), chaine2 = new String();
chaine2 = chaine.toUpperCase();   //Donne "COUCOU COUCOU"

La méthode length() renvoie la longueur d'une chaîne de caractères (en comptant les espaces).

String chaine = new String("coucou ! "); 
int longueur = 0;
longueur = chaine.length();   //Renvoie 9

La méthode equals() permet de vérifier (donc de tester) si deux chaînes de caractères sont identiques. C'est avec cette fonction que vous effectuerez vos tests de condition sur les String. Exemple concret :

String str1 = new String("coucou"), str2 = new String("toutou");
 
if (str1.equals(str2))
  System.out.println("Les deux chaînes sont identiques !");
 
else
  System.out.println("Les deux chaînes sont différentes !");

Vous pouvez aussi demander la vérification de l'inégalité grâce à l'opérateur de négation. Vous vous en souvenez ? Il s'agit de « ! ». Cela nous donne :

String str1 = new String("coucou"), str2 = new String("toutou");
 
if (!str1.equals(str2))
  System.out.println("Les deux chaînes sont différentes !");
 
else
  System.out.println("Les deux chaînes sont identiques !");

Ce genre de condition fonctionne de la même façon pour les boucles. Dans l'absolu, cette fonction retourne un booléen, c'est pour cette raison que nous pouvons y recourir dans les tests de condition.

Le résultat de la méthode charAt() sera un caractère : il s'agit d'une méthode d'extraction de caractère. Elle ne peut s'opérer que sur des String ! Par ailleurs, elle présente la même particularité que les tableaux, c'est-à-dire que, pour cette méthode, le premier caractère sera le numéro 0. Cette méthode prend un entier comme argument.

String nbre = new String("1234567");
char carac = nbre.charAt(4);   //Renverra ici le caractère 5

La méthode substring() extrait une partie d'une chaîne de caractères. Elle prend deux entiers en arguments : le premier définit le premier caractère (inclus) de la sous-chaîne à extraire, le second correspond au dernier caractère (exclu) à extraire. Là encore, le premier caractère porte le numéro 0.

String chaine = new String("la paix niche"), chaine2 = new String();
chaine2 = chaine.substring(3,13);   //Permet d'extraire "paix niche"

La méthode indexOf() explore une chaîne de caractères à la recherche d'une suite donnée de caractères, et renvoie la position (ou l'index) de la sous-chaîne passée en argument. la méthode indexOf() explore à partir du début de la chaîne, lastIndexOf() explore en partant de la fin, mais renvoie l'index à partir du début de la chaîne. Ces deux méthodes prennent un caractère ou une chaîne de caractères comme argument, et renvoient un int. Tout comme charAt() et substring(), le premier caractère porte le numéro 0. Je crois qu'ici, un exemple s'impose, plus encore que pour les autres fonctions :

String mot = new String("anticonstitutionnellement");
int n = 0;
 
n = mot.indexOf('t');           //n vaut 2
n = mot.lastIndexOf('t');       //n vaut 24
n = mot.indexOf("ti");          //n vaut 2
n = mot.lastIndexOf("ti");      //n vaut 12
n = mot.indexOf('x');           //n vaut -1

Les méthodes que nous allons voir nécessitent la classe Math, présente dans java.lang. Elle fait donc partie des fondements du langage. Par conséquent, aucun import particulier n'est nécessaire pour utiliser la classe Math qui regorge de méthodes utiles :

double X = 0.0;
X = Math.random();
//Retourne un nombre aléatoire 
//compris entre 0 et 1, comme 0.0001385746329371058

double sin = Math.sin(120);     //La fonction sinus
double cos = Math.cos(120);     //La fonction cosinus
double tan = Math.tan(120);     //La fonction tangente
double abs = Math.abs(-120.25); //La fonction valeur absolue (retourne le nombre sans le signe)
double d = 2;
double exp = Math.pow(d, 2);    //La fonction exposant
//Ici, on initialise la variable exp avec la valeur de d élevée au carré
//La méthode pow() prend donc une valeur en premier paramètre, et un exposant en second

Ces méthodes retournent toutes un nombre de type double.

Je ne vais pas vous faire un récapitulatif de toutes les méthodes présentes dans Java, sinon j'y serai encore dans mille ans… Toutes ces méthodes sont très utiles, croyez-moi. Cependant, les plus utiles sont encore celles que nous écrivons nous-mêmes ! C'est tellement mieux quand cela vient de nous.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les méthodes de classe Créer sa propre méthode

Créer sa propre méthode

Quelques méthodes utiles La surcharge de méthode

Créer sa propre méthode

Voici un exemple de méthode que vous pouvez écrire :

public static double arrondi(double A, int B) {
  return (double) ( (int) (A * Math.pow(10, B) + .5)) / Math.pow(10, B);
}

Décortiquons un peu cela :

Nous verrons dans ce chapitre les différents types de renvoi ainsi que les paramètres que peut accepter une méthode.

Vous devez savoir deux choses concernant les méthodes :

  1. elles ne sont pas limitées en nombre de paramètres;

  2. il en existe trois grands types :

    • les méthodes qui ne renvoient rien. Les méthodes de ce type n'ont pas d'instruction return, et elles sont de type void ;

    • les méthodes qui retournent des types primitifs (double, int etc.). Elles sont de type double, int, char etc. Celles-ci possèdent une instruction return ;

    • les méthodes qui retournent des objets. Par exemple, une méthode qui retourne un objet de type String. Celles-ci aussi comportent une instruction return.

Jusque-là, nous n'avons écrit que des programmes comportant une seule classe, ne disposant elle-même que d'une méthode : la méthode main. Le moment est donc venu de créer vos propres méthodes. Que vous ayez utilisé ou non la méthode arrondi dans votre TP, vous avez dû voir que celle-ci se place à l'extérieur de la méthode main, mais tout de même dans votre classe !

Pour rappel, jetez un œil à la capture d'écran du premier TP, à la figure suivante.

Emplacement des méthodes
Emplacement des méthodes

Puisque nous venons d'étudier les tableaux, nous allons créer des méthodes pour eux. Vous devez certainement vous souvenir de la façon de parcourir un tableau. Et si nous faisions une méthode qui permet d'afficher le contenu d'un tableau sans que nous soyons obligés de retaper la portion de code contenant la boucle ? Je me doute que vous n'en voyez pas l'intérêt maintenant, car exception faite des plus courageux d'entre vous, vous n'avez utilisé qu'un ou deux tableaux dans votre main du chapitre précédent. Si je vous demande de déclarer vingt-deux tableaux et que je vous dis : « Allez, bande de Zéros ! Parcourez-moi tout ça ! », vous n'allez tout de même pas écrire vingt-deux boucles for ! De toute façon, je vous l'interdis. Nous allons écrire une méthode. Celle-ci va :

Avec ce que nous avons défini, nous savons que notre méthode sera de type void et qu'elle prendra un tableau en paramètre. Voici un exemple de code complet :

public class Sdz1
{
  public static void main(String[] args)
  {
    String[] tab = {"toto", "tata", "titi", "tete"};
    parcourirTableau(tab); 
  }
 
  static void parcourirTableau(String[] tabBis)
  {
    for(String str : tabBis)
      System.out.println(str);
  }
}

Bon. Vous voyez que la méthode parcourt le tableau passé en paramètre. Si vous créez plusieurs tableaux et appelez la méthode sur ces derniers, vous vous apercevrez que la méthode affiche le contenu de chaque tableau !

Voici un exemple ayant le même effet que la méthode parcourirTableau, à la différence que celle-ci retourne une valeur : ici, ce sera une chaîne de caractères.

public class Sdz1 {
 
  public static void main(String[] args)
  {
    String[] tab = {"toto", "tata", "titi", "tete"};
    parcourirTableau(tab);
    System.out.println(toString(tab));   
  }

  static void parcourirTableau(String[] tab)
  {
    for(String str : tab)
    System.out.println(str);
  }
           
  static String toString(String[] tab)
  {
    System.out.println("Méthode toString() !\n----------");
    String retour = "";
                   
    for(String str : tab)
      retour += str + "\n"; 
                   
    return retour;
  }         
}

Vous voyez que la deuxième méthode retourne une chaîne de caractères, que nous devons afficher à l'aide de l'instruction System.out.println(). Nous affichons la valeur renvoyée par la méthode toString(). La méthode parcourirTableau, quant à elle, écrit au fur et à mesure le contenu du tableau dans la console. Notez que j'ai ajouté une ligne d'écriture dans la console au sein de la méthode toString(), afin de vous montrer où elle était appelée.

Il nous reste un point important à aborder. Imaginez un instant que vous ayez plusieurs types d'éléments à parcourir : des tableaux à une dimension, d'autres à deux dimensions, et même des objets comme des ArrayList (nous les verrons plus tard, ne vous inquiétez pas). Sans aller aussi loin, vous n'allez pas donner un nom différent à la méthode parcourirTableau pour chaque type primitif !

Vous avez dû remarquer que la méthode que nous avons créée ne prend qu'un tableau de String en paramètre. Pas un tableau d'int ou de long, par exemple. Si seulement nous pouvions utiliser la même méthode pour différents types de tableaux… C'est là qu'entre en jeu ce qu'on appelle la surcharge.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Quelques méthodes utiles La surcharge de méthode

La surcharge de méthode

Créer sa propre méthode Votre première classe

La surcharge de méthode

La surcharge de méthode consiste à garder le nom d'une méthode (donc un type de traitement à faire : pour nous, lister un tableau) et à changer la liste ou le type de ses paramètres. Dans le cas qui nous intéresse, nous voulons que notre méthode parcourirTableau puisse parcourir n'importe quel type de tableau. Nous allons donc surcharger notre méthode afin qu'elle puisse aussi travailler avec des int, comme le montre cet exemple :

static void parcourirTableau(String[] tab)
{
  for(String str : tab)
    System.out.println(str);
}
        
static void parcourirTableau(int[] tab)
{
  for(int str : tab)
    System.out.println(str);
}

Avec ces méthodes, vous pourrez parcourir de la même manière :

Vous pouvez faire de même avec les tableaux à deux dimensions. Voici à quoi pourrait ressembler le code d'une telle méthode (je ne rappelle pas le code des deux méthodes ci-dessus) :

static void parcourirTableau(String[][] tab)
{
  for(String tab2[] : tab)
  {
    for(String str : tab2)
      System.out.println(str);
  }
}

La surcharge de méthode fonctionne également en ajoutant des paramètres à la méthode. Cette méthode est donc valide :

static void parcourirTableau(String[][] tab, int i)
{
  for(String tab2[] : tab)
  {
    for(String str : tab2)
      System.out.println(str);
  }
}

En fait, c'est la JVM qui va se charger d'invoquer l'une ou l'autre méthode : vous pouvez donc créer des méthodes ayant le même nom, mais avec des paramètres différents, en nombre ou en type. La machine virtuelle fait le reste. Ainsi, si vous avez bien défini toutes les méthodes ci-dessus, ce code fonctionne :

String[] tabStr = {"toto", "titi", "tata"};
int[] tabInt = {1, 2, 3, 4};
String[][] tabStr2 = {{"1", "2", "3", "4"}, {"toto", "titi", "tata"}};

//La méthode avec un tableau de String sera invoquée
parcourirTableau(tabStr);
//La méthode avec un tableau d'int sera invoquée
parcourirTableau(tabInt);
//La méthode avec un tableau de String à deux dimensions sera invoquée
parcourirTableau(tabStr2);

Vous venez de créer une méthode qui vous permet de centraliser votre code afin de ne pas avoir à retaper sans arrêt les mêmes instructions. Dans la partie suivante, vous apprendrez à créer vos propres objets. Elle sera très riche en informations, mais ne vous inquiétez pas : nous apprendrons tout à partir de zéro. ;)

En résumé

J'ose espérer que vous avez apprécié ce tuto sur les bases du langage Java ! En tout cas, je me suis bien amusé en le faisant.

Maintenant, nous allons rentrer dans les méandres de la programmation orientée objet !
Alors ?... Toujours prêts ? :p

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Créer sa propre méthode Votre première classe

Votre première classe

La surcharge de méthode Structure de base

Dans la première partie de cet ouvrage sur la programmation en Java, nous avons travaillé avec une seule classe. Vous allez apprendre qu'en faisant de la programmation orientée objet, nous travaillerons en fait avec de nombreuses classes. Rappelez-vous la première partie : vous avez déjà utilisé des objets… Oui ! Lorsque vous faisiez ceci : String str = new String("tiens… un objet String");.

Ici str est un objet String. Vous avez utilisé un objet de la classe String : on dit que vous avez créé une instance de la classeString(). Le moment est venu pour vous de créer vos propres classes.

Structure de base

Votre première classe Les constructeurs

Structure de base

Une classe peut être comparée à un moule qui, lorsque nous le remplissons, nous donne un objet ayant la forme du moule ainsi que toutes ses caractéristiques. Comme quand vous étiez enfants, lorsque vous vous amusiez avec de la pâte à modeler.

Si vous avez bien suivi la première partie de ce cours, vous devriez savoir que notre classe contenant la méthode main ressemble à ceci :

class ClasseMain{

  public static void main(String[] args){ 
    //Vos données, variables, différents traitements…
  }//Fin de la méthode main

}//Fin de votre classe

Créez cette classe et cette méthode main (vous savez le faire, maintenant). Puisque nous allons faire de la POO (Programmation Orientée Objet), nous allons créer une seconde classe dans ce fameux projet ! Créons sans plus tarder une classe Ville. Allez dans File > New > Class ou utilisez le raccourci dans la barre d'outils, comme sur la figure suivante.

Création d'une nouvelle classe Java dans Eclipse
Création d'une nouvelle classe Java dans Eclipse

Nommez votre classe « Ville » (avec un « V » majuscule, convention de nommage oblige). Cette fois, vous ne devez pas y créer la méthode main.

Au final, vous devriez avoir le rendu de la figure suivante.

Classe Ville
Classe Ville

Ici, notre classe Ville est précédée du mot clé public. Vous devez savoir que lorsque nous créons une classe comme nous l'avons fait, Eclipse nous facilite la tâche en ajoutant automatiquement ce mot clé, qui correspond à la portée de la classe. Retenez pour l'instant que public class UneClasse{} et class UneClasse{} sont presque équivalents !

En programmation, la portée détermine qui peut faire appel à une classe, une méthode ou une variable. Vous avez déjà rencontré la portée public : cela signifie que tout le monde peut faire appel à l'élément. Ici dans le cas qui nous intéresse il s'agit d'une méthode. Une méthode marquée comme public peut donc être appelée depuis n'importe quel endroit du programme.

Nous allons ici utiliser une autre portée : private. Elle signifie que notre méthode ne pourra être appelée que depuis l'intérieur de la classe dans laquelle elle se trouve ! Les méthodes déclarées private correspondent souvent à des mécanismes internes à une classe que les développeurs souhaitent « cacher » ou simplement ne pas rendre accessibles de l'extérieur de la classe…

Bon. Toutes les conditions sont réunies pour commencer activement la programmation orientée objet ! Et si nous allions créer notre première ville ?

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Votre première classe Les constructeurs

Les constructeurs

Structure de base Accesseurs et mutateurs

Les constructeurs

Vu que notre objectif dans ce chapitre est de construire un objet Ville, il va falloir définir les données qu'on va lui attribuer. Nous dirons qu'un objet Ville possède :

Nous allons faire ceci en mettant des variables d'instance (de simples variables identiques à celles que vous manipulez habituellement) dans notre classe. Celle-ci va contenir une variable dont le rôle sera de stocker le nom, une autre stockera le nombre d'habitants et la dernière se chargera du pays ! Voici à quoi ressemble notre classe Ville à présent :

public class Ville{
  String nomVille;
  String nomPays;
  int nbreHabitants; 
}

Contrairement aux classes, les variables d'instance présentes dans une classe sont public si vous ne leur spécifiez pas de portée. Alors, on parle de variable d'instance, parce que dans nos futures classes Java qui définiront des objets, il y aura plusieurs types de variables (nous approfondirons ceci dans ce chapitre). Pour le moment, sachez qu'il y a trois grands types de variables dans une classe objet :

  1. Les variables d'instance : ce sont elles qui définiront les caractéristiques de notre objet.

  2. Les variables de classe : celles-ci sont communes à toutes les instances de votre classe.

  3. Les variables locales : ce sont des variables que nous utiliserons pour travailler dans notre objet.

Dans l'immédiat, nous allons travailler avec des variables d'instance afin de créer des objets différents. Il ne nous reste plus qu'à créer notre premier objet, pour ce faire, nous allons devoir utiliser ce qu'on appelle des constructeurs.

Un constructeur est une méthode d'instance qui va se charger de créer un objet et, le cas échéant, d'initialiser ses variables de classe ! Cette méthode a pour rôle de signaler à la JVM (Java Virtual Machine) qu'il faut réserver de la mémoire pour notre futur objet et donc, par extension, d'en réserver pour toutes ses variables.

Notre premier constructeur sera ce qu'on appelle communément un constructeur par défaut, c'est-à-dire qu'il ne prendra aucun paramètre, mais permettra tout de même d'instancier un objet, et vu que nous sommes perfectionnistes, nous allons y initialiser nos variables d'instance. Voici votre premier constructeur :

public class Ville{   
  //Stocke le nom de notre ville
  String nomVille;
  //Stocke le nom du pays de notre ville
  String nomPays;
  //Stocke le nombre d'habitants de notre ville
  int nbreHabitants;
     
  //Constructeur par défaut
  public Ville(){
    System.out.println("Création d'une ville !");      
    nomVille = "Inconnu";
    nomPays = "Inconnu";
    nbreHabitants = 0;
  } 
}

Vous avez remarqué que le constructeur est en fait une méthode qui n'a aucun type de retour (void, double…) et qui porte le même nom que notre classe ! Ceci est une règle immuable : le (les) constructeur(s) d'une classe doit (doivent) porter le même nom que la classe !

Son corollaire est qu'un objet peut avoir plusieurs constructeurs. Il s'agit de la même méthode, mais surchargée ! Dans notre premier constructeur, nous n'avons passé aucun paramètre, mais nous allons bientôt en mettre.

Vous pouvez d'ores et déjà créer une instance de Ville. Cependant, commencez par vous rappeler qu'une instance d'objet se fait grâce au mot clé new, comme lorsque vous créez une variable de type String.

Maintenant, vu que nous allons créer des objets Ville, nous allons procéder comme avec les String. Vérifions que l'instanciation s’effectue comme il faut. Allons dans notre classe contenant la méthode main et instancions un objet Ville. Je suppose que vous avez deviné que le type de notre objet sera Ville !

public class Sdz1{ 
  public static void main(String[] args){   
    Ville ville = new Ville(); 
  } 
}

Exécutez ce code, vous devriez avoir l'équivalent de la figure suivante sous les yeux.

Création d'un objet Ville
Création d'un objet Ville

Maintenant, nous devons mettre des données dans notre objet, ceci afin de pouvoir commencer à travailler… Le but sera de parvenir à une déclaration d'objet se faisant comme ceci :

Ville ville1 = new Ville("Marseille", 123456789, "France");

Vous avez remarqué qu'ici, les paramètres sont renseignés : eh bien il suffit de créer une méthode qui récupère ces paramètres et initialise les variables de notre objet, ce qui achèvera notre constructeur d'initialisation.

Voici le constructeur de notre objet Ville, celui qui permet d'avoir des objets avec des paramètres différents :

public class Ville {
 
  //Stocke le nom de notre ville
  String nomVille;
  //Stocke le nom du pays de notre ville
  String nomPays;
  //Stocke le nombre d'habitants de notre ville
  int nbreHabitants;
 
  //Constructeur par défaut
  public Ville(){
    System.out.println("Création d'une ville !");          
    nomVille = "Inconnu";
    nomPays = "Inconnu";
    nbreHabitants = 0;
  }
 
  //Constructeur avec paramètres
  //J'ai ajouté un « p » en première lettre des paramètres.
  //Ce n'est pas une convention, mais ça peut être un bon moyen de les repérer.
  public Ville(String pNom, int pNbre, String pPays)
  {
    System.out.println("Création d'une ville avec des paramètres !");
    nomVille = pNom;
    nomPays = pPays;
    nbreHabitants = pNbre;
  }        
}

Dans ce cas, l'exemple de déclaration et d'initialisation d'un objet Ville que je vous ai montré un peu plus haut fonctionne sans aucun souci ! Mais il vous faudra respecter scrupuleusement l'ordre des paramètres passés lors de l'initialisation de votre objet : sinon, c'est l'erreur de compilation à coup sûr ! Ainsi :

//L'ordre est respecté -> aucun souci
Ville ville1 = new Ville("Marseille", 123456789, "France");
//Erreur dans l'ordre des paramètres -> erreur de compilation au final
Ville ville2 = new Ville(12456, "France", "Lille");

Par contre, notre objet présente un gros défaut : les variables d'instance qui le caractérisent sont accessibles dans votre classe contenant votre main ! Ceci implique que vous pouvez directement modifier les attributs de la classe. Testez ce code et vous verrez que le résultat est identique à la figure suivante :

public class Sdz1 {
 
  public static void main(String[] args)
  {
    Ville ville =  new Ville();
    System.out.println(ville.nomVille);
    ville.nomVille = "la tête à toto ! ! ! !";
    System.out.println(ville.nomVille);
                
    Ville ville2 = new Ville("Marseille", 123456789, "France");
    ville2.nomPays = "La tête à tutu ! ! ! ! ";
    System.out.println(ville2.nomPays);
  }
          
}
Modification des données de notre objet
Modification des données de notre objet

Vous constatez que nous pouvons accéder aux variables d'instance en utilisant le « . », comme lorsque vous appelez la méthode subString() de l'objet String. C'est très risqué, et la plupart des programmeurs Java vous le diront. Dans la majorité des cas, nous allons contrôler les modifications des variables de classe, de manière à ce qu'un code extérieur ne fasse pas n'importe quoi avec nos objets ! En plus de ça, imaginez que vous souhaitiez faire quelque chose à chaque fois qu'une valeur change ; si vous ne protégez pas vos données, ce sera impossible à réaliser… C'est pour cela que nous protégeons nos variables d'instance en les déclarant private, comme ceci :

public class Ville {

  private String nomVille;
  private String nomPays;
  private int nbreHabitants;
   
  //…     
}

Désormais, ces attributs ne sont plus accessibles en dehors de la classe où ils sont déclarés ! Nous allons maintenant voir comment accéder tout de même à nos données.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Structure de base Accesseurs et mutateurs

Accesseurs et mutateurs

Les constructeurs Les variables de classes

Accesseurs et mutateurs

Un accesseur est une méthode qui va nous permettre d'accéder aux variables de nos objets en lecture, et un mutateur nous permettra d'en faire de même en écriture ! Grâce aux accesseurs, vous pourrez afficher les variables de vos objets, et grâce aux mutateurs, vous pourrez les modifier :

public class Ville {

  //Les variables et les constructeurs n'ont pas changé…
          
  //*************   ACCESSEURS *************
    
  //Retourne le nom de la ville
  public String getNom()  {  
    return nomVille;
  }

  //Retourne le nom du pays
  public String getNomPays()
  {
    return nomPays;
  }

  // Retourne le nombre d'habitants
  public int getNombreHabitants()
  {
    return nbreHabitants;
  } 
 
  //*************   MUTATEURS   *************

  //Définit le nom de la ville
  public void setNom(String pNom)
  {
    nomVille = pNom;
  }

  //Définit le nom du pays
  public void setNomPays(String pPays)
  {
    nomPays = pPays;
  }

  //Définit le nombre d'habitants
  public void setNombreHabitants(int nbre)
  {
    nbreHabitants = nbre;
  }  
}

Nos accesseurs sont bien des méthodes, et elles sont public pour que vous puissiez y accéder depuis une autre classe que celle-ci : depuis le main, par exemple. Les accesseurs sont du même type que la variable qu'ils doivent retourner. Les mutateurs sont, par contre, de type void. Ce mot clé signifie « rien » ; en effet, ces méthodes ne retournent aucune valeur, elles se contentent de les mettre à jour.

À présent, essayez ce code dans votre méthode main :

Ville v = new Ville();
Ville v1 = new Ville("Marseille", 123456, "France");       
Ville v2 = new Ville("Rio", 321654, "Brésil");

System.out.println("\n v = "+v.getNom()+" ville de  "+v.getNombreHabitants()+ " habitants se situant en "+v.getNomPays());
System.out.println(" v1 = "+v1.getNom()+" ville de  "+v1.getNombreHabitants()+ " habitants se situant en "+v1.getNomPays());
System.out.println(" v2 = "+v2.getNom()+" ville de  "+v2.getNombreHabitants()+ " habitants se situant en "+v2.getNomPays()+"\n\n");
        
/*
  Nous allons interchanger les Villes v1 et v2
  tout ça par l'intermédiaire d'un autre objet Ville.        
*/       
Ville temp = new Ville();
temp = v1;
v1 = v2;
v2 = temp;
       
System.out.println(" v1 = "+v1.getNom()+" ville de  "+v1.getNombreHabitants()+ " habitants se situant en "+v1.getNomPays());
System.out.println(" v2 = "+v2.getNom()+" ville de  "+v2.getNombreHabitants()+ " habitants se situant en "+v2.getNomPays()+"\n\n");

/*       
  Nous allons maintenant interchanger leurs noms
  cette fois par le biais de leurs mutateurs.
*/   
v1.setNom("Hong Kong");
v2.setNom("Djibouti");
      
System.out.println(" v1 = "+v1.getNom()+" ville de  "+v1.getNombreHabitants()+ " habitants se situant en "+v1.getNomPays());
System.out.println(" v2 = "+v2.getNom()+" ville de  "+v2.getNombreHabitants()+ " habitants se situant en "+v2.getNomPays()+"\n\n");

À la compilation, vous devriez obtenir la figure suivante.

Essai des accesseurs
Essai des accesseurs

Vous voyez bien que les constructeurs ont fonctionné, que les accesseurs tournent à merveille et que vous pouvez commencer à travailler avec vos objets Ville. Par contre, pour afficher le contenu, on pourrait faire plus simple, comme par exemple créer une méthode qui se chargerait de faire tout ceci… Je sais ce que vous vous dites : « Mais les accesseurs, ce ne sont pas des méthodes ? ». Bien sûr que si, mais il vaut mieux bien distinguer les différents types de méthodes dans un objet :

Avec nos objets Ville, notre choix est un peu limité par le nombre de méthodes possibles, mais nous pouvons tout de même en faire une ou deux pour l'exemple :

Par contre, un problème va se poser ! Vous savez déjà qu'en Java, on appelle les méthodes d'un objet comme ceci : monString.subString(0,4);. Cependant, vu qu'il va falloir qu'on travaille depuis l'intérieur de notre objet, vous allez encore avoir un mot clé à retenir… Cette fois, il s'agit du mot clé this. Voici tout d'abord le code de notre classe Ville en entier, c'est-à-dire comportant les méthodes dont on vient de parler :

public class Ville {
 
  private String nomVille;
  private String nomPays;
  private int nbreHabitants;
  private char categorie;
   
  public Ville(){
    System.out.println("Création d'une ville !");          
    nomVille = "Inconnu";
    nomPays = "Inconnu";
    nbreHabitants = 0;
    this.setCategorie();
  }
 
  public Ville(String pNom, int pNbre, String pPays)
  {
    System.out.println("Création d'une ville avec des paramètres !");
    nomVille = pNom;
    nomPays = pPays;
    nbreHabitants = pNbre;
    this.setCategorie();
  }  
    
  //Retourne le nom de la ville
  public String getNom()  {  
    return nomVille;
  }

  //Retourne le nom du pays
  public String getNomPays()
  {
    return nomPays;
  }

  // Retourne le nombre d'habitants
  public int getNombreHabitants()
  {
    return nbreHabitants;
  } 

  //Retourne la catégorie de la ville
  public char getCategorie()
  {
    return categorie;
  } 
 
  //Définit le nom de la ville
  public void setNom(String pNom)
  {
    nomVille = pNom;
  }

  //Définit le nom du pays
  public void setNomPays(String pPays)
  {
    nomPays = pPays;
  }

  //Définit le nombre d'habitants
  public void setNombreHabitants(int nbre)
  {
    nbreHabitants = nbre;
    this.setCategorie();
  }  
 
  //Définit la catégorie de la ville
  private void setCategorie() {
 
    int bornesSuperieures[] = {0, 1000, 10000, 100000, 500000, 1000000, 5000000, 10000000};
    char categories[] = {'?', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'};

    int i = 0;
    while (i < bornesSuperieures.length && this.nbreHabitants >= bornesSuperieures[i])
      i++;

    this.categorie = categories[i];
  }

  //Retourne la description de la ville
  public String decrisToi(){
    return "\t"+this.nomVille+" est une ville de "+this.nomPays+ ", elle comporte : "+this.nbreHabitants+" habitant(s) => elle est donc de catégorie : "+this.categorie;
  }

  //Retourne une chaîne de caractères selon le résultat de la comparaison
  public String comparer(Ville v1){
    String str = new String();

    if (v1.getNombreHabitants() > this.nbreHabitants)
      str = v1.getNom()+" est une ville plus peuplée que "+this.nomVille;
     
    else
      str = this.nomVille+" est une ville plus peuplée que "+v1.getNom();
     
    return str;
  }
}

Pour expliciter le fonctionnement du mot clé this, prenons l'exemple de la méthode comparer(Ville V1). La méthode va s'utiliser comme suit :

Ville V = new Ville("Lyon", 654, "France");
Ville V2 = new Ville("Lille", 123, "France");
 
V.comparer(V2);

Dans cette méthode, nous voulons comparer le nombre d'habitants de chacun des deux objets Ville. Pour accéder à la variable nbreHabitants de l'objet V2, il suffit d'utiliser la syntaxe V2.getNombreHabitants() ; nous ferons donc référence à la propriété nbreHabitants de l'objet V2.
Mais l'objet V, lui, est l'objet appelant de cette méthode. Pour se servir de ses propres variables, on utilise alors this.nbreHabitants, ce qui a pour effet de faire appel à la variable nbreHabitants de l'objet exécutant la méthode comparer(Ville V).

Explicitons un peu les trois méthodes qui ont été décrites précédemment.

La méthode categorie()

Elle ne prend aucun paramètre, et ne renvoie rien : elle se contente de mettre la variable de classe categorie à jour. Elle détermine dans quelle tranche se trouve la ville grâce au nombre d'habitants de l'objet appelant, obtenu au moyen du mot clé this. Selon le nombre d'habitants, le caractère renvoyé changera. Nous l'appelons lorsque nous construisons un objet Ville (que ce soit avec ou sans paramètre), mais aussi lorsque nous redéfinissons le nombre d'habitants : de cette manière, la catégorie est automatiquement mise à jour, sans qu'on ait besoin de faire appel à la méthode.

La méthode decrisToi()

Celle-ci nous renvoie un objet de type String. Elle fait référence aux variables qui composent l'objet appelant la méthode, toujours grâce à this, et nous renvoie donc une chaîne de caractères qui nous décrit l'objet en énumérant ses composants.

La méthode comparer(Ville V1)

Elle prend une ville en paramètre, pour pouvoir comparer les variables nbreHabitants de l'objet appelant la méthode et de celui passé en paramètre pour nous dire quelle ville est la plus peuplée ! Et si nous faisions un petit test ?