Java - Chapitre 2
Syntaxe, du concret !
Syntaxe, du concret !
Je vous l'accorde le premier chapitre ne permet pas de faire en soit beaucoup de choses en algorithmique pure. Mais après celui-ci, vous pourrez faire dans l'absolu des algorithmes assez approfondis (sans aller jusqu'à Dijkstra bien sûr). Ce chapitre sera centré sur deux points, les fonctions, et les structures de contrôle. Le prérequis ? Un minimum d'algorithmique ainsi que le tuto qui va bientôt arriver de notre ami Pikooz sur les boucles, mais vous pouvez déjà lire celui sur les structures conditionnelles. Mais je vois que vous êtes impatients, commençons tout de suite !
:arrow: I. Fonction
On peut aussi appeler ça procédure, voire méthode. Mais je suis mathématicien dans l'âme, je garde le sens de fonction. Bon, un petit rappel sur le sens de ce terme. Une fonction c'est une grosse boite dans laquelle on rentre des paramètres et elle s'exécute, et renvoie (ou non) une réponse. Bon mettons cela en pratique, un jour quelqu'un a dit que la réponse au sens de l'existence et à tout le reste était 42, fabriquons donc une fonction qui donne cette réponse.
- Code: Tout sélectionner
public class FctTest {
public static int existence() {
return 42;
}
public static void main(String args[]) {
System.out.println(existence());
}
}
Le code principal est connu sauf les lignes 1, 2 et 3. La ligne 2 annonce la déclaration de la fonction existence n'ayant pas de paramètre à cause du "()" retournant un int. Le public vous savez ce que c'est, et le static, gardons le pour l'instant, c.f. chapitre 3 sur l'objet.
Passons à la ligne 3 maintenant, c'est là que se fini la fonction en pratique (pas en syntaxique), on ordonne à existence() de retourner la valeur 42. La ligne 3 est très claire :D.
Passons à un peu plus dûr (ou presque), en demandant à la fonction des petits paramètres, prenons d'ailleurs notre Somme utilisé dans le chapitre 1. Faisons une mini fonction retournant la somme de deux paramètres (je sais c'est inutile mais c'est pour vous montrer) :
- Code: Tout sélectionner
public static int somme(int a, int b) {
return (a+b);
}
// Dans le main on le lancerait comme ceci
int c = somme(5,7); //c contient maintenant 12
Moui, je sais, j'ai pas bien compliqué l'affaire, mais c'est difficile de trouver un exemple qui n'utilise pas de structure de contrôle. Faisons une fonction qui fait tout le boulot à la place du main de Somme.java développé au premier chapitre.
- Code: Tout sélectionner
public static void somme() {
int a, b;
String entree;
entree = JOptionPane.showInputDialog(null, "Entrez le premier entier :");
a = Integer.parseInt(entree);
entree = JOptionPane.showInputDialog(null, "Entrez le deuxième entier :");
b = Integer.parseInt(entree);
JOptionPane.showMessageDialog(null, "Voici le résultat : "+(a+c));
}
Remarquez déjà la première ligne différente ! On n'a plus le "int" de tout à l'heure, on a ici void, en anglais ce serait vide (on aurait pu mettre empty en fait mais non), ce qui signifie que la fonction ne renvoie rien ! On a donc pas de présence d'un quelconque return. Pourtant on peut en mettre un, le cas échéant, en faisant un
- Code: Tout sélectionner
public static void test() {
...
return; // On arrête la fonction
... // Code non-exécuté
}
Bien, potentiellement nous n'avons pas tellement avancé, nous avons juste une nouvelle présentation, une nouvelle organisation du code (tout n'est pas dans le main). Voyons maintenant, le centre de la programmation.
:arrow: II. Instruction conditionnelles
Ce sont certes les instructions les plus simples, mais c'est la base de tout ce que l'on fait depuis l'avènement de l'informatique dans les années 60-70. Tout d'abord introduisont le IF (ou SI en français).
- Code: Tout sélectionner
if( <condition> ) {
<Code exécuté si condition remplie>
}
Si la condition est vraie alors faire ... Nous ne sommes pas en Basic, ni en CaML, ici on a pas besoin de then après le if, les accollades suffisent. Il y a bien évidemment les structures classiques if/else.
- Code: Tout sélectionner
if( <condition> ) {
<Code exécuté si condition remplie>
} else {
<Code exécuté si la condition était fausse>
}
Et aussi
if( <condition 1> ) {
<Code si condition 1 remplie>
} else if( <condition 2> ) {
<Code si condition 1 fausse mais 2 vraie>
...
} else if( <condition n>) {
<Code si condition 1 fausse, 2 aussi, 3 aussi ..., mais condition n vraie>
} else {
<Code si aucune condition remplie>
}
Mais comment faire une condition ? Avec un booléen évidemment, moui c'est vite dit ça, un exemple vaut mieux que de longues paroles :
- Code: Tout sélectionner
if(age >= 18) {
System.out.println("Cette personne est majeure");
} else {
System.out.println("Cette personne est mineure");
}
if(note >= 10) {
System.out.println("Bravo, tu as ton BAC !");
} else if(note >= 8) {
System.out.println("Tu es quand même au ratrapage !");
} else {
System.out.println("Désolé, tu n'es pas admis ...");
}
Voyons tout de même les opérateurs conditionnels :
- Code: Tout sélectionner
Symbole Signification
== Egalité (attention à ne pas confondre avec = qui est l'affectation)
>= Supérieur ou égal
<= Inférieur ou égal
< Inférieur strict
> Supérieur strict
!= Différent
Mais entre conditions, entre booléens quoi, on peut utiliser les opérateurs logiques :
- Code: Tout sélectionner
Symbole Signification
&& ET logique
|| OU logique
& ET booléen logique
| OU booléen logique
^ OU exclusif booléen logique
! NON logique (Ã mettre devant l'expression)
On a donc des expressions équivalentes, comme :
- Code: Tout sélectionner
!(note >= 10) == (note < 10)
Ou encore la loi de Morgan,
Soient a et b deux conditions
!(a && b) == !a || !b
!(a || b) == !a && !b
Pourquoi & et && ? C'est une sombre histoire d'évaluation, en clair lorsque vous faite de la logique pure (booléenne), vous utiliserez le &, mais lorsque vous utiliserez des condtions, vous prendrez le &&.
Les simplifications permettent d'alléger le code, donc pensez à prendre un papier lorsque vous faites un peu de conditionnel compliqué, et simplifiez du mieux que vous le pouvez (désolé à ceux qui passent(aient) un BAC S Si, ça va vous rappeler des souvenirs).
Une petite structure alternative, pour les feignants !
- Code: Tout sélectionner
variable = <condition> ? résultat vrai : résultat faux.
Exemple pour les années bissextiles
int nb_jour_fevrier = (annee%4 == 0) ? 29 : 28;
Voilà , voilà , nous avons fait à peut près le tour de la question du conditionnel simple.
Petite application rapide, dans le récursif (oula je fais peur là non ?), si vous n'aimez pas c'est pas grâve mais lisez sinon. Comment définir la fonction puissance n-ième d'un entier (voire d'un float) en quelques lignes grâce à tout ce que l'on sait :
- Code: Tout sélectionner
public static int powern(int a, int n) {
return (n != 0) ? a*powern(a,n-1) : 1;
}
Je vous laisse réfléchir là -dessus.
Voyons une dernière structure conditionnelle, appelée switch. Elle est certes un peu moins répendue car les applications sont plus faibles que le if (qui l'est tout le temps). Regardons ceci plus en détail, vous avez une variable (numérique) qui peu prendre une multitude de valeurs, l'idée est de pouvoir exploiter tous les cas qui peuvent se présenter. On a certes la solution des if imbriqué (on y reviendra la plupart du temps) mais c'est lourd à écrire, on a donc :
- Code: Tout sélectionner
switch( <variable> ) {
case <valeur 1> :
<instruction si variable == valeur 1>
break;
case <valeur 2> :
<instruction si variable == valeur 2>
break;
...
default :
<instruction par défaut si aucune valeur n'a été atteinte>
}
Inconvénient de cette structure, il n'y a que pour les valeurs numériques (pour les caractères - char - aussi mais on peut les assimiler à des nombres) que cela est utile. Pourquoi ça ne marcherait pas, je sais pas moi, avec des String par exemple ? Tout simplement car
- Code: Tout sélectionner
String s = "Salut";
if(s == "Salut") {
System.out.println("Tiens, c'est égal");
} else {
System.out.println("Ah ben non ...");
}
Et là ô surprise, le programme répond : "Ah ben non ...". Je vais marcher un peu sur l'objet là mais je n'ai pas d'autre choix. ' s ' n'est pas qu'une variable, il possède des propriétés, des fonctions, propres à lui, ainsi il est stocké d'une manière particulière en mémoire, lorsque l'on compare s et "Salut", ce sont deux objets (aïe le mot est laché) différents, l'un a été créé une ligne avant, et celui là n'est donc pas au même emplacement mémoire. Que faire ? Ce que l'on va bien souvent faire :
- Code: Tout sélectionner
s.equals("Salut") // Renvoi "true"
s.equalsIgnoreCase("salut") // Renvoi "true" (IgnoreCase veut dire, ignorer les majuscules)
s.equals("salut") // Renvoi "false"
Avant d'attaquer les boucles, mettons au point les tableaux ...
Les tableaux sont très très utilisés en programmation. D'ailleurs lorsque vous réfléchissez un peu sur un papier, vous utilisez souvent soit un schéma, soit un tableau, pour structurer vos idées, vos connaissances. Ben voila, le tableau permet de stocker une quantité indéfinie de variable et de les récupérer de façon très très simple. Voyons cela de plus près :
- Code: Tout sélectionner
int tableau[] = new int[5];
La variable tableau est un tableau (non sérieux ?) d'entier qui contient 5 emplacements. On peut évidemment généraliser cette notion à n'importe quel type
- Code: Tout sélectionner
type variable[] = new type[ taille ]);
Et là Java est très généreux, en C vous vous rappelez, vous ne pouvez pas mettre dans taille une variable, vous pouvez mettre un constante ou une valeur numérique mais pas plus. Ici tout étant géré dynamiquement, admettez que vous avez tous les droits !
On pourra très bien faire, par exemple :
- Code: Tout sélectionner
int tab[] = new int[ somme() ];
Lorsque la ligne sera appelée, somme() va se lancer, on va exécuter la fonction citée ci-dessus, et paf, on aura notre tableau avec la somme des deux valeurs rentrée.
Pour l'instant votre tableau n'a que des valeurs nulles (null si c'est un String/Objet), il faut donc initialiser le tableau. Là pas beaucoup de solutions, soit à la main, avec des boucles (c.f. juste après), soit l'écriture bien connue :
- Code: Tout sélectionner
int tab[] = {1, 2, 3, 4, 5};
Vous voulez maintenant récupérer les valeurs ! ATTENTION ! Il y a un piège là . En effet, en informatique, le 0 est une entité en elle-même. Ainsi la première colonne du tableau n'est pas la 1, mais la 0. On a donc :
- Code: Tout sélectionner
tab[0] == 1
tab[1] == 2
tab[4] == 5
tab[5] Error ArrayIndexOutOfBoundsException
Et oui, une des premières erreurs de la manipulation de tableau, c'est de sortir de celui-ci. L'index 5 n'existe pas ici, parce qu'il représente le 6ème élément du tableau, alors qu'il n'y a que 5 éléments. Pour éviter tout problème il y a la constante associée au tableau qui donne sa taille :
- Code: Tout sélectionner
tab.length == 5
Je vais faire une petite extrapolation aux tableaux multidimensionnels, que l'on rencontre un peu moins mais il faut les avoir vu. En effet, pour vous, je n'ai pas écrit encore ce qu'était un vrai tableau avec des lignes et des colonnes. Bah en programmation ça ne change pas grand chose, à l'intérieur des cellules d'un tableau à une dimension, on met un autre tableau.
- Code: Tout sélectionner
type variable[][] = new type[ lignes ][ colonnes ];
Et on a le remplissage par défaut :
int tab[][] = { {1,6,1,8,0} , {3,1,4,1,6}, {2,7,1,8,3} , {0,5,5,7,2}, {1,4,1,4,2} };
Ceux qui ont vu que ce tableau représente le développement décimal tronqué à l'ordre 4 de Phi, Pi, e, Gamma et racine de 2, gagnent ..... ma reconnaissance :D. On a donc :
- Code: Tout sélectionner
tab[1] <= tableau correspondant à Pi, int Pi[5] = {3,1,4,1,6}
tab[4][1] == 4
Evidemment on peut généraliser à une dimension quelconque, mais je n'ai jamais vu d'exemple de tableau à 3 dimensions déjà .
A quoi sert un ordinateur finalement ? A faire des choses que l'homme est incapable de faire seul (calculs ...), à faire une multitude de fois une chose qui lourde l'homme une fois déjà :D, ce qu'on est feignant quand même ! La notion de boucle intervient très très naturellement, c'est d'ailleurs le fondamental de l'algorithme, un procédé que l'on itère jusqu'à la fin du programme.
IV.1 Le tant-que / while
D'accord un procédé à itérer, mais on s'arrête quand ? Comment on contrôle tout ça ? C'est le but principal du while, à traduire "Tant que". Le principe c'est de faire un bloc d'instruction TANT QUE la condition est remplie.
- Code: Tout sélectionner
while( <condition> ) {
<instructions exécutées tant que <condition> est vraie>
}
Le principe y est très très simple. Mais voyons un exemple un peu plus concret. Prenons un exemple de calcul de moyenne.
- Code: Tout sélectionner
public static double moyenne(int notes) {
double total = 0;
int nb = notes;
while(nb-- > 0) {
String s = JOptionPane.showInputDialog(null, "Entrez une note :");
total += Integer.parseInt(s);
}
total /= notes;
return total;
}
Aha, on utilise là beaucoup de nouveaux termes. Le principe c'est de stocker le nombre de notes dans une nouvelle variable. A chaque rentrée de note, on la rajoute au total (tout va bien jusque là ), et on décrémente le nombre de note restantes (le fameux nb--). Si nb devient nul, on a fini ! Il ne nous reste plus qu'à diviser la sommes des notes par le nombre de notes, et hop, on a notre moyenne !
Seulement petit problème ... souvent quand on fait une moyenne on ne regarde pas combien on a de notes. Mmmh qu'à cela ne tienne ! On introduit break et continue, deux instructions telles que :
- Code: Tout sélectionner
while( ... ) {
...
break; // On sort complètement de la boucle
...
}
while( ... ) {
...
continue;
... // Le code ici n'est pas exécuté, mais on ne sort pas de la boucle.
}
Ca y est, on a de quoi faire notre programme de moyenne, non ? si si !
- Code: Tout sélectionner
public static double moyenne() {
double total = 0;
int nb = 0;
while(true) {
String s = JOptionPane.showInputDialog(null, "Entrez une note :");
if( s == null ) {
break;
}
total += Integer.parseInt(s);
nb++;
}
total /= nb;
return total;
}
Oula le vilain, il a mis un while(true), je sais c'est du truandage, mais au moins vous comprennez directement le sens utile du break, si vous appuyez sur Cancel (ou Annuler), vous aurez automatiquement le break qui va se déclancher arrétant ainsi le processus.
IV.2 Le pour / for
Le for, votre plus grand ami à partir de maintenant, et for par ci, et for par là ... Quel est son but ? Après tout, un while peut tout faire... certes mais le parcours d'un tableau par exemple, avec un while ... c'est bof, c'est lourd. Alors qu'un for, c'est fait pour ça !! Voyez donc la structure :
- Code: Tout sélectionner
for(int var = <init> ; <condition> ; <incrementation> ) {
<instructions>
}
Expliquons un peu, le for fournit à lui seul une variable que l'on aura initiée comme on le souhaite, et c'est sur cette variable que va se faire la boucle. A chaque passage de boucle, l'expression d'incrementation sera soulevée et var sera incrémentée (ou décrémentée). La condition peut (et c'est le cas général) dépendre de var et le principe est identique au while. Les instructions peuvent utiliser var en évitant de la modifier (si on la modifie, on commence à ne plus contrôler l'engin). Mais dès la fin du for, var n'existe plus, elle est détruite le GC est passé par là .
Voyons cela sur l'exemple classique de l'affichage de tous les éléments d'un tableau tab :
- Code: Tout sélectionner
for(int i = 0 ; i < tab.length ; i++) {
System.out.println(tab[i]);
}
équivalent en while
int i = 0;
while(i < tab.length) {
System.out.println(tab[i]);
i++;
}
// i existe toujours ici !
Voilà la différence majeure avec le while, c'est qu'à la fin du for, le i n'existe plus ! Et la surcharge de variable, je vous assure que ce n'est pas une partie de plaisir.
Bien évidemment, on peut imbriquer tant que l'on veut, mais attention le temps d'exécution sera très lent. Petit exemple très classique, le tracé de la table de multiplication :
- Code: Tout sélectionner
for(int i = 1 ; i <= 10 ; i++) {
for(int j = 1 ; j <= 10 ; j++) {
System.out.print((i*j)+"\t");
}
System.out.print("\n");
}
Sympathique n'est-ce pas ?
IV.3 Le faire-tant-que / do-while
Une petite alternative, je ne m'y attarde pas beaucoup du fait que les applications sont encore une fois rares (mais tout de même présente), il s'agit d'une dérivée du while, appelée le do-while. Regardons sa structure et comparons :
- Code: Tout sélectionner
do {
<instructions>
} while( <condition> );
Ici, le mot clef while et la condition se trouvent à la fin, ainsi, tout va se jouer sur la première boucle. Le while va vérifier la condition, et ensuite il exécutera, alors que le do-while, va d'abord faire tourner une fois la boucle, ensuite il verra s'il continue. J'ai des applications, mais elles ne sont soit pas assez concrètes, soit trop dûres pour être appelées ici. Mais c'est une structure qui peut tout de même dépanner au besoin.
Un dernier point sur lequel je m'attarde aujourd'hui, ce sont les exceptions. Lorsque l'on programme, des erreurs peuvent arriver, c'est naturel, imaginez que vous vous connectiez à un site alors que votre connection est débranchée, il y aura une erreur. Ce ne sera pas forcément la faute de l'ordinateur, ou vous qui ait mal manipuler, ces erreurs peuvent arriver très naturellement. D'ailleurs, Java n'appelle pas ça des erreurs, mais des Exceptions.
Par exemple, vous tentez une division à un moment par une variable fournie par l'utilisateur, il y a toujours le risque de diviser par 0, on ouvre donc un cas où la division par zéro est déclanchée.
Certaines fonctions requierent d'être mis dans une structure qui gère les exceptions, notament toutes les fonctions réseaux. Voyons maintenant la tête de la structure :
- Code: Tout sélectionner
try {
... // Code à "risque"
} catch ( <Variable d'exception> ) {
... // Code à exécuter en cas d'exception
}
Notez que l'on peut aligner les catch à la suite (évite de faire plusieurs try pour rien). Voyons notre cas de division par 0 :
- Code: Tout sélectionner
try {
a /= b;
} catch(ArithmeticException e) {
e.printStackTrace(); // Permet de fournir l'explication de l'exception
}
Bon recensont un peu les principales différentes exceptions, couraments rencontrés :
- Code: Tout sélectionner
- ArithmeticException : Problème lors d'une opération arithmétique, division par zéro
- ArrayIndexOutOfBoundsException : On est sorti d'un tableau
- NegativeArraySizeException : Lorsque l'on initie un tableau avec comme taille -1
- NullPointerException : On manipule un objet qui en fait est nul
- NumberFormatException : Généré par exemple par Integer.parseInt quand la chaîne n'est pas dans le bon format pour faire un nombre.
- Exception : La super-exception, elle se déclenche dès qu'il y a un problème de quelque ordre soit-il (très utilisé).
Donc adaptons la fonction somme du début, avec la division, en prenant compte de tout ce qui peut arriver.
- Code: Tout sélectionner
public static void division() {
double a, b, c;
String entree;
entree = JOptionPane.showInputDialog(null, "Entrez le premier réel :");
try {
a = Double.parseDouble(entree);
entree = JOptionPane.showInputDialog(null, "Entrez le deuxième réel :");
b = Double.parseDouble(entree);
c = a/b;
JOptionPane.showMessageDialog(null, "Voici le résultat : "+c);
} catch(NumberFormatException nfe) {
JOptionPane.showMessageDialog(null, "Il fallait rentrer un nombre, merci !", "Erreur", JOptionPane.ERROR_MESSAGE);
} catch(ArithmeticException ae) {
JOptionPane.showMessageDialog(null, "Tu sais que la division par 0, c'est pas beau ?", "Erreur", JOptionPane.ERROR_MESSAGE);
}
}
Et là , vous tentez une division par 0, et paf ! Il vous marque : Infinity. Ah l'enflure, il est très fort. Pourquoi ça marchait avant ? Tout simplement parce que le double supporte l'infini (ça fait mal ça). Pour savoir si un double est un infini, il y a une fonction qui dit ça c'est Double.isInfinite( double v ). Faites la même chose avec des entiers, vous aurez votre exception. Mais au fait pourquoi utiliser l'infini ? Dans l'algorithmie de graphes, lorsque la distance entre deux points qui ne sont pas rejoignable est assimilée à l'infini (et on utilise ses propriétés), d'où son utilitée.
Voila, j'ai fini (enfin) ! Je vous assure que ces deux premiers chapitres furent très durs à rédiger pour ma part puisque ce n'était que de la botanique (descriptif quoi). Les explications étant assez dures à fournir car ce n'est que la syntaxe, le fonctionnement n'est pas bien décrit pour l'instant, je suis prêt à répondre à toutes les questions ! Pour info, je ne connais pas TOUT par coeur, notament les exceptions, je suis allé voir dans la documentation officielle que TOUT programmeur Java doit avoir sur son ordi ! Vous pourrez retrouver tout le descriptif de l'API Java à cette adresse : http://java.sun.com/j2se/1.4.2/docs/api/index.html
Tout y est, c'est classé par package, je rappelle pour mémoire que les classes principales sont dans java.lang.
Le prochain chapitre se trouve un peu plus motivant ! Il s'intitulera "Rudiments d'objets". Je vous apprendrai le principe, l'utilité et la mise en place de la programmation objet, raison de vivre de Java.
Merci bien, à bientôt (je m'absente une semaine ...) !
Loïc WydD Petit


