Page d'accueilFindIt !ContactDémarrer en JavaCréation et utilisation des classes

Le langage JavaTM

Table des matièresHierarchie des classes

CJava

 Les notions de base

Objets, classes et héritage
Références
Les mots-clés de Java
Types primitifs
Structure d'un programme
Les packages

 

Objets, classes et héritage

Qu'entend on par langage objet, ou plus exactement langage orienté objet ? Le propos qui suit, n'est que la nième réponse, qui complétera une littérature déjà vaste sur le sujet. Cette explication rapide vous permettra de vous rappeler le vocabulaire couramment utilisé en programmation orientée objet.
L'exemple donné décrit un cas typique de programmation (gestion de compte en banque), plus parlant que des cas qui peuvent paraître plus abstraits (les catégories de voitures, les espèces animales, etc...).
Le meilleur moyen d'expliquer la programmation objet passe en effet par l'exemple, car elle essaye de s'appuyer sur l'habitude qu'a l'homme, de classer les objets du monde qui l'entourent en catégories, sous-catégories, et ainsi de suite...

Il faut faire la différence entre la programmation objet et un langage objet : Un langage objet tel que Java, Small Talk ou C++, est le moyen le plus aisé et le plus rapide de "programmer objet", mais la programmation objet est plus un style de programmation que peut respecter un programme écrit en C ou en PASCAL (plus difficilement tout de même !).

Mettons-nous donc à la place d'une banque : elle a des comptes à gérer, le votre, le mien et des milliers d'autres. Tous ces comptes ont en commun un certain nombre de caractéristiques communes que vous retrouvez sur votre relevé : un numéro et un solde, au minimum (même pas forcément une identité pour certains comptes).
Le banquier va donc créer un modèle de relevé avec les cases Numéro et Solde. L'imprimeur va en tirer des centaines d'exemplaires pour que le banquier puisse les remplir avec les informations de ses clients.
Le banquier gère aussi des comptes qui comportent d'autres informations dont il veut garder la trace. Il n'a pas besoin de créer un modèle entièrement nouveau : à partir de son premier modèle, il crée d'autres modèles de relevés : un pour les comptes de dépôts où il ajoute n cases Opération, un autre pour les comptes d'épargne où il ajoute une case Taux d'intérêts :

Objets relevés de compte
Figure 2. Des objets "relevés de compte"

Notre banquier décide de s'informatiser, et engage un informaticien expert dans la technologie magique dont il entend parler dans tous les journaux : "les technologies objets".
Celui-ci lui explique sa démarche : "Pour un informaticien, chaque relevé que vous imprimez est un objet, chaque modèle que vous avez créé est une classe, et le premier modèle que vous avez créé est une classe dont les modèles suivants ont hérité. Les cases Numéro, Solde, Opération, Taux d'Intérêt sont des champs qui permettent de mémoriser l'état courant d'un compte et le solde se calcule grâce à une méthode".
Mais le banquier, pas dupe, lui répond : "Je ne veux pas vous acheter un dictionnaire !... Tout ça ne sont que de nouveaux mots. Quelle est la vraie différence avec d'autres technologies classiques et éprouvées ?".
Aïe, aïe, expliquer la différence sans terme trop technique et fumeux ?!? "Une classe est une entité qui forme un tout : chaque objet qui est une instance (désolé encore un nouveau mot !) d'une classe comporte un ensemble de champs qui décrivent son état ET un ensemble de méthodes qui permettent de le modifier : on appelle ceci l'encapsulation. L'héritage vous permet de créer de nouvelles classes dérivées d'anciennes dont elle garde ou modifie les caractéristiques, sans que vous ayez à retoucher vos anciennes classes. Convaincu ?" (Espérons-le...)

Les liens d'héritage définis entre les différentes classes d'un modèle définissent un graphe d'héritage ou une hiérarchie de classes :

Graphe d'héritage
Figure 3. Graphe d'héritage

CompteDepot est une classe dérivée de Compte. Compte est la "super classe" de CompteEpargne et CompteDepot, et CompteEpargne est la super classe de PEL. L'application Banque décrite au chapitre suivant s'inspire du modèle décrit ici.

La différence principale entre une structure C et une classe est évidente : on ne peut pas déclarer des méthodes (ou des fonctions) à l'intérieur du corps d'une structure C. A l'opposé, Java ne permet pas de déclarer de méthodes en dehors du corps d'une classe.
Une classe peut comporter uniquement des champs sans méthodes ; elle peut aussi n'avoir que des méthodes sans déclarer de champ.

!

L'héritage est différent de la composition : en C, vous pouvez créer une structure Compte et une structure CompteEpargne, utilisant la première :

