Cours les bases du langage java, tutoriel & guide de travaux pratiques APPRENTISSAGE DU LANGAGE JAVA en pdf.
Classes et interfaces
L’objet par l’exemple
Généralités
Nous abordons maintenant, par l’exemple, la programmation objet. Un objet est une entité qui contient des données qui définissent son état (on les appelle des attributs ou propriétés) et des fonctions (on les appelle des méthodes). Un objet est créé selon un modèle qu’on appelle une classe :
public class C1{
type1 p1; // propriété p1
type2 p2; // propriété p2
…
type3 m3(…){ // méthode m3
…
}
type4 m4(…){
…
// méthode m4
}
…
}
A partir de la classe C1 précédente, on peut créer de nombreux objets O1, O2,… Tous auront les propriétés p1, p2,… et les méthodes m3, m4, … Ils auront des valeurs différentes pour leurs propriétés pi ayant ainsi chacun un état qui leur est propre.
Par analogie la déclaration
int i,j;
crée deux objets (le terme est incorrect ici) de type (classe) int. Leur seule propriété est leur valeur.
Si O1 est un objet de type C1, O1.p1 désigne la propriété p1 de O1 et O1.m1 la méthode m1 de O1.
Considérons un premier modèle d’objet : la classe personne.
Définition de la classe personne
La définition de la classe personne sera la suivante :
import java.io.*;
public class personne{
// attributs
private String prenom;
private String nom;
private int age;
// méthode
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
// méthode
public void identifie(){
System.out.println(prenom+ », »+nom+ », »+age);
}
}
Nous avons ici la définition d’une classe, donc un type de donnée. Lorsqu’on va créer des variables de ce type, on les appellera des objets. Une classe est donc un moule à partir duquel sont construits des objets.
Les membres ou champs d’une classe peuvent être des données ou des méthodes (fonctions). Ces champs peuvent avoir l’un des trois attributs suivants :
privé Un champ privé (private) n’est accessible que par les seules méthodes internes de la classe
public Un champ public est accessible par toute fonction définie ou non au sein de la classe protégé
Un champ protégé (protected) n’est accessible que par les seules méthodes internes de la classe ou d’un objet dérivé (voir ultérieurement le concept d’héritage).
En général, les données d’une classe sont déclarées privées alors que ses méthodes sont déclarées publiques. Cela signifie que l’utilisateur d’un objet (le programmeur) n’aura pas accès directement aux données privées de l’objet pourra faire appel aux méthodes publiques de l’objet et notamment à celles qui donneront accès à ses données privées.
La syntaxe de déclaration d’un objet est la suivante :
public class nomClasse{
private donnée ou méthode privée
public donnée ou méthode publique
protected donnée ou méthode protégée
}
Remarques
L’ordre de déclaration des attributs private, protected et public est quelconque.
La méthode initialise
Revenons à notre classe personne déclarée comme :
import java.io.*;
public class personne{
// attributs
private String prenom;
private String nom;
private int age;
// méthode
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
// méthode
public void identifie(){
System.out.println(prenom+ », »+nom+ », »+age);
}
}
Quel est le rôle de la méthode initialise ? Parce que nom, prenom et age sont des données privées de la classe personne, les instructions
personne p1;
p1.prenom= »Jean »;
p1.nom= »Dupont »;
p1.age=30;
sont illégales. Il nous faut initialiser un objet de type personne via une méthode publique. C’est le rôle de la méthode initialise. On écrira :
personne p1;
p1.initialise(« Jean », »Dupont »,30);
L’écriture p1.initialise est légale car initialise est d’accès public.
L’opérateur new
La séquence d’instructions
personne p1;
p1.initialise(« Jean », »Dupont »,30);
est incorrecte. L’instruction
personne p1;
déclare p1 comme une référence à un objet de type personne. Cet objet n’existe pas encore et donc p1 n’est pas initialisé. C’est comme si on écrivait :
personne p1=null;
où on indique explicitement avec le mot clé null que la variable p1 ne référence encore aucun objet.
Lorsqu’on écrit ensuite p1.initialise(« Jean », »Dupont »,30);
on fait appel à la méthode initialise de l’objet référencé par p1. Or cet objet n’existe pas encore et le compilateur signalera l’erreur.
Pour que p1 référence un objet, il faut écrire :
personne p1=new personne();
Cela a pour effet de créer un objet de type personne non encore initialisé : les attributs nom et prenom qui sont des références d’objets de type String auront la valeur null, et age la valeur 0. Il y a donc une initialisation par défaut. Maintenant que p1 référence un objet, l’instruction d’initialisation de cet objet p1.initialise(« Jean », »Dupont »,30); est valide.
Le mot clé this
Regardons le code de la méthode initialise :
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
L’instruction this.prenom=P signifie que l’attribut prenom de l’objet courant (this) reçoit la valeur P. Le mot clé this désigne l’objet courant : celui dans lequel se trouve la méthode exécutée. Comment le connaît-on ? Regardons comment se fait l’initialisation de l’objet référencé par p1 dans le programme appelant : p1.initialise(« Jean », »Dupont »,30);
C’est la méthode initialise de l’objet p1 qui est appelée. Lorsque dans cette méthode, on référence l’objet this, on référence en fait l’objet p1. La méthode initialise aurait aussi pu être écrite comme suit :
public void initialise(String P, String N, int age){
prenom=P;
nom=N;
this.age=age;
}
Lorsqu’une méthode d’un objet référence un attribut A de cet objet, l’écriture this.A est implicite. On doit l’utiliser explicitement lorsqu’il y a conflit d’identificateurs. C’est le cas de l’instruction :
this.age=age; où age désigne un attribut de l’objet courant ainsi que le paramètre age reçu par la méthode. Il faut alors lever l’ambiguïté en désignant l’attribut age par this.age.
Un programme de test
Voici un programme de test :
public class test1{
public static void main(String arg[]){
personne p1=new personne();
p1.initialise(« Jean », »Dupont »,30);
Classes et interfaces 32
p1.identifie();
}
}
La classe personne est définie dans le fichier source personne.java et est compilée :
E:\data\serge\JAVA\BASES\OBJETS\2>javac personne.java
E:\data\serge\JAVA\BASES\OBJETS\2>dir
10/06/2002 09:21 473 personne.java
10/06/2002 09:22 835 personne.class
10/06/2002 09:23 165 test1.java
Nous faisons de même pour le programme de test :
E:\data\serge\JAVA\BASES\OBJETS\2>javac test1.java
E:\data\serge\JAVA\BASES\OBJETS\2>dir
10/06/2002 09:21 473 personne.java
10/06/2002 09:22 835 personne.class
10/06/2002 09:23 165 test1.java
10/06/2002 09:25 418 test1.class
On peut s’étonner que le programme test1.java n’importe pas la classe personne avec une instruction :
import personne;
Lorsque le compilateur rencontre dans le code source une référence de classe non définie dans ce même fichier source, il recherche la classe à divers endroits :
dans les paquetages importés par les instructions import
dans le répertoire à partir duquel le compilateur a été lancé
Dans notre exemple, le compilateur a été lancé depuis le répertoire contenant le fichier personne.class, ce qui explique qu’il a trouvé la définition de la classe personne. Mettre dans ce cas de figure une instruction import provoque une erreur de compilation :
E:\data\serge\JAVA\BASES\OBJETS\2>javac test1.java
test1.java:1: ‘.’ expected
import personne;
^
1 error
Pour éviter cette erreur mais pour rappeler que la classe personne doit être importée, on écrira à l’avenir en début de programme :
classes importées
import personne;
Nous pouvons maintenant exécuter le fichier test1.class :
E:\data\serge\JAVA\BASES\OBJETS\2>java test1
Il est possible de rassembler plusieurs classes dans un même fichier source. Rassemblons ainsi les classes personne et test1 dans le fichier source test2.java. La classe test1 est renommée test2 pour tenir compte du changement du nom du fichier source :
paquetages importés import java.io.*;
class personne{
// attributs
private String prenom; // prénom de ma personne
private String nom; // son nom
private int age; // son âge
// méthode
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}//initialise
// méthode
public void identifie(){
System.out.println(prenom+ », »+nom+ », »+age);
}//identifie
}//classe
public class test2{
public static void main(String arg[]){
personne p1=new personne();
p1.initialise(« Jean », »Dupont »,30);
p1.identifie();
}
}
On notera que la classe personne n’a plus l’attribut public. En effer, dans un fichier source java, seule une classe peut avoir l’attribut public. C’est celle qui a la fonction main. Par ailleurs, le fichier source doit porter le nom de cette dernière. Compilons le fichier test2.java :
E:\data\serge\JAVA\BASES\OBJETS\3>dir
10/06/2002 09:36 633 test2.java
E:\data\serge\JAVA\BASES\OBJETS\3>javac test2.java
E:\data\serge\JAVA\BASES\OBJETS\3>dir
10/06/2002 09:36 633 test2.java
10/06/2002 09:41 832 personne.class
10/06/2002 09:41 418 test2.class
On remarquera qu’un fichier .class a été généré pour chacune des classes présentes dans le fichier source. Exécutons maintenant le fichier test2.class :
E:\data\serge\JAVA\BASES\OBJETS\2>java test2
Jean,Dupont,30
Par la suite, on utilisera indifféremment les deux méthodes :
classes rassemblées dans un unique fichier source
une classe par fichier source
Une autre méthode initialise
Considérons toujours la classe personne et rajoutons-lui la méthode suivante :
public void initialise(personne P){
prenom=P.prenom;
nom=P.nom;
this.age=P.age;
}
On a maintenant deux méthodes portant le nom initialise : c’est légal tant qu’elles admettent des paramètres différents. C’est le cas ici. Le paramètre est maintenant une référence P à une personne. Les attributs de la personne P sont alors affectés à l’objet courant (this). On remarquera que la méthode initialise a un accès direct aux attributs de l’objet P bien que ceux-ci soient de type private. C’est toujours vrai : les méthodes d’un objet O1 d’une classe C a toujours accès aux attributs privés des autres objets de la même classe C.
Voici un test de la nouvelle classe personne :
import personne; import java.io.*;
public class test1{
public static void main(String arg[]){
personne p1=new personne();
p1.initialise(« Jean », »Dupont »,30);
System.out.print(« p1= »);
p1.identifie();
personne p2=new personne();
p2.initialise(p1);
System.out.print(« p2= »);
p2.identifie();
}
}
et ses résultats :
p1=Jean,Dupont,30
p2=Jean,Dupont,30
Classes et interfaces 34
Constructeurs de la classe personne
Un constructeur est une méthode qui porte le nom de la classe et qui est appelée lors de la création de l’objet. On s’en sert généralement pour l’initialiser. C’est une méthode qui peut accepter des arguments mais qui ne rend aucun résultat. Son prototype ou sa définition ne sont précédés d’aucun type (même pas void).
Si une classe a un constructeur acceptant n arguments argi, la déclaration et l’initialisation d’un objet de cette classe pourra se faire sous la forme :
classe objet =new classe(arg1,arg2, … argn); ou
classe objet;
…
objet=new classe(arg1,arg2, … argn);
Lorsqu’une classe a un ou plusieurs constructeurs, l’un de ces constructeurs doit être obligatoirement utilisé pour créer un objet de cette classe. Si une classe C n’a aucun constructeur, elle en a un par défaut qui est le constructeur sans paramètres : public C(). Les attributs de l’objet sont alors initialisés avec des valeurs par défaut. C’est ce qui s’est passé lorsque dans les programmes précédents, on avait écrit :
personne p1;
p1=new personne();
Créons deux constructeurs à notre classe personne :
public class personne{
// attributs
private String prenom;
private String nom;
private int age;
// constructeurs
public personne(String P, String N, int age){
initialise(P,N,age);
}
public personne(personne P){
initialise(P);
}
// méthode
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
public void initialise(personne P){
this.prenom=P.prenom;
this.nom=P.nom;
this.age=P.age;
}
// méthode
public void identifie(){
System.out.println(prenom+ », »+nom+ », »+age);
}
}
Nos deux constructeurs se contentent de faire appel aux méthodes initialise correspondantes. On rappelle que lorsque dans un constructeur, on trouve la notation initialise(P) par exemple, le compilateur traduit par this.initialise(P). Dans le constructeur, la méthode initialise est donc appelée pour travailler sur l’objet référencé par this, c’est à dire l’objet courant, celui qui est en cours de construction.
Voici un programme de test :
// import personne;
import java.io.*;
public class test1{
public static void main(String arg[]){
personne p1=new personne(« Jean », »Dupont »,30);
System.out.print(« p1= »);
p1.identifie();
personne p2=new personne(p1);
System.out.print(« p2= »);
p2.identifie();
Classes et interfaces 35
}
}
et les résultats obtenus :
p1=Jean,Dupont,30
p2=Jean,Dupont,30
2.1.9 Les références d’objets
Nous utilisons toujours la même classe personne. Le programme de test devient le suivant :
import personne; import java.io.*;
public class test1{
public static void main(String arg[]){
// p1
personne p1=new personne(« Jean », »Dupont »,30);
System.out.print(« p1= »); p1.identifie();
p2 référence le même objet que p1 personne p2=p1; System.out.print(« p2= »); p2.identifie();
p3 référence un objet qui sera une copie de l’objet référencé par p1 personne p3=new personne(p1);
System.out.print(« p3= »); p3.identifie();
on change l’état de l’objet référencé par p1
p1.initialise(« Micheline », »Benoît »,67);
System.out.print(« p1= »); p1.identifie();
comme p2=p1, l’objet référencé par p2 a du changer d’état System.out.print(« p2= »); p2.identifie();
comme p3 ne référence pas le même objet que p1, l’objet référencé par p3 n’a pas du changer System.out.print(« p3= »); p3.identifie();
}
}
Les résultats obtenus sont les suivants :
p1=Jean,Dupont,30
p2=Jean,Dupont,30
p3=Jean,Dupont,30
p1=Micheline,Benoît,67
p2=Micheline,Benoît,67
p3=Jean,Dupont,30
Lorsqu’on déclare la variable p1 par
personne p1=new personne(« Jean », »Dupont »,30);
p1 référence l’objet personne(« Jean », »Dupont »,30) mais n’est pas l’objet lui-même. En C, on dirait que c’est un pointeur, c.a.d. l’adresse de l’objet créé. Si on écrit ensuite :
p1=null
Ce n’est pas l’objet personne(« Jean », »Dupont »,30) qui est modifié, c’est la référence p1 qui change de valeur. L’objet personne(« Jean », »Dupont »,30) sera « perdu » s’il n’est référencé par aucune autre variable.
Lorsqu’on écrit :
personne p2=p1;
on initialise le pointeur p2 : il « pointe » sur le même objet (il désigne le même objet) que le pointeur p1. Ainsi si on modifie l’objet « pointé » (ou référencé) par p1, on modifie celui référencé par p2.
Lorsqu’on écrit :
personne p3=new personne(p1);
il y a création d’un nouvel objet, copie de l’objet référencé par p1. Ce nouvel objet sera référencé par p3. Si on modifie l’objet « pointé » (ou référencé) par p1, on ne modifie en rien celui référencé par p3. C’est ce que montrent les résultats obtenus.
Classes et interfaces 36
Les objets temporaires
Dans une expression, on peut faire appel explicitement au constructeur d’un objet : celui-ci est construit, mais nous n’y avons pas accès (pour le modifier par exemple). Cet objet temporaire est construit pour les besoins d’évaluation de l’expression puis abandonné. L’espace mémoire qu’il occupait sera automatiquement récupéré ultérieurement par un programme appelé « ramasse-miettes » dont le rôle est de récupérer l’espace mémoire occupé par des objets qui ne sont plus référencés par des données du programme.
Considérons l’exemple suivant :
// import personne;
public class test1{
public static void main(String arg[]){
new personne(new personne(« Jean », »Dupont »,30)).identifie();
}
}
et modifions les constructeurs de la classe personne afin qu’ils affichent un message :
// constructeurs
public personne(String P, String N, int age){
System.out.println(« Constructeur personne(String, String, int) »);
initialise(P,N,age);
}
public personne(personne P){
System.out.println(« Constructeur personne(personne) »);
initialise(P);
}
Nous obtenons les résultats suivants :
Constructeur personne(String, String, int)
Constructeur personne(personne)
Jean,Dupont,30
montrant la construction successive des deux objets temporaires.
Méthodes de lecture et d’écriture des attributs privés
Nous rajoutons à la classe personne les méthodes nécessaires pour lire ou modifier l’état des attributs des objets :
public class personne{
private String prenom;
private String nom;
private int age;
public personne(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
1. LES BASES DU LANGAGE JAVA
1.1 INTRODUCTION
1.2 LES DONNEES DE JAVA
1.3 LES INSTRUCTIONS ELEMENTAIRES DE JAVA
1.4 LES INSTRUCTIONS DE CONTROLE DU DEROULEMENT DU PROGRAMME
1.5 LA STRUCTURE D’UN PROGRAMME JAVA
1.6 LA GESTION DES EXCEPTIONS
1.7 COMPILATION ET EXECUTION D’UN PROGRAMME JAVA
1.8 ARGUMENTS DU PROGRAMME PRINCIPAL
1.9 PASSAGE DE PARAMETRES A UNE FONCTION
1.10 L’EXEMPLE IMPOTS
2. CLASSES ET INTERFACES
2.1 L’ OBJET PAR L’EXEMPLE
2.2 L’HERITAGE PAR L’EXEMPLE
2.3 CLASSES INTERNES
2.4 LES INTERFACES
2.5 CLASSES ANONYMES
2.6 LES PAQUETAGES
2.7 L’EXEMPLE IMPOTS
3. CLASSES D’USAGE COURANT
3.1 LA DOCUMENTATION
3.2 LES CLASSES DE TEST
3.3 LA CLASSE STRING
3.4 LA CLASSE VECTOR
3.5 LA CLASSE ARRAYLIST
3.6 LA CLASSE ARRAYS
3.7 LA CLASSE ENUMERATION
3.8 LA CLASSE HASHTABLE
3.9 LES FICHIERS TEXTE
3.10 LES FICHIERS BINAIRES
3.11 UTILISER LES EXPRESSION REGULIERES
3.12 EXERCICES
4. INTERFACES GRAPHIQUES
4.1 LES BASES DES INTERFACES GRAPHIQUES
4.2 CONSTRUIRE UNE INTERFACE GRAPHIQUE AVEC JBUILDER
4.3 BOITES DE DIALOGUE
4.4 BOITES DE SELECTION
4.5 L’APPLICATION GRAPHIQUE IMPOTS
4.6 ECRITURE D’APPLETS
4.7 L’APPLET IMPOTS
4.8 CONCLUSION
4.9 JBUILDER SOUS LINUX
5. GESTION DES BASES DE DONNEES AVEC L’API JDBC
5.1 GENERALITES
5.2 LES ETAPES IMPORTANTES DANS L’EXPLOITATION DES BASES DE DONNEES
5.3 IMPOTS AVEC UNE BASE DE DONNEES
5.4 EXERCICES
6. LES THREADS D’EXECUTION
6.1 INTRODUCTION
6.2 CREATION DE THREADS D’EXECUTION
6.3 INTERET DES THREADS
6.4 UNE HORLOGE GRAPHIQUE
6.5 APPLET HORLOGE
6.6 SYNCHRONISATION DE TACHES
7. PROGRAMMATION TCP-IP
7.1 GENERALITES
7.2 GESTION DES ADRESSES RESEAU EN JAVA
7.3 COMMUNICATIONS TCP-IP
7.4 APPLICATIONS
7.5 EXERCICES
8. JAVA RMI
8.1 INTRODUCTION
8.2 APPRENONS PAR L’EXEMPLE
8.3 DEUXIEME EXEMPLE : SERVEUR SQL SUR MACHINE WINDOWS
8.4 EXERCICES
9. CONSTRUCTION D’APPLICATIONS DISTRIBUEES CORBA
9.1 INTRODUCTION
9.2 PROCESSUS DE DEVELOPPEMENT D’UNE APPLICATION CORBA
9.3 EXEMPLE 2 : UN SERVEUR SQL
9.4 CORRESPONDANCES IDL – JAVA