Académique Documents
Professionnel Documents
Culture Documents
AU
LANGAGE JAVA
LICENCE 1, INFORMATIQUE, RESEAUX ET
TELECOMMUNICATIONS
1
Chapitre 0
Présentation de Java
Après un très bref historique du langage Java montrant dans quel esprit il a été créé, nous en
présenterons les principales caractéristiques. Nous verrons tout d’abord que Java est un langage objet
et nous exposerons les concepts majeurs de la programmation orientée objet. Puis nous ferons la
distinction entre les programmes utilisant une interface console et les programmes utilisant une
interface graphique, ce qui nous amènera à parler des possibilités de programmation événementielle
qui sont offertes par Java sous la forme de classes standard. Enfin, nous montrerons que Java est le
premier langage à offrir une portabilité aussi avancée.
2
ses bibliothèques de classes indépendantes de la plate-forme, ce qui est le point essentiel de la
programmation sur internet ou plusieurs machines dissemblables sont interconnectées.
La réalisation multi-plateformes dépend en fait du système d'exploitation et de sa capacité à posséder
des outils de compilation et d'interprétation de la machine virtuelle Java.
En Java l'on développe deux genres de programmes :
● Les applications qui sont des logiciels classiques s'exécutant directement sur une plate-
forme spécifique soit à travers une machine virtuelle java soit directement en code
exécutable par le système d'exploitation. (code natif).
● Les applets sont des programmes java insérés dans un document HTML s'exécutant à
travers la machine virtuelle java du navigateur lisant le document HTML.
● Des applications pour appareils mobiles, avec J2ME ;
● et bien d'autres ! Java EE, JMF, J3D pour la 3D…
3
L’encapsulation des données présente un intérêt manifeste en matière de qualité de logiciel. Elle facilite
considérablement la maintenance : une modification éventuelle de la structure des données d’un objet n’a
d’incidence que sur l’objet lui-même ; les utilisateurs de l’objet ne seront pas concernés par la teneur de cette
modification (ce qui n’était bien sûr pas le cas avec la programmation structurée). De la même manière,
l’encapsulation des données facilite grandement la réutilisation d’un objet.
2.2. Le concept de classe
Le concept de classe correspond simplement à la généralisation de la notion de type que l’on
rencontre dans les langages classiques. En effet, une classe n’est rien d’autre que la descrip- tion d’un
ensemble d’objets ayant une structure de données commune et disposant des mêmes méthodes. Les
objets apparaissent alors comme des variables d’un tel type classe (en P.O.O., on dit aussi qu’un objet
est une instance de sa classe). Bien entendu, seule la structure est commune, les valeurs des champs
étant propres à chaque objet. En revanche, les métho- des sont effectivement communes à l’ensemble
des objets d’une même classe.
Lorsque, comme cela arrive parfois dans l’écriture d’interfaces graphiques, on est amené à ne créer
qu’un seul objet d’une classe donnée, la distinction entre les notions d’objet et de classe n’est pas
toujours très évidente.
En revanche, lorsque l’on dispose de plusieurs objets d’une même classe, le principe d’encapsulation
s’appliquera à la classe et non à chacune de ses instances, comme nous le verrons.
2.3. L’héritage
Un autre concept important en P.O.O. est celui d’héritage. Il permet de définir une nouvelle classe
à partir d’une classe existante (qu’on réutilise en bloc !), à laquelle on ajoute de nouvelles données et
de nouvelles méthodes. La conception de la nouvelle classe, qui hérite des propriétés et des aptitudes
de l’ancienne, peut ainsi s’appuyer sur des réalisations antérieures parfaitement au point et les
spécialiser à volonté. Comme on peut s’en douter, l’héritage facilite largement la réutilisation de
produits existants, d’autant plus qu’il peut être réitéré autant de fois que nécessaire (la classe C peut
hériter de B, qui elle-même hérite de A).
2.4. Le polymorphisme
En Java, comme généralement, en P.O.O., une classe peut "redéfinir" (c’est-à-dire modifier) certaines
des méthodes héritées de sa classe de base. Cette possibilité est la clé de ce que l’on nomme le
"polymorphisme", c’est-à-dire la possibilité de traiter de la même manière des objets de types
différents, pour peu qu’ils soient issus de classes dérivées d’une même classe de base. Plus
précisément, on utilise chaque objet comme s’il était de cette classe de base, mais son comportement
effectif dépend de sa classe effective (dérivée de cette classe de base), en particulier de la manière dont
ses propres méthodes ont été redéfinies. Le polymorphisme permet d’ajouter de nouveaux objets
dans un scénario préétabli et, éventuellement, écrit avant d’avoir connaissance du type exact de ces
objets.
2.5. Java est Presque un pur langage de POO
4
Certains langages ont été conçus pour appliquer à la lettre les principes de P.O.O. C’est notamment
le cas de Simula, Smalltalk et de Eiffel. Dans ce cas, tout est objet (ou instance de classe) et
l’encapsulation des données est absolue. Les procédures sont obligatoirement des méthodes, ce qui
revient à dire qu’il n’existe pas de procédures indépendantes, c’est-à-dire susceptibles de s’exécuter
indépendamment de tout objet.
D’autres langages, comme Pascal ou C++, ont cherché à appliquer une "philosophie objet" à un
langage classique. Les objets y cohabitent alors avec des variables usuelles. Il existe à la fois des
méthodes, applicables à un objet, et des procédures indépendantes. À la limite, on peut réaliser un
programme ne comportant aucun objet.
Java se veut un langage de la première catégorie, autrement dit un pur langage de P.O.O. Par nature,
un programme s’y trouvera formé d’une classe ou de la réunion de plusieurs classes et il instanciera
des objets. Il sera impossible de créer un programme n’utilisant aucune classe. Cependant, il faut
apporter quelques nuances qui troublent très légèrement la pureté du langage.
● Java dispose de types dits primitifs pour représenter les entiers, les flottants, les
caractères et les booléens. Les variables correspondantes ne sont pas des objets.
Certes, la plupart du temps, ces types primitifs seront utilisés pour définir les champs
d’une classe, donc finalement d’un objet ; cependant, il y aura des exceptions...
● Une classe pourra comporter des méthodes particulières dites méthodes de classe
(déclarées avec le mot-clé static) qui seront utilisables de façon indépendante d’un
objet. Comme ces méthodes peuvent déclarer localement des variables d’un type
primitif, on voit qu’on peut ainsi retrouver les possibilités des procédures ou des
fonctions des langages non objets. La seule différence (purement syntaxique) viendra
de ce que ces méthodes seront localisées artificiellement dans une classe (on verra
qu’il existe une telle méthode nommée main jouant le rôle de programme principal).
À la limite, on peut concevoir un programme ne comportant aucun objet (mais
obligatoirement au moins une classe). C’est d’ailleurs cette particularité que nous
exploiterons pour vous exposer les bases du langage, en dehors de tout contexte
objet.
● L’encapsulation se trouve naturellement induite par la syntaxe du langage mais elle
n’est pas absolue.
3. Java et la portabilité
Dans la plupart des langages, on dit qu’un programme est portable car un même code source peut
être exploité dans des environnements différents moyennant simplement une nouvelle compilation.
En Java, la portabilité va plus loin. En effet, comme nous l’avons évoqué précédemment, la
compilation d’un code source produit, non pas des instructions machine, mais un code
intermédiaire formé de byte codes. D’une part, ce code est exactement le même, quel que soit le
compilateur et l’environnement concernés. D’autre part, ces byte codes sont exécutables dans toute
5
implémentation disposant du logiciel d’interprétation nommé machine virtuelle ou, parfois, système
d’exécution Java.
De surcroît, Java définit exactement les caractéristiques des types primitifs servant à représenter les
caractères, les entiers et les flottants. Cela concerne non seulement la taille de l’emplacement
mémoire, mais aussi le comportement arithmétique correspondant. Ainsi, quelle que soit la
machine, une valeur de type float (réel) aura exactement la même taille, les mêmes limites et la même
précision. Java est ainsi le premier langage qui assure qu’un même programme, exécuté dans des
environnements différents, fournira les mêmes résultats.
6
Chapitre 1
Bases de Java
Ce chapitre constitue une première approche d’un programme Java, fondée sur quelques exemples
commentés. Vous y verrez, de manière informelle pour l’instant, comment configurer un
environnement pour faire du Java, comment s’expriment les instructions de base (déclaration,
affectation, écriture...). Cela nous permettra par la suite d’illustrer certaines notions par des
programmes complets, compréhensibles avant même que nous n’ayons effectué une étude détaillée
des instructions correspondantes.
Nous dégagerons ensuite quelques règles générales concernant l’écriture d’un programme. Enfin,
nous montrerons comment mettre en œuvre un programme Java, de sa saisie à son exécution, ce qui
vous permettra de vous familiariser avec votre propre environnement de développement.
Notez que nous exploiterons ici les possibilités de simplification présentées au chapitre précédent.
D’une part, nous nous limiterons à des programmes utilisant une interface de type console ; d’autre
part, nous ne ferons pas intervenir d’objets. Autrement dit, ce chapitre se bornera à vous montrer
comment s’expriment en Java des concepts que vous avez déjà rencontrés dans d’autres langages (C,
C++, Visual Basic, C#, PHP...).
● J2SE (Java 2 Standard Edition, celui qui nous intéresse dans cet ouvrage) : permet de développer des
applications dites « client lourd », par exemple Word, Excel, la suite OpenOffice.org… Toutes ces
applications sont des « clients lourds » . C'est ce que nous allons faire dans ce cours
● Java EE (Java Enterprise Edition) : permet de développer des applications web en Java. On parle aussi
de clients légers.
● J2ME (Java 2 Micro Edition) : permet de développer des applications pour appareils portables,
comme des téléphones portables, des PDA…
1.2 Les Environnements de développement intégrés (IDE)
7
❖ Eclispe IDE
« 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.
Eclipse IDE est lui-même principalement écrit en Java.
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 :
❖ Netbeans
❖ Intellij IDEA
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 logique de notre
programme. Une méthode contient :
8
● un corps : le contenu de la méthode, délimité par des accolades ;
● une valeur de retour : le résultat que la méthode va retourner.
Hello World
● System : ceci correspond à l'appel d'une classe qui se nomme « System » . C'est une classe
utilitaire qui permet surtout d'utiliser l'entrée et la sortie standard, c'est-à-dire la saisie clavier
et l'affichage à l'écran.
● out : objet de la classe System qui gère la sortie standard.
● print : méthode qui écrit dans la console le texte passé en paramètre (entre les parenthèses).
Prenons le code suivant :
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 :
9
Vous souhaitez insérer un retour à la ligne pour que votre texte soit plus lisible… Pour cela, vous
avez plusieurs solutions :
1 Hello World !
2 Je suis
3
4 Titi
● lorsque vous utilisez le caractère d'échappement \n, quelle que soit la méthode appelée, celle-
ci ajoute immédiatement un retour à la ligne à son emplacement ;
● lorsque vous utilisez la méthode println(), celle-ci ajoute automatiquement un retour à la
ligne à la fin de la chaîne passée en paramètre ;
● un caractère d'échappement peut être mis dans la méthode println().
Deux autres caractères d’échappement:
1. \r va insérer un retour chariot, parfois utilisé aussi pour les retours à la ligne ;
2. \t va faire une tabulation.
10
Figure 1: Exemple de Programme en Java
Bien entendu, nous avons utilisé le même canevas que précédemment avec un autre nom de classe (ici
Exemple) :
Les deux premières instructions de notre fonction main sont des déclarations classiques :
int n;
double x ;
La première précise que la variable n est de type int, c’est-à-dire qu’elle est destinée à contenir des
nombres entiers (relatifs). Comme la plupart des langages, Java dispose de plusieurs types entiers. De
la même manière, la seconde instruction précise que x est une variable de type double, c’est-à-dire
destinée à contenir des nombres flottants en "double précision" (approximation de nombres réels).
Nous verrons que Java dispose de deux types de flottants, le second se nommant float (nous ne
l’avons pas utilisé ici car il aurait fait intervenir des pro- blèmes de conversion des constantes
flottantes).
11
Comme dans la plupart des langages modernes, les déclarations sont obligatoires en Java. Cependant,
il n’est pas nécessaire qu’elles soient regroupées en début de programme (comme cela est le cas en C
ou en Pascal) ; il suffit simplement qu’une variable ait été déclarée avant d’être utilisée.
Les instructions suivantes sont des affectations classiques :
n=5;
x=2*n+1.5;
Les deux instructions suivantes font appel à la fonction System.out.println déjà entrevue au
paragraphe 2 :
System.out.println ("n = " + n) ;
System.out.println ("x = " + x) ;
Mais cette fois, vous constatez que son argument ne se limite plus à une simple constante chaîne. En
Java, l’expression "n = " + n est interprétée comme la concaténation de la chaîne constante "n = "
avec le résultat de la conversion en chaîne de la valeur de la variable n. Une telle conversion fournit
en fait la suite de caractères correspondant à l’écriture du nombre en décimal.
La même remarque s’applique à l’expression "x = " + x. Nous verrons que l’opérateur + pos- sède une
propriété intéressante : dès que l’un de ses deux opérandes est de type chaîne, l’autre est converti en
chaîne.
La suite du programme est classique. On y note simplement une déclaration (tardive) de la variable
y. Elle est autorisée à ce niveau car y n’a pas été utilisée dans les instructions précédentes.
Remarques :
Avec une instruction telle que :
System.out.println ("resultats = ", a + b*x) ;
on affichera à la suite du texte "resultats = ", la valeur de a suivie de celle de b*x. Pour obtenir celle
de l’expression a + b*x, il faudra procéder ainsi :
System.out.println ("resultats = ", (a + b*x) ) ;
Ce paragraphe expose un certain nombre de règles générales intervenant dans l’écriture d’un
programme Java. Nous aborderons en particulier ce qu’on nomme les identificateurs et les mots-clés,
le format libre dans lequel sont écrites les instructions, l’usage des séparateurs et des commentaires.
4.1. Les identificateurs
En Java comme dans la plupart des autres langages, un identificateur est formé de lettres ou de chiffres, le
premier caractère étant obligatoirement une lettre. Les lettres comprennent les majuscules A-Z et les
minuscules a-z, ainsi que le caractère "souligné" (_) et le caractère $. Voici quelques identificateurs corrects :
ligne clavier valeur_5 _total_56
Aucune limitation ne pèse sur le nombre de caractères, qui sont tous significatifs (en C, seuls les 32 premiers
l’étaient).
12
Compléments
Notez bien que, comme en C (et contrairement à Pascal), on distingue les majuscules des minuscules.
Ainsi, Ligne et ligne désignent deux identificateurs différents ; il en va de même pour PremProg et
Premprog.
Bien qu’elles ne soient nullement imposées par le langage, certaines règles sont traditionnellement
utilisées dans le choix des identificateurs d’un programme Java. Ainsi, les noms de variables et les
noms de fonctions sont écrits en minuscules, sauf s’ils sont formés de la juxtaposition de plusieurs
mots, auquel cas chaque mot sauf le premier comporte une majuscule, par exemple: valeur,
nombreValeurs, tauxEmprunt, nombreReponsesExactes. Les noms de classes suivent la même règle,
mais leur première lettre est écrite en majuscules : Clavier, PremProg. Les noms de constantes sont
écrits entièrement en majuscules. Notez que ces règles permettent de savoir que System est une classe
et que out n’en est pas une.
4.2. Les mots-clés
Certains mots-clés sont réservés par le langage à un usage bien défini et ne peuvent pas être utilisés
comme identificateurs. En voici la liste, par ordre alphabétique :
Dans notre langue écrite, les mots sont séparés par un espace, un signe de ponctuation ou une fin de
ligne.
Il en va quasiment de même en Java. Ainsi, dans un programme, deux identificateurs successifs entre
lesquels la syntaxe n’impose aucun signe particulier doivent impérativement être séparés soit par un
espace, soit par une fin de ligne. En revanche, dès que la syntaxe impose un séparateur quelconque,
13
il n’est pas nécessaire de prévoir d’espaces supplémentaires, bien qu’en pratique cela améliore la
lisibilité du programme.
Ainsi, vous ne pouvez pas remplacer :
int x,y
par
intx,y
En revanche, vous pourrez écrire indifféremment :
int n,compte,p,total,valeur ;
ou, plus lisiblement :
int n, compte, p, total, valeur ;
voire :
int n,
compte,
p,
total,
valeur ;
4.4. Les commentaires
Comme tout langage évolué, Java autorise la présence de commentaires dans les programmes source.
Ces textes explicatifs destinés aux lecteurs du programme n’ont aucune incidence sur sa compilation.
Java dispose de deux formes de commentaires :
• les commentaires usuels,
• les commentaires de fin de ligne,
Ce sont ceux de C++. Ils sont introduits par le double caractère //. Tout le texte suivant jusqu’à la fin de la
ligne est considéré comme un commentaire. Cette nouvelle possibilité n’apporte qu’un surcroît de confort
et de sécurité. En effet, une ligne telle que :
14
Vous pouvez mêler les deux formules. Notez que dans :
le commentaire ouvert par /* ne se termine qu’au prochain */ ; donc partie1 et partie2 sont des
commentaires, tandis que partie3 est considéré comme appartenant aux instructions. De même, dans :
partie1 // partie2 /* partie3 */ partie4
Le commentaire introduit par // s’étend jusqu’à la fin de la ligne. Il couvre donc partie2, partie3 et partie4.
Compléments
On dit souvent que Java possède une troisième forme de commentaires dits de documentation. Il s’agit en fait
d’un cas particulier de commentaires usuels, puisqu’ils commencent par /** et qu’ils se terminent par */. Leur
seul intérêt est de pouvoir être extraits automatiquement par des programmes utilitaires de création de
documentation tels que Javadoc.
En résumé
15
Chapitre 2
Les types des variables et les
opérateurs en Java
Dans les chapitres précédents, nous avons rencontré les types int et double. Java dispose d’un certain
nombre de types de base dits primitifs, permettant de manipuler des entiers, des flottants, des
caractères et des booléens. Ce sont les seuls types du langage qui ne sont pas des classes. Mais ils seront
utilisés pour définir les champs de données de toutes les classes que vous serez amenés à créer.
Avant de les étudier en détail, nous effectuerons un bref rappel concernant la manière dont
l’information est représentée dans un ordinateur et la notion de type qui en découle.
16
1.2.1. Représentation mémoire
Un bit est réservé au signe (0 pour positif et 1 pour négatif). Les autres servent à représenter :
● la valeur absolue du nombre pour les positifs,
● ce que l’on nomme le complément à deux du nombre, pour les négatifs.
Examinons cela plus en détail, dans le cas de nombres représentés sur 16 bits (ce qui correspondra
finalement au type short de Java).
1 0000000000000001 0001
2 0000000000000010 0002
3 0000000000000011 0003
16 0000000000010000 00F0
127 0000000001111111 007F
255 0000000011111111 00FF
-1 1111111111111111 FFFF
-2 1111111111111110 FFFE
-3 1111111111111101 FFFD
-4 1111111111111100 FFFC
-16 1111111111110000 FFF0
-256 1111111100000000 FF00
Java dispose de quatre types entiers, correspondant chacun à des emplacements mémoire de taille
différente, donc à des limitations différentes. Le tableau suivant en récapitule leurs caractéristiques.
Notez l’existence de constantes prédéfinies de la forme Integer.MAX_VALUE qui fournissent les
différentes limites.
17
Type Taille Taille Valeur minimale Valeur maximale
(octets) (bit)
long 8 64 -9 223 372 036 854 775 808 9 223 372 036 854 775 807
(Long.MIN_VALUE) (Integer.MAX_VALUE)
18
Le tableau suivant récapitule les caractéristiques des types float et double. Notez ici encore l’existence
de constantes prédéfinies de la forme Float.MAX_VALUE qui fournissent les différentes limites.
Type Taille Taille Précision Valeur absolue minimale Valeur absolue maximale
(octets) (bit) (chiffres
significatifs)
Une estimation de l’erreur relative de troncature est fournie dans la colonne Précision sous la forme
d’un nombre de chiffres significatifs. Il s’agit d’une approximation décimale plus parlante que son
équivalent (exact) en nombre de bits significatifs.
1.4. Le type caractère
1.4.1. Généralités
Comme la plupart des langages, Java permet de manipuler des caractères. Mais il offre l’originalité
de les représenter en mémoire sur deux octets en utilisant le code universel Unicode.
Parmi les 65536 combinaisons qu’offre Unicode, on retrouve les 128 possibilités du code ASCII
restreint (7 bits) et même les 256 possibilités du code ASCII/ANSI (Latin 1) utilisé par Windows.
Celles-ci s’obtiennent simplement en complétant le code ASCII par un un premier octet nul. Cela
signifie donc qu’en Java comme dans les autres langages, la notion de caractère dépasse celle de
caractère imprimable, c’est-à-dire auquel on peut associer un graphisme.
Une variable de type caractère se déclare en utilisant le mot-clé char comme dans :
char c1, c2 ; // c1 et c2 sont deux variables de type caractère
19
Bien entendu, cette notation n’est utilisable que si le caractère est disponible dans l’implémentation
considérée, qu’il dispose d’un graphisme et d’une combinaison de touche permettant de le saisir avec
un éditeur.
Certains caractères ne disposant pas de graphisme possèdent une notation conventionnelle utilisant
le caractère \ ; dans cette catégorie, on trouve également quelques caractères qui, bien que disposant
d’un graphisme, jouent un rôle particulier de délimiteur qui leur interdit la notation classique. En
voici la liste :
\’’ 0022
\’ 0027
\\
Ce type sert à représenter une valeur logique du type vrai/faux. Il n’existe pas dans tous les langages
car il n’est pas aussi indispensable que les autres. En effet, on peut souvent se contenter d’expressions
booléennes du genre de celles qu’on utilise dans une instruction if :
if (n<p) ..... // n<p est une expression booléenne valant vrai ou faux
20
En Java, on peut disposer de variables de ce type, ce qui permettra des affectations telles que :
Une variable peut recevoir une valeur initiale au moment de sa déclaration, comme dans :
int n=15;
Cette instruction joue le même rôle que :
int n; n=15;
Rien n’empêche que la valeur de n n’évolue par la suite.
Comme en Java les déclarations peuvent apparaître à n’importe quel emplacement du programme,
on ne peut plus les distinguer complètement des instructions exécutables. En particulier, on peut
initialiser une variable avec une expression autre qu’une constante. Voici un exemple :
Un autre exemple :
int n; .....
n = Clavier.lireInt() ;
int p=2*n; //OK-la valeur initiale de p ne sera toute fois
// connue qu’au moment de l’exécution
En Java, une variable n’ayant pas encore reçu de valeur ne peut pas être utilisée, sous peine d’aboutir
à une erreur de compilation. En voici deux exemples :
21
int n;
System.out.println("n="+n); //erreur de compilation: la valeur de n n’est pas définie ici
int n;
int p = n+3 ; // erreur de compilation : la valeur de n n’est pas encore défine
n = 12 ; // elle l’est ici (trop tard)
Contrairement à la plupart des autres langages, Java est capable de détecter toutes les situations de
variables non initialisées, comme le montre cet exemple :
int n;
if(...)n=30;
p=2*n; //erreur de compilation:n n’est pas initialisée dans tous les cas
Ici, le compilateur s’est aperçu de ce que la variable n pouvait ne pas recevoir de valeur dans certains
cas. En revanche, les instructions suivantes seront acceptées :
int n;
if(...)n=30;
else n=10;
p=2*n; //OK: n a obligatoirement reçu l’une des valeurs 30 ou 10
Compléments
Nous verrons que ce que nous nommons pour l’instant variable est en fait une variable locale à la
méthode main. Nous rencontrerons d’autres sortes de variables, en particulier les champs des objets.
Contrairement aux variables locales évoquées ici, ces champs seront soumis à une initialisation
implicite par défaut ; le risque de champ non défini n’existera donc pas. D’autre part, nous verrons
qu’il existe des variables ou des champs d’un type autre que primitif, à savoir objet ou tableau.
22
2. Les types objets
2.1. 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 :
10
//Quatrième méthode de déclaration
11
String chaine = new String("Et une de plus !");
12
13
Attention
String commence par une majuscule ! Et lors de l'initialisation, on utilise des doubles cotes (« " " »).
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.
23
Voici quelques exemples de noms de classes et de variables :
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.
Remarque :
Veillez à bien respecter la casse (majuscules et minuscules), car une déclaration de CHAR à la place
de char ou autre chose provoquera une erreur, tout comme une variable de type string à la place de
String !
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 :
3. Les opérateurs
Java est l’un des langages les plus fournis en opérateurs. Il dispose en effet des opérateurs classiques
(arithmétiques, logiques) mais aussi d’un important éventail d’opérateurs originaux d’affectation et
d’incrémentation. Nous verrons en effet que ces actions sont réalisées par des opérateurs.
Ce paragraphe étudie tous les opérateurs de Java, ainsi que les règles de priorité et de conversion de
type qui interviennent dans les évaluations des expressions.
24
3.1. Les opérateurs arithmétiques
Les opérateurs arithmétiques sont ceux que l'on apprend à l'école primaire… ou presque :
25
int x=5, y ; x = 5, y = ??? Déclaration
Pour l'instruction précédente " y = b / 2 " engendrant une erreur de conversion voici deux corrections
possibles utilisant le transtypage :
y = (int)b / 2 ; // b est converti en int avant la division qui s'effectue sur deux int.
y = (int)(b / 2) ; // c'est le résultat de la division qui est converti en int.
3.2. Les opérateurs relationnels
Comme tout langage, Java permet de comparer des expressions à l’aide d’opérateurs classiques de
comparaison. En voici un exemple :
2 * a >b +5
Le résultat est une valeur booléenne ayant l’une des deux valeurs true ou false. On pourra :
● l’utiliser dans une instruction if, comme dans :
if(2*a>b+5)...
● l’affecter à une variable booléenne :
26
boolean ok ; .....
ok=2*a>b+5
Opérateur Signification
< Inférieur à
> Supérieur à
== Égal à
!= Different de
27
3.3. Les opérateurs logiques
Java dispose d’opérateurs logiques dont voici la liste, classée par priorités décroissantes (il n’existe pas
deux opérateurs ayant la même priorité). Nous reviendrons un peu plus loin sur l’existence
d’opérateurs voisins (& et &&, | et ||).
Opérateur Signification
! Négation
&& Et
|| ou
Par exemple :
(a<b) && (c<d)
prend la valeur true (vrai) si les deux expressions a<b et c<d sont toutes les deux vraies, la valeur false
(faux) dans le cas contraire.
(a<b) || (c<d)
prend la valeur true si l’une au moins des deux conditions a<b et c<d est vraie, la valeur
false dans le cas contraire.
! (a<b)
prend la valeur true si la condition a<b est fausse, la valeur false dans le cas contraire. Cette expression
possède en fait la même valeur que a>=b.
3.4. Les opérateurs d’incrémentation et de décrémentation
Dans des programmes écrits dans un langage autre que Java (ou C), on rencontre souvent des
expressions (ou des instructions) telles que :
i= i+ 1
n =n - 1
qui incrémentent ou qui décrémentent de 1 la valeur d’une variable. En Java, ces actions peuvent
être réalisées par des opérateurs unaires portant sur cette variable. Ainsi, l’expression : ++i
a pour effet d’incrémenter de 1 la valeur de i, et sa valeur est celle de i après incrémentation.
Là encore, comme pour l’affectation, nous avons affaire à une expression qui non seulement
possède une valeur, mais qui, de surcroît, réalise une action (incrémentation de i).
Il est important de voir que la valeur de cette expression est celle de i après incrémentation.
28
Ainsi, si la valeur de i est 5, l’expression :
n = ++i - 5
affectera à i la valeur 6 et à n la valeur 1.
En revanche, lorsque cet opérateur est placé après son unique opérande, la valeur de l’expression
correspondante est celle de la variable avant incrémentation. Ainsi, si i vaut 5, l’expression :
n = i++ - 5
affectera à i la valeur 6 et à n la valeur 0 (car ici la valeur de l’expression i++ est 5). On dit que ++ est:
● un opérateur de préincrémentation lorsqu’il est placé à gauche de son opérande,
● un opérateur de postincrémentation lorsqu’il est placé à droite de son opérande.
Bien entendu, lorsque seul importe l’effet d’incrémentation de l’opérande, cet opérateur peut être
indifféremment placé avant ou après. Ainsi, ces deux instructions sont équivalentes (ici, il s’agit bien
d’instructions car les expressions sont terminées par un point-virgule – leur valeur se trouve donc
inutilisée) :
i++ ;
++i ;
De la même manière, il existe un opérateur de décrémentation noté -- qui est :
● un opérateur de prédécrémentation lorsqu’il est placé à gauche de son opérande,
● un opérateur de postdécrémentation lorsqu’il est placé à droite de son opérande.
Tous ces opérateurs peuvent s’appliquer à tous les types numériques (pas nécessairement entiers)
ainsi qu’au type char.
29
Chapitre 3
Les entrées clavier
Après 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.
1. La classe Scanner
Les 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 :
30
1 import java.util.Scanner;
Vous devez avoir une jolie vague rouge sous le mot Scanner. Ajoutez au-dessus de la
déclaration de votre classe, cette ligne :
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.
Un package est un ensemble de classes. En fait, c'est un ensemble de dossiers et de sous-dossiers
contenant une ou plusieurs classes, mais nous verrons ceci plus en détail lorsque nous ferons nos
propres packages.
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.
31
Une fois l'application lancée, le message que vous avez écrit auparavant s'affiche dans la console, en
bas. 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.
Si vous remplacez la ligne de code qui récupère une chaîne de caractères comme suit :
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, l’IDE 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).
Il y’a un type de variables primitives qui n’est pas pris en compte par la classe Scanner : il s’agit du
type char.
Voici comment on pourrait récupérer un caractère :
32
1 System.out.println("Saisissez une lettre :");
2 Scanner sc = new Scanner(System.in);
3 String str = sc.nextLine();
4 char carac = str.charAt(0);
5 System.out.println("Vous avez saisi le caractère : " + carac);
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.
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 :
33
1 import java.util.Scanner;
2
3 public class Main {
4 public static void main(String[] args){
5 Scanner sc = new Scanner(System.in);
6 System.out.println("Saisissez un entier : ");
7 int i = sc.nextInt();
8 System.out.println("Saisissez une chaîne : ");
9 String str = sc.nextLine();
10 System.out.println("FIN ! ");
11 }
12 }
… 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 :
1 import java.util.Scanner;
2
3 public class Main {
4 public static void main(String[] args){
5 Scanner sc = new Scanner(System.in);
6 System.out.println("Saisissez un entier : ");
7 int i = sc.nextInt();
8 System.out.println("Saisissez une chaîne : ");
9 //On vide la ligne avant d'en lire une autre
10 sc.nextLine();
11 String str = sc.nextLine();
12 System.out.println("FIN ! ");
}
34
13 }
14
En résumé
● Pour pouvoir récupérer ce vous allez taper dans la console, vous devrez initialiser l'objet
Il y a une méthode de récupération de données pour chaque type (sauf les char) : nextLine() pour les
String, nextInt()pour les int …
35
Chapitre 4 :
Les structures
conditionnelles et les boucles
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.
1. La structure « if…else »
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 :
36
● « ? : » : l'opérateur ternaire. Pour celui-ci, vous comprendrez mieux avec un exemple qui
Comme 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 :
1 if(//condition)
{
2
//Exécution des instructions si la condition est remplie
3 }
4 else
5 {
//Exécution des instructions si la condition n'est pas
6
remplie
7 }
8
1 int i = 10;
2
if (i < 0)
3 System.out.println("le nombre est négatif");
4 else
5 System.out.println("le nombre est positif");
Dans ce cas, notre classe affiche « le nombre est positif ». Expliquons un peu ce qui se passe.
37
● Dans un premier temps, la condition du if est testée : elle dit « si i est strictement inférieur à
0 alors fais ça ».
● Dans un second temps, vu que la condition précédente est fausse, le programme exécute le
else.
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 :
1 int i = 0;
2 if (i < 0)
3 {
4 System.out.println("Ce nombre est négatif !");
5 }
6 else
7 {
8 if(i == 0)
9 System.out.println("Ce nombre est nul !");
10
11 else
12 System.out.println("Ce nombre est positif !");
13 }
14
Une autre façon d'écrire ce code, avec le même résultat : on ajoute juste un petit « sinon si… ».
38
1 int i = 0;
2 if (i < 0)
System.out.println("Ce nombre est négatif !");
3
4 else if(i > 0)
5 System.out.println("Ce nombre est positif !");
6
7 else
System.out.println("Ce nombre est nul !");
39
1 int i = 58;
2 if(i < 100 && i > 50)
3 System.out.println("Le nombre est bien dans l'intervalle.");
4 else
5 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 ».
Avec l'opérateur « && », la clause est remplie si et seulement si les conditions la constituant sont
toutes remplies ; si l'une des conditions n'est pas vérifiée, la clause sera considérée comme fausse.
Cet opérateur vous initie à la notion d'intersection d'ensembles. Ici, nous avons deux conditions qui
définissent un ensemble chacune :
1 int i = 58;
2 if(i < 100 && i > 100)
3 System.out.println("Le nombre est bien dans l'intervalle.");
4 else
5 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.
2. La structure switch
40
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 :
Syntaxe
1 switch (/*Variable*/)
2 {
3 case /*Argument*/:
4 /*Action*/;
5 break;
6 default:
7 /*Action*/;
8 }
● Si aucun des cas ne correspond, la classe va exécuter ce qui se trouve dans l'instruction
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 :
41
1 int note = 10; //On imagine que la note maximale est 20
2
3 switch (note)
4 {
5 case 0:
6 System.out.println("Ouch !");
7 break;
8 case 10:
9 System.out.println("Vous avez juste la moyenne.");
10 break;
11 case 20:
12 System.out.println("Parfait !");
13 break;
14 default:
15 System.out.println("Il faut davantage travailler.");
16 }
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 :
42
1 String chaine = "Bonjour";
2
switch(chaine) {
3
case "Bonjour":
4
System.out.println("Bonjour monsieur !");
5 break;
6 case "Bonsoir":
7 System.out.println("Bonsoir monsieur !");
8 break;
9 default:
System.out.println("Bonjoir ! :p");
10
}
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 :
● Nous cherchons à affecter une valeur à notre variable max, mais de l'autre côté de l'opérateur
d'affectation se trouve une condition ternaire…
● Ce qui se trouve entre les parenthèses est évalué : x est-il plus petit que y ? Donc, deux cas de
figure se profilent à l'horizon :
○ si la condition renvoie true (vrai), qu'elle est vérifiée, la valeur qui se trouve après le ?
sera affectée ;
○ sinon, la valeur se trouvant après le symbole: sera affectée.
● L'affectation est effective : vous pouvez utiliser votre variable max.
Vous pouvez également faire des calculs (par exemple) avant d'affecter les valeurs :
43
1 int x = 10, y = 20;
2 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 :
○ la structure ? :.
● Si un bloc d'instructions contient plus d'une ligne, vous devez l'entourer d'accolades afin de
bien en délimiter le début et la fin.
● Pour pouvoir mettre une condition en place, vous devez comparer des variables à l'aide
d'opérateurs logiques.
● Vous pouvez mettre autant de comparaisons renvoyant un boolean que vous le souhaitez
dans une condition.
● Pour la structure switch, pensez à mettre les instructions break; si vous ne souhaitez exécuter
qu'un seul bloc case.
44
3. Les boucles
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 !
3.1 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 !
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 !
45
//Une variable vide
1 String prenom;
//On initialise celle-ci à O pour oui
char reponse = 'O';
2 //Notre objet Scanner, n'oubliez pas l'import de
java.util.Scanner !
3 Scanner sc = new Scanner(System.in);
//Tant que la réponse donnée est égale à oui…
4 while (reponse == 'O')
{
//On affiche une instruction
5
System.out.println("Donnez un prénom : ");
//On récupère le prénom saisi
6 prenom = sc.nextLine();
//On affiche notre phrase avec le prénom
7 System.out.println("Bonjour " +prenom+ ", comment
vas-tu ?");
//On demande si la personne veut faire un autre
8 essai
System.out.println("Voulez-vous réessayer ?
9 (O/N)");
1 //On récupère la réponse de l'utilisateur
0 reponse = sc.nextLine().charAt(0);
}
1
1 System.out.println("Au revoir…");
1 //Fin de la boucle
46
a lieu. À la fin, c'est-à-dire à l'accolade fermante de la boucle, le compilateur nous ramène au début
de la boucle.
Cette boucle n'est exécutée que lorsque la condition est remplie : ici, nous avons initialisé la variable
reponse à « O » pour que la boucle s'exécute. Si nous ne l'avions pas fait, nous n'y serions jamais
entrés. Normal, puisque nous testons la condition avant d'entrer dans 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é :
47
1 String prenom;
char reponse = 'O';
2
Scanner sc = new Scanner(System.in);
3 while (reponse == 'O')
4 {
5 System.out.println("Donnez un prénom : ");
prenom = sc.nextLine();
6
System.out.println("Bonjour " +prenom+ ", comment vas-
7 tu ?");
8 //Sans ça, nous n'entrerions pas dans la deuxième boucle
9 reponse = ' ';
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!
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.
48
1 int a = 1, b = 15;
2 while (a < b)
3 {
4 System.out.println("coucou " +a+ "
5 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
1 int a = 1, b = 15;
2 while (a < b)
3 {
4 System.out.println("coucou " +a+ "
5 fois !!");
6 a++;
}
Lorsque vous n'avez qu'une instruction dans votre boucle, vous pouvez enlever les accolades, car
elles deviennent superflues, tout comme pour les instructions if, else if ou else.
Vous auriez aussi pu utiliser cette syntaxe :
1 int a = 1, b = 15;
2 while (a++ < b)
3 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 :
49
1 int a = 1, b = 15;
2 while (++a < b)
3 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 !
1 do{
2 //Instructions
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.
50
1 String prenom = new String();
//Pas besoin d'initialiser : on entre au moins une
2 fois dans la boucle !
3 char reponse = ' ';
do{
System.out.println("Voulez-vous réessayer ?
(O/N)");
reponse = sc.nextLine().charAt(0);
}while(reponse != 'O' && reponse != 'N');
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 !
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.
51
1 for(int i = 1; i <= 10; i++)
2 {
System.out.println("Voici la ligne "+i);
3
}
4
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 :
● Tout comme les conditions, si une boucle contient plus d'une ligne de code à exécuter, vous
52
Chapitre 5 :
Les concepts orientés objets
Chaque langage de programmation appartient à une “famille” de langages définissant une approche
ou une méthodologie générale de programmation. Par exemple, le langage C est un langage de
programmation procédurale car il suppose que le programmeur s’intéresse en priorité aux
traitements que son programme devra effectuer. Un programmeur C commencera par identifier ces
traitements pour écrire les fonctions qui les réalisent sur des données prises comme paramètres
d’entrée.
La programmation orientée-objet (introduite par le langage SmallTalk) propose une méthodologie
centrée sur les données. Le programmeur Java va d’abord identifier un ensemble d’objets, tel que
chaque objet représente un élément qui doit être utilisé ou manipulé par le programme, sous la forme
d’ensembles de données. Ce n’est que dans un deuxième temps, que le programmeur va écrire les
traitements, en associant chaque traitement à un objet donné. Un objet peut être vu comme une
entité regroupant un ensemble de données et de méthodes (l’équivalent d’une fonction en C) de
traitement.
La POO est dirigée par trois fondamentaux qu'il convient de toujours garder à l'esprit :
Encapsulation, héritage et polymorphisme.
1. Notion de classe
Un objet est une variable (presque) comme les autres. Il faut notamment qu’il soit déclaré avec son
type. Le type d’un objet est un type complexe (par opposition aux types primitifs entier, caractère, …)
qu’on appelle une classe.
Une classe est un modèle de la structure statique (les champs ou attributs) et du comportement
dynamique (les opérations ou méthodes) des objets associés à cette classe.
Une classe est un modèle d'objet. C'est un nouveau type créé par le programmeur et qui sert de
modèle pour tous les objets de cette classe. Une classe spécifie les informations et les actions
qu'auront en commun tous les objets qui en sont issus.
Une classe regroupe un ensemble de données (qui peuvent être des variables primitives ou des objets)
et un ensemble de méthodes de traitement de ces données et/ou de données extérieures à la classe.
On parle d’encapsulation pour désigner le regroupement de données dans une classe.
Par exemple, une classe Rectangle utilisée pour instancier des objets représentant des
rectangles, encapsule 4 entiers : la longueur et la largeur du rectangle ainsi que la position en abscisse
et en ordonnée de l’origine du rectangle (par exemple, le coin en haut à gauche). On peut alors
53
imaginer que la classe Rectangle implémente une méthode permettant de déplacer le rectangle qui
nécessite en entrée deux entiers indiquant la distance de déplacement en abscisse et en ordonnée.
L’accès aux positions de l’origine du rectangle se fait directement (i.e. sans passage de paramètre)
lorsque les données sont encapsulées dans la classe où est définie la méthode.
Représentation graphique:
Rectangle
- longueur
- largeur
- origine_x
- origine_y
+ deplace()
+ surface()
Représentation en code:
Un exemple, écrit en Java, de la classe Rectangle est donné ci-dessous :
int longueur;
int largeur;
int origine_x;
int origine_y;
}
}
Pour écrire un programme avec un langage orienté-objet, le programmeur écrit uniquement des
classes correspondant aux objets de son système. Les traitements à effectuer sont programmés dans
les méthodes de ces classes qui peuvent faire appel à des méthodes d’autres classes. En général, on
54
définit une classe, dite “exécutable”, dont une méthode peut être appelée pour exécuter le
programme.
1.1. Encapsulation
Lors de la conception d’un programme orienté-objet, le programmeur doit identifier les objets et les
données appartenant à chaque objet mais aussi des droits d’accès qu’ont les autres objets sur ces
données. L’encapsulation de données dans un objet permet de cacher ou non leur existence aux
autres objets du programme. Une donnée peut être déclarée en accès :
● Public ou + :
les autres objets peuvent accéder à la valeur de cette donnée ainsi que la modifier ;
● Privé ou private ou - :
les autres objets n’ont pas le droit d’accéder directement à la valeur de cette donnée (ni de la
modifier). En revanche, ils peuvent le faire indirectement par des méthodes de l’objet concerné (si
celles-ci existent en accès public).
● Protégé ou protected ou #:
L’élément protégé est accessible aux objets de la classe dans laquelle il est défini et aux objets des
classes dérivées.
public protected défaut private
Dans la même classe Oui Oui Oui Oui
Dans une classe du Oui Oui Oui Non
même package
Dans une sous-classe Oui Oui Non Non
d’un autre package
Dans une classe quelcon- Oui Non Non Non
que d’un autre package
Chaque classe doit définir une ou plusieurs méthodes particulières appelées des constructeurs. Un
constructeur est une méthode invoquée lors de la création d’un objet. Cette méthode, qui peut être
vide, effectue les opérations nécessaires à l’initialisation d’un objet. Chaque constructeur doit avoir
le même nom que la classe où il est défini et n’a aucune valeur de retour (c’est l’objet créé qui est
renvoyé).
Règles particulières des constructeurs :
● Un constructeur doit toujours être une méthode publique
● Vous devez impérativement donner le même nom que votre classe au constructeur
● Un constructeur ne retourne rien, c'est-à-dire que vous n'ajouterez pas de return dans cette
méthode.
55
● Vous ne devez pas mettre de void, même si le constructeur ne retourne rien.
Dans l’exemple précédent de la classe rectangle, le constructeur initialise la valeur des données
encapsulées :
56
Un constructeur de copie, appelé aussi « constructeur par recopie », est un constructeur qui admet
comme paramètre un objet (de la même classe) déjà existant.
1.3. Destructeur
Un destructeur est une opération de classe qui détruit des objets. Cette méthode libère donc de
l'espace mémoire. Le garbage collector est un "ramasse-miettes" chargé de faciliter la tâche des
programmeurs en libérant les ressources prises en mémoire automatiquement. Cette méthode n'est
pas forcément appelée lorsque vous supprimez l'objet.
Pour utiliser le destructeur vous devez redéfinir une méthode appelée finalize() publique et qui ne
renvoie rien. On peut donc avoir le code suivant :
public class Rectangle {
...
public Rectangle (Rectangle r)
{
longueur = r.longueur;
largeur = r.largeur;
origine_x = r.origine_x ;
origine_y = r.origine_y ;
}
public finalize ();
}
1.4. Les accesseurs et les mutateurs(get/set)
Un accesseur(get) est une méthode permettant de récupérer le contenu d'une donnée membre
protégée. Un accesseur, pour accomplir sa fonction :
● doit avoir comme type de retour le type de la variable à renvoyer
● ne doit pas nécessairement posséder d'arguments
Une convention de nommage veut que l'on fasse commencer de façon préférentielle le nom de
l'accesseur par le préfixe get, afin de faire ressortir sa fonction première.
➔ get: permet de lire la valeur d’un attribut.
La syntaxe d'un accesseur réduit à sa plus simple expression ressemble donc à ceci :
public class Rectangle {
int longueur;
int largeur;
int origine_x;
int origine_y;
public int getLongueur ()
{
57
return longueur;
}
}
Un mutateur(set) est une méthode permettant de modifier le contenu d'une donnée membre
protégée. Un mutateur, pour accomplir sa fonction :
● doit avoir comme paramètre la valeur à assigner à la donnée membre. Le paramètre doit donc
être du type de la donnée membre
● ne doit pas nécessairement renvoyer de valeur (il possède dans sa plus simple expression le
type void)
Une convention de nommage veut que l'on fasse commencer de façon préférentielle le nom du
mutateur par le préfixe set.
La syntaxe d'un mutateur réduit à sa plus simple expression ressemble donc à ceci :
➔ Set : permet de modifier la valeur d’un attribut.
2. Notion d’objet
Un objet représente une entité individuelle et identifiable, réelle ou abstraite, avec un rôle bien défini
dans le domaine du problème, chaque objet peut être caractérisé par une identité, des états
significatifs et par un comportement.
2.1. Instanciation
58
Un objet est une instance (anglicisme signifiant « cas » ou « exemple ») d’une classe et est référencé
par une variable ayant un état (ou valeur). Pour créer un objet, il est nécessaire de déclarer une
variable dont le type est la classe à instancier, puis de faire appel à un constructeur de cette classe.
L’exemple ci-dessous illustre la création d’un objet de classe Cercle en Java :
Cercle mon_rond;
mon_rond = new Cercle();
L’usage de parenthèses à l’initialisation du vecteur, montre qu’une méthode est appelée pour
l’instanciation. Cette méthode est un constructeur de la classe Cercle. Si le constructeur appelé
nécessite des paramètres d’entrée, ceux-ci doivent être précisés entre ces parenthèses (comme lors
d’un appel classique de méthode). L’instanciation d’un objet de la classe Rectangle faisant appel au
constructeur donné en exemple ci-dessous pourra s’écrire :
La même syntaxe est utilisée pour appeler une méthode d’un objet. Par exemple :
mon_rectangle.deplace(0,0) ;
Pour qu’un tel appel soit possible, il faut que trois conditions soient remplies :
a. La variable ou la méthode appelée existe !
b. Une variable désignant l’objet visé existe et soit instanciée.
c. L’objet, au sein duquel est fait cet appel, ait le droit d’accéder à la méthode ou à la variable.
59
Pour référencer l’objet “courant” (celui dans lequel se situe la ligne de code), le langage Java fournit
le mot-clé this. Celui-ci n’a pas besoin d’être instancié et s’utilise comme une variable désignant
l’objet courant. Le mot-clé this est également utilisé pour faire appel à un constructeur de l’objet
courant. Ces deux utilisations possibles de this sont illustrées dans l’exemple suivant :
class Carre {
int cote;
int origine_x;
int origine_y;
Carre( int cote, int
x, int y) {
this.cote = cote;
this.origine_x =
x;
this.origine_y =
y;
}
Carre( int cote){
this cote, 0, 0 ;
}
}
60
Chapitre 6
Héritage
1. Héritage
Le principe
Quand on dispose d’une classe c, on a la possibilité de l’agrandir (ou de l’étendre) en créant une
deuxième classe c0. On dit dans ce cas que c0 hérite de c, ou encore que c est la classe mère et c0 la classe
fille. Par exemple, considérons les deux classes suivantes :
package heritage;
public class ClasseMere{
private final int x;
61
Notes bien que comme la classe mère possède une méthode getX, la classe fille la possède aussi. Et
l’attribut x de tout instance de la classe mère est aussi un attribut de toute instance de la classe fille.
En java, une classe ne peut avoir qu’une seule classe mère. Dans d’autres langages (comme le C++)
cela est permis, mais pour des raisons de fiabilité, Java l’interdit.
Object
En java, toutes les classes héritent implicitement d’une classe Object. L’ensemble des classes
Java, y compris celles écrites en dehors de l’API, forme une hiérarchie avec une racine unique.
Cette racine est la classe Object dont hérite toute autre classe. En effet, si vous ne précisez pas
explicitement une relation d’héritage lors de l’écriture d’une classe, celle-ci hérite par défaut de la
classe Object. Grâce à cette propriété, des classes génériques de création et de gestion d’un
ensemble, plus élaborées que les tableaux, regroupent des objets appartenant à la classe Object
(donc de n’importe quelle classe).
Une des propriétés induites par le polymorphisme est que l’interpréteur Java est capable de
trouver le traitement à effectuer lors de l’appel d’une méthode sur un objet. Ainsi, pour plusieurs
objets déclarés sous la même classe (mais n’ayant pas la même classe réelle), le traitement associé à
une méthode donnée peut être différent. Si cette méthode est redéfinie par la classe réelle d’un
objet (ou par une classe située entre la classe réelle et la classe de déclaration), le traitement effectué
est celui défini dans la classe la plus spécifique de l’objet et qui redéfinit la méthode.
2. Polymorphisme
Considérons l’exemple suivant :
package heritage ;
public class TestClasseFillePolymorphisme{
public static void main(String [] args){
ClasseMere o = new ClasseFille(1,2);
System.out.println(“(”+o.getX()+”,”
+((ClasseFille)o).getY()+”)”);
}
}
On remarque d’une part que o référence un objet de la classe fille de son type. Cela s’appelle le
polymorphisme. D’autre part, si l’on souhaite effectuer des opérations spécifiques aux objets de la
classe fille, il est nécessaire d’effectuer un cast.
3. Redéfinition de méthodes
62
La méthode toString() appartient initialement à la classe Object. Elle est donc héritée par toutes les
classes. Vous avez la possibilité de la redéfinir, c'est-à-dire de remplacer la méthode par défaut par
une méthode davantage adaptée à la classe en question.
4. Interfaces
L’héritage multiple est interdit en Java. Les interfaces sont un moyen de résoudre en partie le
problème.
Une interface est un ensemble de constantes et de méthodes vides. Il est impossible d’instancier une
interface. On utilise une interface en créant une classe qui en hérite et qui contient le corps des
méthodes qui y sont déclarées.. Dans le cas où la classe mère est une interface, le mot-clé implements
prend la place de extends.
L’utilité du concept d’interface réside dans le regroupement de plusieurs classes, tel que chacune
implémente un ensemble commun de méthodes, sous un même type. Une interface possède les
caractéristiques suivantes :
● elle contient des signatures de méthodes ;
● elle ne peut pas contenir de variables ;
● une interface peut hériter d’une autre interface (avec le mot-clé extends) ;
● une classe (abstraite ou non) peut implémenter plusieurs interfaces. La liste des interfaces
implémentées doit alors figurer après le mot-clé implements placé dans la déclaration de
classe, en séparant chaque interface par une virgule.
En voici un exemple d’utilisation :
package heritage ;
import java . util . ArrayList ;
interface Saluer{
public void direBonjour () ;
}
63
}
}
}
Vous remarquez que la méthode Saluer est implémentée par les trois classes Bonjour, Hello et
GutenTag, qui contiennent trois façons différentes de programmer la méthode public void saluer().
Le polymorphisme permet de mettre dans une référence de type Saluer tout objet dont le type hérite
de Saluer.
On peut voir une interface comme un contrat :
§ Toute sous-classe d’une interface se doit d’implémenter toutes les méthodes qui y sont
déclarées.
§ En échange, il devient possible d’utiliser le polymorphisme, et donc d’utiliser le même
code avec des objets de types différents.
La restriction interdisant l’héritage multiple en Java ne s’applique pas aux interfaces. Une classe peut
implémenter plusieurs interfaces.
5. Classes Abstraites
Nous voulons représenter des sommes dans des devises différentes et implémenter
automatiquement les conversions. Nous allons créer une classe devise qui contiendra l’attribut
somme et nous utiliserons l’héritage pour implémenter les spécificités des diverses devises (Euros,
Dollars, Livres, ...).
package heritage;
public abstract class Devise{
private double somme = 0;
/∗
∗ Nombre de devises pour 1$ .
∗/
64
public abstract double getCours () ;
public abstract String getUnite () ;
protected void setSomme(double somme){
this.somme=somme;
}
@override
}
public class Livres extends Devise{
public Livres(Devise d) {
setSomme(d);
}
return “Livres”;}
65
@Override public String getUnite (){
return "Euros" ;
}
@Override
public double getCours(){
return 1.0;
}
@Override
return "Dollars" ;
66
Chapitre 7
Les Tableaux en JAVA
1. Déclaration
Un tableau en Java est un objet. Il est nécessaire de le créer par allocation dynamique avec un new
en précisant ses dimensions. On note:
T[]
Le type tableau d’éléments de type T. La taille du tableau n’est précisée qu’à l’instanciation. On
déclare un tableau t d’éléments de type T de la façon suivante :
T [ ] t;
Par exemple, si l’on souhaite créer un tableau i d’éléments de type int, on utilise l’instruction :
int [ ] i ;
2. Instanciation
Comme un tableau est un objet, il est nécessaire d’instancier pendant l’exécution. On instancie un
tableau avec l’instruction
new T[taille];
Par exemple, si l’on souhaite déclarer et allouer dynamiquement un tableau de 100 entiers, en utilise
67
int [ ] t =new int [100];
for ( int i = 0 ; i < 100; i++)
{
t [ i ] = 100 − (i + 1) ;
System.out.println(t [ i ]) ;
}
On remarque que la variable i est déclarée à l’intérieur de la boucle for, cette façon de déclarer les
variables est très utile en Java. Dans l’exemple,
package tableaux ;
68
int [ ] [ ] [ ] t = new int [ 2 ] [ 2 ] [ 2 ] ;
for(int i=0; i<2; i++)
for(int j=0; i<2; j++)
for(int k=0; i<2; k++)
{
t[i][j][k] = 100∗i+10∗j+k;
System.out.println(t[i][j][k]) ;
}
Ou encore,
package tableaux ;
}
}
69