typedef struct
{
  int   numero;
  float solde;
} Compte;
 
typedef struct
{
  Compte compte;
  float  tauxInterets;
} CompteEpargne;
 
...
CompteEpargne unCompte;
unCompte.compte.numero = 1;
/* Pour accéder au numero vous passez par le champ */
/* compte de CompteEpargne                         */

En Java, vous pouvez utilisez la composition comme en C. Par contre, grâce à l'héritage, tous les champs et méthodes hérités sont accessibles directement comme s'ils avaient été déclarés par la classe dérivée elle-même.
De toute façon, ne confondez pas l'héritage et la composition : Bien que l'héritage soit une caractéristique d'un langage objet, il ne faut pas se précipiter pour l'utiliser. Vous utiliserez sûrement bien plus souvent la composition (en créant des classes qui sont l'assemblage de différents composants) et à part pour les classes d'applets, la plupart de vos premières classes n'hériteront pas d'autres classes.
Pour vous en convaincre, vous n'avez qu'à étudier la hiérarchie des classes de la bibliothèque Java, et vous verrez que la plupart des classes n'héritent pas les unes des autres.
L'héritage sert le plus souvent quand on veut modifier le comportement par défaut de classes existantes (par exemple, modifier le comportement de la classe Applet), ou quand vous avez besoin d'ajouter des fonctionnalités à une classe existante et que vous ne voulez pas modifier celle-ci parce qu'elle est déjà utilisée (par exemple, le compteur de temps dérive d'un afficheur digital statique).

Références

La notion de référence est fondamentale en Java. La différence avec la notion de pointeur en C est faible, mais essentielle :
Les variables (champs, paramètres ou variables locales) en Java sont soit d'un type primitif (byte, short, int, long, float, double, char ou boolean), soit des références désignant des objets. Comme les pointeurs en C, ces références sont comparables à des adresses mémoires permettant de désigner un objet alloué dynamiquement. Un même objet peut être référencé par plusieurs variables à un moment donné.
Par contre, la comparaison s'arrête ici : en effet, en C un pointeur peut désigner un type primitif (int* par exemple), ou un autre pointeur (char** par exemple). Java lui, ne permet pas de déclarer une variable qui serait une référence sur un type primitif ou une référence sur une autre référence. Tout objet ne peut être créé que dynamiquement grâce à l'opérateur new : ClasseObjet unObjet; ne crée aucun objet en Java mais une référence sur un objet de classe ClasseObjet.

C

La notion de pointeur du C est remplacée par la notion de référence en Java, proche de celle du C++ mais limitée aux variables désignant des objets alloués dynamiquement.

C

En C/C++, on utilise souvent une convention d'écriture pour les noms de variables permettant de distinguer les variables qui sont des pointeurs et celles qui n'en sont pas,... comme par exemple :

struct Point
{
  int x, y;
};
 
struct Point  point1,     // point1 est une variable de type Point et n'est pas un pointeur
             *ptrPoint2;  // ptrPoint2 est un pointeur sur Point
int    entier,            // entier est une variable entière du type primitif int
      *ptrEntier;         // ptrEntier est un pointeur sur int

Comme en Java il n'est possible de déclarer que des références comparables à ptrPoint2 ou des variables d'un type primitif comparables à entier, il n'est pas utile d'utiliser un qualificatif dans le nom des variables qui permet de rappeler qu'une variable est une référence. En général, on écrit directement point2.


Il ne faut pas voir la notion de référence comme une limitation de Java par rapport au C, mais plutôt comme une simplification de la programmation : La seule chose réellement perdue est l'arithmétique de pointeurs du C, par contre le gain en sécurité d'accès à la mémoire est important, car une référence ne peut avoir pour valeur que null ou l'adresse valide d'un objet.

C

Les opérateurs * et & n'existent pas en Java. Le seul opérateur d'accès aux champs et aux méthodes d'un objet est le point (.), et les opérateurs -> et :: du C++ sont absents. Tout ceci simplifie grandement la manipulation des adresses.


La création d'objet et leur manipulation sont décrites au chapitre traitant de la création des classes.

Les mots-clés de Java

abstract

default

if

private

throw

boolean

do

implements

protected

throws

break

double

import

public

transient

byte

else

instanceof

return

try

case

extends

int

short

void

catch

final

interface

static

volatile

char

finally

long

super

while

class

float

native

switch

const

for

new

synchronized

continue

goto

package

this


Les liens définis dans le tableau indiquent les endroits où sont utilisés le mot-clé pour la première fois. Java 1.4 a introduit le mot clé assert, et Java 5.0 le mot clé enum.

