Révision des cours précédents


Avant d'aller plus loin, il est temps de revoir les cours précédents à la lumière de la programmation objet. Pour des raisons pédagogiques, nous avons tout d'abord abordé la programmation Java en utilisant les concepts de la programmation procédurale. De ce fait, nous avons été obligé de mentir un peu sur certains points ou d'éluder certaines explications. Commencons par le premier cours (Premières Notions).

La classe fenêtre

Revenons sur le code initial généré par NetBeans. Nous avons vu (voir notice de NetBeans) que lorsque vous ajoutez une fenêtre à une application Java, NetBeans vous demande le nom d'une classe. Cette classe est une classe qui représente la nouvelle fenêtre et dont la déclaration est générée automatiquement par NetBeans. Nous l'appellerons par la suite classe fenêtre.

Par exemple, si l'on choisit le nom Addition comme nom de classe, on obtient ceci:

CodeInitial.jpg, 44kB

Ce code utilise de nombreux concepts que nous n'avons pas encore abordés. Pour l'instant, il doit donc vous apparaitre très obscur. Mais en observant sa structure générale, vous reconnaitrez grosso modo la déclaration d'une classe nommée Addition avec un constructeur et une méthode appelée main.

La classe fenêtre est automatiquement associée à un fichier de même nom avec l'extension ".java". Donc dans l'exemple, ci-dessus, ce sera le fichier Addition.java.

Dans la majorité des exercices que vous avez vus jusqu'ici, vous n'aviez pas à donner le nom de la classe fenêtre, puisque vous partiez d'un projet dont l'interface graphique était déjà réalisée. Mais la déclaration de la classe fenêtre a toujours été présente et le code que vous avez écrit se situait donc toujours à l'intérieur de cette classe.

Variables globales et sous-programmes

En particulier, ce que nous avons appelé zone de déclaration, c'est à dire l'endroit où nous avons déclarés nos 'variables globales' et nos 'sous-programmes', se situe entièrement à l'intérieur de la classe fenêtre.

Cela a deux conséquences:

Composants et procédures évènementielles

Les composants en tant qu'objets

Revenons à l'exercice de prise en main de NetBeans. Dans cet exercice nous avions construit la fenêtre de l'application Addition. Avant de renommer explicitement les composants de la fenêtre, leurs noms par défaut étaient les suivants:

Composants-Noms-Par-Defaut.jpg, 53kB

Chaque type de composant d'une fenêtre est représenté par une classe: il y a par exemple la classe JButton pour les boutons, la classe JLabel pour les étiquettes et la classe JTextField pour les champs de texte.

Dans notre exemple, nous avons donc

Chaque fois que vous ajoutez un composant, Netbeans l'insère dans la déclaration de la classe fenêtre en tant qu'attribut. Après avoir placé les septs composants définis précédemment, on obtient le code suivant:

Code-Initial-Avec-Composants.jpg, 59kB

Les sept lignes de code commencant par private javax.swing déclarent les composants de la fenêtre. Que signifie private ? que signifie javax.swing ? Nous répondrons à ces questions plus tard. En attendant, je vous demanderait d'admettre que la ligne de code:

 private javax.swing.JButton jButton1;

est simplement la déclaration d'un attribut de la classe fenêtre: cet attribut est nommé jButton1 et son type est JButton. A votre niveau de connaissance actuel, vous pouvez donc voir cette ligne de code comme:

  JButton jButton1;

et de même pour les six autres déclarations de composants.

Les procédures évènementielles en tant que méthodes

Rappelons nous que la déclaration d'une procédure évènementielle est automatiquement générée par NetBeans. On remarque qu'elle figure toujours à l'intérieur de la classe fenêtre. Il s'agit donc d'une méthode de cette classe et non pas d'un sous-programme.

La classe String

En Java, tout est représenté par des classes, mis à part les types dit primitifs (par exemple int, double et boolean) que l'on peut assimiler aux types de la programmation procédurale.

Nous avons considérés jusqu'ici String comme un type primitif. En fait, il s'agit d'une classe et ceci a des conséquences importantes.

Déclaration d'une chaine de caractères

Lorsque vous déclarez une chaine de caractères en Java, vous déclarez en fait une variable susceptible de contenir l'adresse d'une chaine de caractères. Par exemple:

   String nom;

Cette déclaration ne réserve pas une place mémoire pour contenir les caractères de la variable nom, mais uniquement son adresse !

Initialisation d'une chaine de caractères

Lorsque vous affectez un littéral de type chaine de caractères à une variable, vous lui affectez en fait l'adresse d'un nouvel objet de la classe String. Par exemple, l'instruction:

 nom = "Thirion";