C

goto et const sont des mots-clés qui sont réservés mais non utilisés par Java ; ils permettent notamment au compilateur de vérifier qu'en cas de portage d'un programme écrit en C vers Java, des mots-clés du C n'ont pas été oubliés...

C

null est une valeur utilisée pour les références inconnues et qui ne désignent aucun objet. Il ne s'écrit pas en majuscules comme en C.

Les mots-clés du C/C++ absent en Java

auto

extern

register

typedef

#define

friend

sizeof

union

delete

inline

struct

unsigned

enum (sauf Java 5.0)

operator

template

virtual

Les liens définis dans le tableau désignent les endroits où il est traité de la disparition de ces mots-clés.

Types primitifs

Applet ScrollText

TYPE

DESCRIPTION

VALEUR PAR DEFAUT

byte

Entier signé occupant 8 bits (valeurs de -128 à 127)

0

short

Entier signé occupant 16 bits (valeurs de -32768 à 32767)

0

int

Entier signé occupant 32 bits (valeurs de -2147483648 à 2147483647)

C

Le type int occupe toujours la même taille en Java : 32 bits.

0

long

Entier signé occupant 64 bits (valeurs de -9223372036854775808 à 9223372036854775807)

C

Le type long occupe 64 bits en Java contrairement à 32 bits en C.

0L

float

Nombre à virgule flottante occupant 32 bits (norme IEEE 754)

0.0f

double

Nombre à virgule flottante occupant 64 bits (norme IEEE 754)

0.0d

char

Caractère Unicode occupant 16 bits (valeurs littérales de '\u0000' à '\uffff' avec 4 chiffres hexadécimaux obligatoires après \u).
Les 128 premiers caractères sont les codes ASCII et se notent comme en C, entre '' ('a', '1',...). Voici la liste des caractères compris entre '\u0080' et '\u00ff', qui contient notamment les caractères accentués français :

Applet Unicode

Méfiez-vous car la plupart des éditeurs de texte ne génèrent pas à la compilation la bonne valeur Unicode pour ces caractères. Utilisez alors les valeurs hexadécimales ('\u00e9' au lieu de 'é' par exemple).

C

Le type char occupe 16 bits en Java contrairement à 8 bits en C. Utilisez byte pour une valeur sur 8 bits. Les valeurs littérales des caractères spéciaux sont les mêmes : '\n' pour un saut de ligne, '\t' pour une tabulation, '\'' pour le caractère ', '\"' pour le caractère ", '\\' pour le caractère \,...

'\u0000'

boolean

Booléen dont la valeur est true ou false (vrai ou faux).

C

Le type boolean n'existe pas en C. En Java, il est obligatoire dans certaines expressions (if (...) par exemple).

false

Les variables de type float et double peuvent prendre aussi des valeurs correspondant à l'infini positif ou négatif, ou représentant une valeur non significative. Voir les classes Float et Double.
Les valeurs littérales entières (byte, short, int et long) peuvent se noter de trois façons :

C

Le modificateur unsigned du C n'existe pas en Java, où tous les types entiers sont signés. Comme en C, les entiers peuvent prendre pour valeur des littéraux notés sous forme décimale (i=10) , hexadécimale (i=0x0A) ou octale (i=012).


Chacun des types primitifs Java occupe toujours la même place mémoire quelque soit la plate-forme d'exécution. La taille d'un entier de type int est toujours de 32 bits (ou 4 octets).

Les opérateurs qui s'appliquent à chacun des types primitifs sont étudiés au chapitre sur les instructions et les opérateurs.

!

Une valeur littérale d'un nombre à virgule flottante sans l'extension f ou d, comme par exemple 10.2 est considérée de type double.

Structure d'un programme

La structure d'un programme Java est plus simple qu'en C.
Chaque fichier qui le compose, se divise en trois parties (optionnelles) :

/* Début du fichier NouvelleClasse.java */
 
/* 1. Une éventuelle déclaration de package */
package nomPackage;
 
/* 2. Zéro ou plusieurs import */
import nomClasse;              // Importer une classe sans package
import nomPackage.nomClasse;   // Importer une classe d'un package
import nomPackage.*;           // Importer toutes les classes d'un package
 
/* 3. Déclarations des classes et des interfaces du fichier */
public class NouvelleClasse     // Une seule classe ou interface déclarée public,
{                               // qui porte le même nom que le fichier
  // Corps de NouvelleClasse
}
 
class NouvelleClasse2
{
  // Corps de NouvelleClasse2
}
 
interface NouvelleInterface
{
  // Corps de NouvelleInterface
}
 
// ...
 
/* Fin du fichier NouvelleClasse.java */

Les packages sont comparables à des sous-répertoires et sont traités au paragraphe suivant.

Les classes d'un même package peuvent s'utiliser mutuellement sans avoir à utiliser une clause import : Si, par exemple, vous créez deux fichiers Classe1.java et Classe2.java déclarant respectivement les classes Classe1 et Classe2, vous pouvez utiliser directement Classe2 dans le fichier Classe1.java.

C

Les commentaires s'écrivent de la même manière en Java qu'en C++ :

  • Tout ce qui suit // est ignoré jusqu'à la fin de la ligne.
  • Tout ce qui est compris entre /* et */ est ignoré. Ces commentaires peuvent incorporer des commentaires écrits avec la syntaxe précédente //, mais pas d'autres commentaires avec la syntaxe /* */.
    Il est conseillé d'utiliser d'abord les commentaires avec // pour permettre d'imbriquer ce type de commentaire, dans ceux utilisant /* */ au cas où vous ayez de besoin de commenter tout un bloc. Par exemple :
    class Classe1
    {
      /* Bloc inutilisé
      int x = 1; // x sert à ...
      */
     
      // ... 
    }
  • Il existe un troisième type de commentaire utilisant la syntaxe précédente : Les commentaires javadoc. javadoc est une commande qui utilise tous les commentaires compris entre /** et */ et respectant une syntaxe spéciale pour générer automatiquement une documentation des classes. Toute la documentation des classes fournie par Javasoft est créée grâce à cet outil.

C

Java est un langage "pur" objet et ne permet de définir au niveau global qu'UNIQUEMENT des classes ou des interfaces : Pas de constantes, ni de macros (#define du C), pas de variables globales qu'elles soient statiques ou non, pas de types autres que des classes (typedef est inutile), et toutes les fonctions ne peuvent être déclarées que comme méthodes appartenant à une classe.
C'est la raison pour laquelle vous verrez que tous les exemples donnés déclarent des classes qui sont souvent inutiles pour expliciter tel ou tel problème, mais obligatoires pour que l'exemple puisse être compilé.

Les packages

import

Les classes fournies avec le Java Development Kit ou par d'autres sources sont rangées dans des packages (ou paquets si vous préférez), comparables à des groupes rassemblant les classes par thème. Dans un fichier .java, vous devez indiquer à quels packages appartiennent les classes que vous utilisez. La clause import permet de spécifier ces packages pour chacune des classes ou pour chaque groupe de classes. Ces clauses se placent en début de fichier avant la déclaration de la première classe ou interface du fichier :

import nomClasse;            // Importer une classe sans package
import nomPackage.nomClasse; // Importer une classe d'un package
import nomPackage.*;         // Importer toutes les classes d'un package