affecte en réalité à la variable nom, l'adresse d'un nouvel objet de la classe String, qui contient les caractères de la chaine "Thirion".

Signalons au passage que la classe String possède un constructeur dont le paramètre est la valeur de la nouvelle chaine. L'instruction précédente peut donc être considérée comme un raccourci d'écriture de:

 nom = new String ("Thirion");
Valeur par défaut des chaines de caractères

Une chaine de caractères étant une référence à un objet, sa valeur par défaut est null et non pas la chaine vide !

Egalité de chaines de caractères

Supposons que l'on affecte à deux variables de type String la même valeur. Par exemple:

   String nom1="thirion", nom2="thirion";

alors l'expression booléenne nom1 == nom2 sera égale à false.

Ce résultat supprenant au premier abord est du au fait que nom1 et nom2 contiennent les adresses de deux objets de la classe String. L'opérateur == compare donc les adresses de ces deux objets et non pas leurs valeurs. Comme il s'agit d'objets distincts, ils seront stockés à des adresses distinctes dans le tas. Le résultat de la comparaison est donc false.

Pour comparer les valeurs de deux chaines, if faut utiliser la méthode equals de la classe String. Par exemple, pour comparer nom1 et nom2 dans un if, on pourra écrire:

if (nom1.equals(nom2))... 
Ordre lexicographique

L'ordre lexicographique est une généralisation de l'ordre alphabétique dans laquelle on considère non seulement les lettres de l'alphabet, mais tous les caractères représentés dans un ordinateur.

La logique de cet ordre est la même que celle de l'ordre alphabétique, en utilisant l'ordre des caractères de la table ASCII.

Pour comparer des chaines de caractères selon cet ordre, la classe String dispose de la méthode compareTo. C'est le signe du nombre entier retourné par cette méthode qui permet de conclure.

Plus précisement, soient ch1 et ch2 deux variables de type String, il y a trois cas à distinguer:

Exemple

L'exemple de projet présenté ci-dessous se trouve dans le dossier Exemple-Java-ProgObjet1/OrdreLexico.

CompareToNegatif.jpg, 19kB
Autres méthodes de la classe String

Voici quelques unes des méthodes de la classe String. Ce document n'est pas exhaustif. Si vous souhaitez plus d'informations, je vous conseil le livre de Claude Delannoy.

Les tableaux

En Java, les tableaux sont des objets d'un genre un peu particulier. Il n'existe pas de classe représentant les tableaux. Mais la création d'un tableau utilise le même principe que l'instanciation d'un objet, c'est à dire l'allocation dynamique de mémoire. C'est la raison pour laquelle il est nécessaire d'utiliser l'opérateur new pour créer un tableau.

Déclaration d'un tableau

Revenons sur la déclaration d'un tableau. Pour déclarer un tableau N de dix entiers, nous avions jusqu'ici procédé de la manière suivante:

 int N[] = new int [10];

En fait, cette ligne de code n'est pas une simple déclaration. On pourrait la décomposer en deux comme suit:

 int N[];
 N[] = new int [10];

La première ligne peut être assimilée à la déclaration d'un objet de la classe 'tableau d'entiers'. N[] est donc utilisé pour mémoriser l'adresse de tableau et non pas son contenu.

La deuxième ligne de code alloue dynamiquement de l'espace mémoire dans le tas: l'espace nécessaire pour mémoriser les dix éléments du tableau. L'opérateur new retourne l'adresse de cette plage mémoire qui est ensuite stockée dans N[].

Initialisation explicite

Lorsque vous initialisez explicitement un tableau en précisant toutes les valeurs des éléments, il y a également allocation dynamique de mémoire, bien que l'appel de l'opérateur new soit absent. Par exemple, l'instruction:

 int N[];
 N[] = {6, 6, 1961};

alloue dynamiquement une plage mémoire suffisante pour mémoriser trois entiers, stocke les valeurs 6, 6 et 1961 dans ces entiers, puis l'adresse de la plage mémoire dans N[]. Bien que l'opérateur new soit absent dans cette ligne de code, l'allocation dynamique de mémoire a bien lieu.

L'attribut length

La "classe tableau" possède un attribut nommé length. Il contient le nombre d'éléments d'un tableau. Cet attribut pourra par exemple vous être utile pour retrouver le nombre d'éléments d'un tableau initialisé explicitement.

Exemple:

 String Mot[]= 
 {"a","n","t","i","c","o","n","s","t","i","t","u","t","i",
 "o","n","n","e","l","l","e","m","e","n","t"};
 
 es.AfficherMessage("Il y a "+ Mot[].length
                    +" lettres dans ce mot");