import est suivi soit directement du nom d'une classe, soit du nom d'un package, suivi lui-même du nom d'une classe ou d'un astérisque (*). L'astérisque permet d'importer les classes d'un package à la demande, c'est-à-dire que quand le compilateur recherchera une classe Classe1 qu'il ne connaît pas encore, il cherchera notamment dans les packages suivis d'un astérisque si Classe1 existe.
La classe nomClasse peut correspondre soit à un fichier source nomClasse.java, soit à un fichier compilé nomClasse.class, dans lequel est définie la classe public à importer.
Un package représente une arborescence indiquant au compilateur quel chemin il faut emprunter pour retrouver la classe. Par exemple, si le package est java.util, il va effectuer sa recherche dans le répertoire java/util (ou java\util sous Windows). Mais où est ce répertoire java/util ? Vous ne trouverez sûrement pas sur votre disque dur de répertoire java à la racine, et non plus dans le répertoire courant où vous écrivez vos programmes... Comme la plupart des langages, le compilateur Java utilise une variable système d'environnement indiquant l'ensemble des chemins prédéfinis à utiliser avec un package pour construire le chemin complet d'accès aux classes : Sous UNIX et Windows, cette variable s'appelle CLASSPATH. Vous pouvez aussi utiliser l'option -classpath avec les commandes javac et java, pour spécifier ce chemin.
Vous pouvez modifier cette variable pour y ajouter le chemin d'accès à d'autres bibliothèques Java ou à vos propres packages, que vous créerez (les environnements de développement plus complets permettent d'ajouter ces chemins plus simplement).
Le chemin correspondant à un package est donc un chemin relatif construit à partir du répertoire courant de compilation ou aux chemins cités dans la variable d'environnement CLASSPATH.

import est optionnel dans les cas suivants :


Pour utiliser une classe nomClasse d'un package nomPackage, vous avez donc le choix entre ces trois options :

    1. Utiliser import nomPackage.nomClasse; au début du fichier et écrire nomClasse ensuite.
    2. Utiliser import nomPackage.*; au début du fichier et écrire nomClasse ensuite.
    3. Ecrire nomPackage.nomClasse à chaque fois que vous voulez utiliser la classe nomClasse.

En rassemblant les classes par groupes, les packages permettent d'organiser l'ensemble des classes et d'éviter d'éventuels conflits sur les noms des classes. En effet, si deux classes appartenant à deux packages distincts portent le même nom, il est toujours possible de les utiliser ensemble dans un même fichier .java, en les différenciant avec leur nom du package grâce à la troisième option .

Par exemple, la bibliothèque de Java 1.0 déclare la classe List dans le package java.awt, qui représente un composant graphique de liste. Dans Java 2, une autre classe List a été ajoutée mais elle appartient au package java.util. Cette classe permet de traiter un ensemble d'objets organisé sous forme de liste, et il est logique qu'elle porte ce nom. Si jamais vous avez besoin de ces deux classes dans un même fichier .java, il faut les utiliser sous la forme java.awt.List et java.util.List, ce qui permet de les différencier.
Comme les différents fournisseurs mettent à disposition leurs classes sous leurs propres packages, vous pouvez ainsi utiliser n'importe laquelle de leurs classes et créer des classes utilisant des noms existants dans vos propres packages, sans risque de conflits.

 

!

Si un package nomPackage comporte des sous-packages (par exemple nomPackage.sousPackage), la clause import nomPackage.* n'importe pas ces sous-packages. Il faut explicitement importer chacun d'eux (avec par exemple import nomPackage.sousPackage.*).

Les clauses import permettant d'énumérer la liste des packages auxquels appartiennent les classes utilisées dans un fichier .java, évitent de répéter le nom du package d'une classe à chaque utilisation de celle-ci. Mais il est tout à fait possible de ne spécifier aucun import et d'écrire chaque classe avec son package.

 

C

L'équivalent de #include est plus ou moins import.
Java ne requiert pas de déclaration externe via un fichier header .h ; les fichiers .java ou .class sont suffisants au compilateur pour résoudre les références aux types externes. Le mot-clé extern est inutile en Java.

C

import permet d'importer les classes définies dans d'autres packages (répertoires), mais n'est pas obligatoire pour importer entre elles les classes définies dans un même package, et notamment le package (répertoire) courant de développement.

C

Toutes les classes que vous importez explicitement ou implicitement (parce qu'elles sont du même package ou qu'elles sont du package java.lang), sont chargées dynamiquement une à une à l'exécution d'un programme Java, la première fois qu'elles sont utilisées.
Comme il est décrit au premier chapitre, les liens ne sont donc pas statiques comme en C, où le link rassemble dans un fichier exécutable tous les composants dont un programme a besoin pour fonctionner. Chaque classe est mémorisée dans un fichier .class qui peut être comparé à une (petite) librairie dynamique (ou DLL Dynamic Link Library).

Définir un package

import permet d'importer n'importe quelle classe d'une bibliothèque, mais vous pouvez aussi créer votre propre bibliothèque, pour y rassembler par exemple un groupe de classes utilisées comme outils dans un ou plusieurs projets. Ceci se fait très simplement grâce à la clause package. Si cette clause est utilisée, elle doit être définie en tête d'un fichier .java, comme suit :

package nomPackage;

Comme expliqué précédemment, le nom de package doit correspondre au chemin d'accès à la classe qui utilise la clause package.
En général, une société qui s'appelle nomSociete utilise com.nomsociete comme base pour le nom des packages des produits Java qu'elle livre, par exemple com.nomsociete.produitxxx pour le produit produitxxx. Les classes de ce package devront être enregistrées dans le sous-répertoire com/nomsociete/produitxxx.
Si dans ce sous-répertoire, vous créez une classe public Outil1 dans le fichier Outil1.java, chacune des classes désirant utiliser la classe Outil1, devra inclure la clause import com.nomsociete.produitXXX.Outil1; et le fichier Outil1.java devra définir la clause package com.nomsociete.produitxxx;.

La figure symbolisant les modificateurs d'accès à des classes représente aussi un exemple simple d'utilisation des packages.

La plupart des exemples fournis dans ce manuel n'utilisent pas de package pour simplifier les programmes.


Page d'accueilFindIt !ContactDémarrer en JavaCréation et utilisation des classesDébut de la page
© Copyrights 1997-2023 Emmanuel PUYBARET / eTeks
- Tous droits réservés -
Table des matièresHiérarchie des classes