Cette partie est chargée de présenter les bases du langage java.
Elle comporte les chapitres suivants :
- présentation de java : introduit le langage java en présentant les différentes éditions et versions du JDK, les caractéristiques du langage et décrit l’installation du JDK
- les techniques de base de programmation : présente rapidement comment compiler et executer une application
- la syntaxe et les éléments de bases de java : explore les éléments du langage d’un point de vue syntaxique
- la programmation orientée objet : explore comment java d’utiliser son orientation objet
- la bibliothèque de classes java : propose un présentation rapide des principales API fournies avec le JDK
- les fonctions mathématiques : indique comment utiliser les fonctions mathématiques
- la gestion des exceptions : traite de la faculté de java de traiter les anomalies qui surviennent lors de l’éxécution du code
- le multitâche :
1. Présentation
Java est un langage de programmation à usage général, évolué et orienté objet dont la syntaxe est proche du C. Il existe 2 types de programmes en Java : les applets et les applications. Une application autonome (stand alone program) est une application qui s’exécute sous le contrôle direct du système d’exploitation. Une applet est une application qui est chargée par un navigateur et qui est exécutée sous le contrôle de celui ci.
Ce chapitre contient plusieurs sections :
- Les différentes éditions de Java
- Les caractéristiques
- Les différences entre Java et JavaScript
- L’installation du JDK
1.1. Les différentes éditions de Java
Sun fourni gratuitement un ensemble d’outils et d’API pour permettre le développement de programmes avec Java. Ce kit, nommé JDK, est librement téléchargeable sur le site web de Sun http://java.sun.com ou par FTP ftp://java.sun.com/pub/
Le JRE contient uniquement l’environnement d’exécution de programmes Java. Le JDK contient lui même le JRE. Le JRE seul doit être installé sur les machines ou des applications java doivent être exécutées.
Depuis sa version 1.2, Java a été renommé Java 2. Les numéros de versions 1.2 et 2 désignent donc la même version.
Le JDK a été renommé J2SDK (Java 2 Software development Kit) mais la dénomination JDK reste encore largement utilisée.
le JRE a été renommé J2RE (Java 2 Runtime Edition).
Trois éditions du JDK 1.2 existent :
- J2ME : Java 2 Micro Edition
- J2SE : Java 2 Standard Edition
- J2EE : Java 2 Entreprise Edition
Sun fourni le JDK 1.2 sous les plate−formes Windows, Solaris et Linux.
La version 1.3 de Java est désignée sous le nom Java 2 version 1.3.
La documentation au format HTML des API de java est fournie séparément. Malgré sa taille imposante, cette documentation est indispensable pour obtenir des informations complètes sur les classes fournies. Le tableau ci dessous résume la taille des différents composants selon leur version pour la plateforme Windows.
Version 1.0 |
Version 1.1 |
Version 1.2 |
Version 1.3 |
Version 1.4 béta |
||
JDK compressé |
8,6 Mo |
20 Mo |
30 Mo |
47 Mo |
||
JDK installé |
53 Mo |
59 Mo |
||||
JRE compressé |
12 Mo |
7 Mo |
||||
JRE installé |
35 Mo |
40 Mo |
||||
Documentation |
16 Mo |
21 Mo |
||||
compressée |
||||||
Documentation |
83 Mo |
106 Mo |
||||
décompressée |
||||||
1.1.1. Le JDK 1.0
Cette première version est lancée officiellement en mai 1995.
1.1.2. Le JDK 1.1
Cette version du JDK est annoncée officiellement en mars 1997. Elle apporte de nombreuses améliorations et d’importantes fonctionnalités nouvelles dont :
- les java beans
- les fichiers JAR
- RMI pour les objets distribués
- la sérialisation
- l’introspection
- JDBC pour l’accès aux données
- les classes internes
- l’internationalisation
- un nouveau modèle de sécurité permettant notamment de signer les applets
- JNI pour l’appele de méthodes natives
- …
1.1.3. Le JDK 1.2
Cette version du JDK est lancée fin 1998. Elle apporte de nombreuses améliorations et d’importantes fonctionnalités nouvelles dont :
- un nouveau modèle de sécurité basé sur les policy
- les JFC sont incluses dans le JDK (Swing, Java2D, accessibility, drag & drop …)
- JDBC 2.0
- les collections
- support de CORBA
- un compilateur JIT est inclus dans le JDK
- de nouveaux format audio sont supportés
- …
Java 2 se décline en 3 éditions différentes qui regroupent des APIs par domaine d’applications :
- Java 2 Micro Edition (J2ME) : contient le nécessaire pour développer des applications capable de fonctionner dans des environnements limités tels que les assistants personnels (PDA), les téléphones portables ou les systèmes de navigation embarqués
- Java 2 Standard Edition (J2SE) : contient le nécessaire pour développer des applications et des applets. Cette édition reprend le JDK 1.0 et 1.1.
- Java 2 Enterprise Edition (J2EE) : contient en plus un ensemble de plusieurs API permettant le développement d’applications destinées aux entreprises tel que JDBC pour l’accès aux bases de données, EJB pour développer des composants orientés métiers, Servlet / JSP pour générer des pages HTML dynamiques …
Le but de ces trois éditions est de proposer une solution à base java quelque soit le type de développement à réaliser.
1.1.4. Le JDK 1.3
Cette version du JDK apporte de nombreuses améliorations notamment sur les performances et des fonctionnalités nouvelles dont :
- JNDI est inclus dans le JDK
- hotspot est inclus dans la JVM
- …
La rapidité d’exécution a été grandement améliorée dans cette version.
1.1.5. Le JDK 1.4 béta
Cette version du JDK apporte de nombreuses améliorations notamment sur les performances et des fonctionnalités nouvelles dont :
- JAXP est inclus dans le JDK pour le support de XML
- JDBC version 3.0
- new I/O API pour compléter la gestion des entrée/sortie
- logging API pour la gestion des logs applicatives
- l’outils java WebStart
- …
Cette version devrait être proposée en version finale au début de l’année 2002.
1.1.6. Le résumé des différentes versions
Au fur et à mesure des nouvelles versions du J.D.K., le nombre de packages et de classes s’accroît :
JDK 1.0 |
JDK 1.1 |
JDK 1.2 |
JDK1.3 |
JDK 1.4 béta |
|
Nombre de de packages |
8 |
23 |
76 |
||
Nombre de classes |
201 |
503 |
1840 |
||
1.1.7. Les extensions du JDK
Sun fourni un certains nombres d’API supplémentaires qui ne sont fournies en standard dans le JDK.
Extension Description
Java Naming and directory interface Cet API permet d’unifier l’accès à des ressources. Elle est JNDI intégré au JDK 1.3
Cet API permet de gérer des emails |
|
Java 3D |
Cet API permet de mettre en oeuvre des graphismes en 3 dimensions |
Java Media |
Cet API permet d’utiliser des composants multimédia |
Java Servlets |
Cet API permet de créer des servlets (composants serveurs) |
Java Help |
Cet API permet de créer des aide en ligne pour les applications |
Jini |
Cet API permet d’utiliser java avec des appareils qui ne sont pas des ordinateurs |
JAXP |
Cet API permet le parsing et le traitement de document XML. Elle est intégré au JDK 1.4 |
1.2. Les caractéristiques
Java est interprété |
le source est compilé en pseudo code puis exécuté par un interpréteur Java ( la Java |
|
Virtual Machine (JVM)) |
||
il n’y a pas de compilation spécifique pour chaque plate forme. Le code reste |
||
Java est indépendant de |
indépendant de la machine sur laquelle il s’exécute. Il est possible d’exécuter des |
|
programmes Java sur tous les environnements qui possèdent une Java Virtual Machine. |
||
toute plate−forme |
||
Cette indépendance est assurée au niveau du code source grâce à Unicode et au niveau |
||
du byte code. |
||
Java est orienté objet. |
||
le choix de ses auteurs a été d’abandonner des éléments mal compris ou mal exploités des |
||
Java est simple |
autres langages tels que la notion de pointeurs (pas d’incident en manipulant directement |
|
la mémoire), de l’héritage multiple et de la surcharge des opérateurs |
||
Java est fortement typé |
toutes les variables sont typées et il n’existe pas de conversion automatique qui |
|
risquerait une perte de données. |
||
Java assure la gestion de la |
l’allocation de la mémoire pour un objet est automatique à sa création et Java récupère |
|
automatiquement la mémoire inutilisée grâce au garbage collector qui restitue les zones |
||
mémoire |
||
de mémoire laissées libres suite à la destruction des objets. |
||
la sécurité fait partie intégrante du système d’exécution et du compilateur. Un |
||
programme Java planté ne menace pas le système d’exploitation. Il ne peut y avoir |
||
d’accès direct à la mémoire. L’accès au disque dur est réglementé dans une applet. |
||
Les programmes fonctionnant sur le Web sous soumis aux restrictions suivantes dans la |
||
version 1.0 de Java : |
||
Java est sûr |
aucun programme ne peut ouvrir, lire, écrire ou effacer un fichier sur le système |
|
de l’utilisateur |
||
aucun programme ne peut lancer un autre programme sur le système de |
||
l’utilisateur |
||
toute fenêtre créée par le programme est clairement identifiée comme fenêtre |
||
Java, ce qui interdit par exemple la création d’une fausse fenêtre demandant un |
||
mot de passe |
||
Les programmes ne peuvent pas se connecter à d’autres sites Web que celui dont |
||
il provient. |
||
Java est économe |
||
le pseudo code a une taille relativement petite car les bibliothèques de classes requises ne sont liées qu’à l’exécution.
il permet l’utilisation de threads qui sont des unités d’exécution isolées. La JVM elle
Java est multitâche
même utilise plusieurs threads.
Les programmes Java exécutés localement sont des applications, ceux qui tournent sur des pages Web sont des applets.
Les différences entre une applet et une application sont :
- les applets n’ont pas de bloc main() : la méthode main() est appelée par la machine virtuelle pour exécuter une application.
- les applets ne peuvent pas être testées avec l’interpréteur mais doivent être intégrées à une page HTML, elle même visualisée avec un navigateur sachant gérer les applets Java, ou testées avec l’applet viewer.
1.3. Les différences entre Java et JavaScript
Il ne faut pas confondre Java et JavaScript. JavaScript est un langage développé par Netscape Communications.
La syntaxe des deux langages est proche car elles dérivent toutes les deux du C++.
Il existe de nombreuses differences entre les deux langages :
Java |
Javascript |
||
Auteur |
Développé par Sun Microsystems |
Développé par Netscape Communications |
|
Format |
Compilé sous forme de byte−code |
Interprété |
|
Stockage |
Applet téléchargé comme un élément de la page |
Source inséré dans la page web |
|
web |
|||
Utilisation |
Utilisable pour développer tous les types |
Utilisable uniquement pour « dynamiser » les |
|
d’applications |
pages web |
||
Execution |
Executé dans la JVM du navigateur |
Executé par le navigateur |
|
POO |
Orienté objets |
Manipule des objets mais ne permet pas d’en |
|
définir |
|||
Typage |
Fortement typé |
Pas de contrôle de type |
|
Compléxité du |
Code relativement complexe |
Code simple |
|
code |
|||
1.4. L’installation du JDK
Le JDK et la documentation sont librement téléchargeable sur le site web de Sun : http://java.sun.com
1.4.1. L’installation de la version 1.3 DU JDK de Sun sous Windows 9x
Pour installer le JDK 1.3 sous Windows 9x, il suffit d’exécuter le programme : j2sdk1_3_0−win.exe
Le programme commence par désarchiver les composants.
Le programme utilise InstallShield pour réaliser l’installation
L’installation vous souhaite la bienvenue et vous donne quelques informations d’usage.
L’installation vous demande ensuite de lire et d’approuver les termes de la licence d’utilisation.
L’installation vous demande le répertoire dans lequel le JDK va être installé. Le répertoire proposé par défaut est pertinent car il est simple.
L’installation vous demande les composants à installer :
- Program Files est obligatoire pour une première installation
- Les interfaces natives ne sont utiles que pour réaliser des appels de code natif dans les programmes java
- Les démos sont utiles car ils fournissent quelques exemples
- les sources contiennent les sources de la plupart des classes java écrite en java. Attention à l’espace disque nécessaire à cet élément
L’installation se poursuit par la copie des fichiers et la configuration du JRE
1.4.2. L’installation de la documentation sous Windows
L’archive contient la documentation sous forme d’arborescence dont la racine est jdk1.3\docs.
Si le répertoire par défaut a été utilisé lors de l’installation, il suffit de décompresser l’archive à la racine du disque C:\.
Il peut être pratique de désarchiver le fichier dans dans un sous répertoire, ce permet de reunir plusieurs versions de la documentation.
1.4.3. La configuration des variables système sous Windows
Pour un bon fonctionnement du JDK, il faut paramétrer correctement deux variables systèmes : la variable PATH qui définie les chemins de recherche des exécutables et la variable CLASSPATH qui définies les chemins de rechercher des classes java.
Pour configurer la variable PATH, il suffit d’ajouter à la fin du fichier autoexec.bat :
Exemple :
SET PATH=%PATH%;C:\JDK1.3\BIN
Attention : si une version antérieure du JDK était déjà présente, la variable PATH doit déjà contient un chemin vers les utilitaires du JDK. Il faut alors modifier ce chemin sinon c’est l’ancienne version qui sera utilisée. Pour vérifier la version du JDK utilisé, il suffit de saisir la commande java –version dans une fenêtre DOS.
La variable CLASSPATH est aussi définie dans le fichier autoexec.bat. Il suffit d’ajouter ligne ou de modifier la ligne existante définissant cette variable.
Exemple :
SET CLASSPATH=.;
Il est intéressant d’ajouter le . qui désigne le répertoire courant dans le CLASSPATH.
Il faudra ajouter par la suite les chemins d’accès aux différent packages requis par les développements.
Pour que ces modifications prennent effet dans le système, il faut redemarrer Windows ou executer ces deux instructions sur une ligne de commande dos.
1.4.4. Les éléments du JDK sous Windows
Le répertoire dans lequel a été installé le JDK contient plusieurs répertoires. Les répertoires données ci après sont ceux utilisés en ayant gardé le répertoire défaut lors de l’installation.
Contenu |
||
Le répertoire d’installation contient deux fichiers intéressants : le fichier readme.html qui |
||
C:\jdk1.3 |
fourni quelques information et des liens web et le fichier src.jar qui contient le source |
|
java de nombreuses classes. Ce dernier fichier n’est présent que si l’option |
||
correspondante a été cochée lors de l’installation. |
||
C:\jdk1.3\bin |
Ce répertoire contient les exécutables : le compilateur javac, l’interpréteur java, le |
|
débuggeur jdb et d’une façon générale tous les outils du JDK. |
||
C:\jdk1.3\demo |
Ce répertoire n’est présent que si l’option nécessaire a été cochée lors de l’installation. Il |
|
contient des applications et des applets avec leur code source. |
||
C:\jdk1.3\docs |
Ce répertoire n’est présent que si la documentation a été décompressée. |
|
C:\jdk1.3\include et |
Ces répertoires ne sont présent que si les options nécessaires ont été cochées lors de |
|
l’installation. Il contient des fichiers d’en−tête C (fichier avec l’extension .H) qui |
||
C:\jdk1.3\include−old |
||
permettent d’inclure du code C dans le source java. |
||
Ce répertoire contient le JRE : il regroupe le nécessaire à l’exécution des applications |
||
notamment le fichier rt.jar qui regroupe les API. Depuis la version 1.3, le JRE contient |
||
deux machines virtuelles : la JVM classique et la JVM utilisant la technologie Hot spot. |
||
C:\jdk1.3\jre |
Cette dernière est bien plus rapide et celle qui est utilisée par défaut. |
|
Les éléments qui composent le JRE sont séparés dans les répertoires bin et lib selon leur |
||
nature. |
||
Ce répertoire ne contient plus que quelques bibliothèques notamment le fichier tools.jar. |
||
C:\jdk1.3\lib |
Avec le JDK 1.1 ce répertoire contenait le fichier de la bibliothèque standard. Ce fichier |
|
est maintenant dans le répertoire JRE. |
||
2. Les techniques de base de programmation en Java
N’importe quel éditeur de texte peut être utilisé pour éditer un fichier source Java.
Il est nécessaire de compiler le source pour le transformer en J−code ou byte−code Java qui sera lui exécuté par la machine virtuelle.
Il est préférable de définir une classe par fichier. Le nom de la classe publique et le fichier qui la contient doivent être identiques.
Pour être compilé, le programme doit être enregistré au format de caractères Unicode : une conversion automatique est faite par le JDK si nécessaire.
Ce chapitre contient plusieurs sections :
- La compilation d’un code source :
Cette section présente la compilation d’un fichier source.
- L’exécution d’un programme et d’une applet :
Cette section présente l’éxécution d’un programme et d’une applet.
2.1. La compilation d’un code source
Pour compiler un fichier source il suffit d’invoquer la commande javac avec le nom du fichier source avec son extension .java
javac NomFichier.java
Le nom du fichier doit correspondre au nom de la classe principale en respectant la casse même si le système d’exploitation n’y est pas sensible
Suite à la compilation, le pseudo code Java est enregistré sous le nom NomFichier.class
2.2. L’exécution d’un programme et d’une applet
2.2.1. L’exécution d’un programme
Une classe ne peut être éxécutée que si elle contient une méthode main() correctement définie.
Pour exécuter un fichier contenant du byte−code il suffit d’invoquer la commande java avec le nom du fichier source sans son extension .class
java NomFichier
2.2.2. L’exécution d’une applet
Il suffit de créer une page HTML pouvant être très simple :
Exemple
<HTML> <TITLE> test applet Java </TITLE> <BODY> <APPLET code=« NomFichier.class » width=270 height=200> </APPLET> </BODY> </HTML>
Il faut ensuite visualiser la page créée dans l’appletviewer ou dans un navigateur 32 bits compatible avec la version de Java dans laquelle l’applet est écrite.
3. La syntaxe et les éléments de bases de java
Ce chapitre ce compose de plusieurs sections :
- Les règles de bases
Cette section présente les règles syntaxiques de base de java.
- Les identificateurs
Cette section présente les règles de composition des identificateurs.
- Les commentaires
Cette section présente les différentes formes de commentaires de java.
- La déclaration et l’utilisation de variables
Cette section présente la déclaration des variables, les types élémentaires, les formats des type élémentaires, l’initialisation des variables, l’affectation et les comparaisons.
- Les opérations arithmétiques
Cette section présente les opérateurs arithmétique sur les entiers et les flottants et les opérateurs d’incrémentation et de décrémentation.
- La priorité des opérateurs
Cette section présente la priorité des opérateurs.
- Les structures de contrôles
Cette section présente les instructions permettant la réalisation de boucles, de branchements conditionnels et de débranchements.
- Les tableaux
Cette section présente la déclaration, l’initialisation explicite et le parcours d’un tableau
- Les conversions de types
Cette section présente la conversion de types élémentaires.
- La manipulation des chaines de caractères
Cette section présente la définition et la manipulation de chaine de caractères (addition, comparaison, changement de la casse … ).
3.1. Les règles de base
Java est sensible à la casse.
Les blocs de code sont encadrés par des accolades. Chaque instruction se termine par un ‘;’.
Une instruction peut tenir sur plusieurs lignes
Exemple :
char code = 'D';
L’indentation est ignorée du compilateur mais elle permet une meilleure compréhension du code par le programmeur.
3.2. Les identificateurs
Chaque objet, classe, programme ou variable est associer à un nom : l’identificateur qui peut se composer de tous les caractères alphanumériques et des caractères _ et $. Le premier caractère doit être une lettre, le caractère de soulignement ou le signe dollars.
Rappel : Java est sensible à la casse.
3.3. Les commentaires
Ils ne sont pas pris en compte par le compilateur donc ils ne sont pas inclus dans le pseudo code. Ils ne se terminent pas par un ;.
Il existe trois type de commentaire en Java :
Type de commentaires |
Exemple |
// commentaire sur une seule ligne
commentaire abrégé
int N=1; // déclaration du compteur
/* commentaires ligne 1
commentaire multiligne
commentaires ligne 2 */
commentaire de documentation automatique |
/** commentaire */ |
3.4. La déclaration et l’utilisation de variables
3.4.1. La déclaration de variables
Une variable possède un nom, un type et une valeur. La déclaration d’une variable doit donc contenir deux choses : un nom et le type de données qu’elle peut contenir. Une variable est utilisable dans le bloc ou elle est définie.
La déclaration d’une variable permet de réserver la mémoire pour en stocker la valeur.
Le type d’une variable peut être un type élémentaire ou un objet :
type_élémentaire variable;
classe variable ;
Exemple :
long nombre;
Rappel : les noms de variables en Java peuvent commencer par un lettre, par le caractère de soulignement ou par le signe dollars. Le reste du nom peut comporter des lettres ou des nombres mais jamais d’espaces.
Il est possible de définir plusieurs variables de même type en séparant chacune d’elles par une virgule.
Exemple :
int jour, mois, annee ;
Java est un langage à typage rigoureux qui ne possède pas de transtypage automatique lorsque ce transtypage risque de conduire à une perte d’information.
Pour les objets, il est nécessaire en plus de la déclaration de la variable de créer un objet avant de pouvoir l’utiliser. Il faut réserver de la mémoire pour la création d’un objet ( remarque : un tableau est un objet en java ) avec l’instruction new. La libération de la mémoire se fait automatiquement grâce au garbage collector.
Exemple :
MaClasse instance; // déclaration de l’objet
instance = new maClasse(); // création de l’objet
OU MaClasse instance = new MaClasse(); // déclaration et création de l’objet
Exemple :
int[] nombre = new int[10];
Il est possible en une seule instruction de faire la déclaration et l’affectation d’une valeur à une variable ou plusieurs variables.
Exemple :
int i=3 , j=4 ;
3.4.2. Les types élémentaires
Les types élémentaires ont une taille identique quelque soit la plate−forme d’exécution : c’est un des éléments qui permet à java d’être indépendant de la plate−forme sur lequel le code s’exécute.
Type |
Désignation |
Longueur |
Valeurs |
Commentaires |
|
boolean |
valeur logique : true ou false |
8 bits |
True ou false |
pas de conversion possible vers un |
|
autre type |
|||||
byte |
octet signé |
8 bits |
−128 à 127 |
||
short |
entier court signé |
16 bits |
−32768 à 32767 |
||
char |
caractère Unicode |
16 bits |
\u0000 à \uFFFF |
entouré de cotes simples dans un |
|
programme Java |
|||||
int |
entier signé |
32 bits |
−2147483648 à |
||
2147483647 |
|||||
float |
virgule flottante simple |
32 bits |
1.401e−045 à |
||
précision (IEEE754) |
3.40282e+038 |
||||
double |
virgule flottante double |
64 bits |
2.22507e−308 à |
||
précision (IEEE754) |
1.79769e+308 |
||||
long |
entier long |
64 bits |
−9223372036854775808 à |
||
9223372036854775807 |
|||||
Les types élémentaires commencent tous par une minuscule.
3.4.3. Le format des types élémentaires
Le format des nombres entiers :
Les types byte, short, int et long peuvent être codés en décimal, hexadécimal ou octal. Pour un nombre hexadécimal, il suffit de préfixer sa valeur par 0x. Pour un nombre octal, le nombre doit commencer par un zéro. Le suffixe l ou L permet de spécifier que c’est un entier long.
Le format des nombres décimaux :
Les types float et double stockent des nombres flottants : pour être reconnus comme tel ils doivent posséder soit un point, un exposant ou l’un des suffixes f, F, d, D. Il est possible de préciser des nombres qui n’ont pas le partie entière ou décimale.
Exemple :
float pi = 3.141f;
double v = 3d
float f = +.1f , d = 1e10f;
Par défaut un littéral est de type double : pour définir un float il faut le suffixer par la lettre f ou F.
Exemple :
double w = 1.1;
Attention : float pi = 3.141; // erreur à la compilation
Le format des caractères :
Un caractère est codé sur 16 bis car il est conforme à la norme Unicode. Il doit être entouré par des apostrophes. Une valeur de type char peut être considérée comme un entier non négatif de 0 à 65535. Cependant la conversion implicite par affectation n’est pas possible.
Exemple :
/* test sur les caractères */
class test1 {
public static void main (String args[]) {
char code = ‘D’;
int index = code − ‘A’;
System.out.println(« index = » + index);
}
}
3.4.4. L’initialisation des variables
Exemple :
int nombre; // déclaration
nombre = 100; //initialisation
OU int nombre = 100; //déclaration et initialisation
En java, toute variable appartenant à un objet (définie comme étant un attribut de l’objet) est initialisée avec une valeur par défaut en accord avec son type au moment de la creation. Cette initialisation ne s’applique pas aux variables locales
Les valeurs par défaut lors de l’initialisation automatique des variables d’instances sont :
Type |
Valeur par défaut |
boolean |
false |
byte, short, int, long |
0 |
float, double |
0.0 |
char |
\u000 |
Remarque : Dans une applet, il est préférable de faire les déclarations et initialisation dans la méthode init().
3.4.5. L’affectation
le signe = est l’opérateur d’affectation et s’utilise avec une expression de la forme variable = expression. L’opération d’affectation est associatif de droite à gauche : il renvoie la valeur affectée ce qui permet d’écrire :
x = y = z = 0;
Il existe des opérateurs qui permettent de simplifier l’écritures d’une opération d’affectation associée à un opérateur mathématique :
Opérateur |
Exemple |
Signification |
= |
a=10 |
équivalent à : a = 10 |
+= |
A+=10 |
équivalent à : a = a + 10 |
−= |
a−= |
équivalent à : a = a − 10 |
*= |
A*= |
équivalent à : a = a * 10 |
/= |
a/=10 |
équivalent à : a = a / 10 |
%= |
A%=10 |
reste de la division |
^= |
a^=10 |
équivalent à : a = a ^ 10 |
<<= |
A<<=10 |
équivalent à : a = a << 10 a est complété par des zéros à droite |
>>= |
a>>=10 |
équivalent à : a = a >> 10 a est complété par des zéros à gauche |
>>>= |
a>>>=10 |
équivalent à : a = a >>> 10 décalage à gauche non signé |
Attention : Lors d’une opération sur des opérandes de type différents, le compilateur détermine le type du résultat en prenant le type le plus précis des opérandes. Par exemple, une multiplication d’une variable de type float avec une variable de type double donne un résultat de type double. Lors d’une opération entre un opérande entier et un flottant, le résultat est du type de l’opérande flottant.
3.4.6. Les comparaisons
Java propose des opérateurs pour toutes les comparaisons :
Opérateur |
Exemple |
Signification |
a > 10 |
strictement supérieur |
||
< |
a < 10 |
strictement inférieur |
|
>= |
a >= 10 |
supérieur ou égal |
|
<= |
a <= 10 |
inférieur ou égal |
|
== |
a == 10 |
Egalité |
|
!= |
a != 10 |
diffèrent de |
|
& |
a & b |
ET binaire |
|
^ |
a ^ b |
OU exclusif binaire |
|
| |
a | b |
OU binaire |
|
&& |
a && b |
ET logique (pour expressions booléennes) : l’évaluation de l’expression |
|
cesse dès qu’elle devient fausse |
|||
|| |
a || b |
OU logique (pour expressions booléennes) : l’évaluation de |
|
l’expression cesse dès qu’elle devient vraie |
|||
opérateur conditionnel : renvoie la valeur b ou c selon l’évaluation de |
|||
? : |
a ? b : c |
l’expression a (si a alors b sinon c) : b et c doivent retourner le même |
|
type |
|||
Les opérateurs sont exécutés dans l’ordre suivant à l’intérieure d’une expression qui est analysée de gauche à droite:
- incréments et décréments
- multiplication, division et reste de division (modulo)
- addition et soustraction
- comparaison
- le signe = d’affectation d’une valeur à une variable
L’usage des parenthèse permet de modifier cet ordre de priorité.
3.5. Les opérations arithmétiques
Les opérateurs arithmétiques se notent + (addition), − (soustraction), * (multiplication), / (division) et % (reste de la division). Ils peuvent se combiner à l’opérateur d’affectation
Exemple :
nombre += 10;
3.5.1. L’arithmétique entière
Pour les types numériques entiers, Java met en oeuvre une sorte de mécanisme de conversion implicite vers le type int appelée promotion entière. Ce mécanisme fait partie des règles mise en place pour renforcer la sécurité du code.
Exemple :
short x= 5 , y = 15;
x = x + y ; //erreur à la compilation
Incompatible type for =. Explicit cast needed to convert int to short.
x = x + y ; //erreur à la compilation
1 error
Les opérandes et le résultat de l’opération sont convertis en type int. Le résultat est affecté dans un type short : il y a donc risque de perte d’informations et donc erreur à la compilation est émise. Cette promotion évite un débordement de capacité sans que le programmeur soit pleinement conscient du risque : il est nécessaire, pour régler le problème, d’utiliser une conversion explicite ou cast
Exemple :
x = (short) ( x + y );
Il est nécessaire de mettre l’opération entre parenthèse pour que ce soit son résultat qui soit converti car le cast a une priorité plus forte que les opérateurs arithmétiques.
La division par zéro pour les types entiers lève l’exception ArithmeticException
Exemple :
/* test sur la division par zero de nombres entiers */ class test3 {
public static void main (String args[]) {
int valeur=10;
double résultat = valeur / 0;
System.out.println(« index = » + résultat);
}
}
3.5.2. L’arithmétique en virgule flottante
Avec des valeurs float ou double, la division par zéro ne produit pas d’exception mais le résultat est indiqué par une valeur spéciale qui peut prendre trois états :
- indéfini : Float.NaN ou Double.NaN (not a number)
- indéfini positif : Float.POSITIVE_INFINITY ou Double.POSITIVE_INFINITY, +
- indéfini négatif : Float.NEGATIVE_INFINITY ou Double.NEGATIVE_INFINITY, +
Conformément à la norme IEEE754, ces valeurs spéciales représentent le résultat d’une expression invalide NaN, une valeur supérieure au plafond du type pour infini positif ou négatif.
X |
Y |
X / Y |
X % Y |
valeur finie |
0 |
+ |
NaN |
valeur finie |
+/− |
0 |
x |
0 |
0 |
NaN |
NaN |
+/− |
valeur finie |
+/− |
NaN |
+/− |
+/− |
NaN |
NaN |
Exemple :
/* test sur la division par zero de nombres flottants */
class test2 {
public static void main (String args[]) {
float valeur=10f;
double résultat = valeur / 0;
System.out.println(« index = » + résultat);
}
}
3.5.3. L’incrémentation et la décrémentation
Les opérateurs d’incrémentation et de décrémentation sont : n++ ++n n−− −−n
Si l’opérateur est placé avant la variable (préfixé), la modification de la valeur est immédiate sinon la modification n’a lieu qu’à l’issu de l’exécution de la ligne d’instruction (postfixé)
L’opérateur ++ renvoie la valeur avant incrémentation s’il est postfixé, après incrémentation s’il est préfixé.
Exemple :
System.out.println(x++); // est équivalent à
System.out.println(x); x = x + 1;
System.out.println(++x); // est équivalent à
x = x + 1; System.out.println(x);
Exemple :
/* test sur les incrementations prefixees et postfixees */
class test4 {
public static void main (String args[]) {
int n1=0;
int n2=0;
System.out.println(« n1 = » + n1 + » n2 = » + n2); n1=n2++;
System.out.println(« n1 = » + n1 + » n2 = » + n2); n1=++n2;
System.out.println(« n1 = » + n1 + » n2 = » + n2);
n1=n1++; //attention System.out.println(« n1 = » + n1 + » n2 = » + n2);
}
}
Résultat :
int n1=0;
int n2=0; // n1=0 n2=0
n1=n2++; // n1=0 n2=1
n1=++n2; // n1=2 n2=2
n1=n1++; // attention : n1 ne change pas de valeur
3.6. La priorité des opérateurs
Java définit les priorités dans les opérateurs comme suit ( du plus prioriotaire au moins prioritaire )
les parenthèses |
( ) |
++ |
||
−− |
||
* |
||
les opérateurs de multiplication, division, et modulo |
/ |
|
% |
||
les opérateurs d’addition et soustraction |
+ |
|
− |
||
les opérateurs de décalage |
<< |
|
>> |
||
< |
||
les opérateurs de comparaison |
> |
|
<= |
||
>= |
||
les opérateurs d’égalité |
== |
|
!= |
||
l’opérateur OU exclusif |
^ |
|
l’opérateur ET |
& |
|
l’opérateur OU |
| |
|
l’opérateur ET logique |
&& |
|
l’opérateur OU logique |
|| |
|
= |
||
les opérateurs d’assignement |
+= |
|
−= |
||
Les parenthèses ayant une forte priorité, l’ordre d’interprétation des opérateurs peut être modifié par des parenthèses.
3.7. Les structures de contrôles
3.7.1. Les boucles
while ( boolean )
{
… // code a éxécuter dans la boucle
}
Le code est exécuté tant que le booléen est vrai. Si avant l’instruction while, le booléen est faux, alors le code de la boucle ne sera jamais exécuté
Ne pas mettre de ; après la condition sinon le corps de la boucle ne sera jamais exécuté
do {
…
} while ( boolean )
Cette boucle est au moins exécuté une fois quelque soit la valeur du booléen;
for ( initialisation; condition; modification) {
…
}
Exemple :
for (i = 0 ; i < 10; i++ ) { ….}
for (int i = 0 ; i < 10; i++ ) { ….}
for ( ; ; ) { … } // boucle infinie
L’initialisation, la condition et la modification de l’index sont optionels.
Dans l’initialisation, on peut déclarer une variable qui servira d’index et qui sera dans ce cas locale à la boucle.
Il est possible d’inclure plusieurs traitements dans l’initialisation et la modification de la boucle : chacun des traitements doit etre séparé par une virgule.
Exemple :
for (i = 0 , j = 0 ; i * j < 1000;i++ , j+= 2) { ….}
La condition peut ne pas porter sur l’index de la boucle :
Exemple :
boolean trouve = false;
for (int i = 0 ; !trouve ; i++ ) {
if ( tableau[i] == 1 )
trouve = true;
… //gestion de la fin du parcours du tableau
}
Il est possible de nommer une boucle pour permettre de l’interrompre même si cela est peu recommendé :
Exemple :
int compteur = 0;
boucle:
while (compteur < 100) {
for(int compte = 0 ; compte < 10 ; compte ++) {
compteur += compte;
System.out.println(« compteur = « +compteur);
if (compteur> 40) break boucle;
}
}
3.7.2. Les branchements conditionnels
if (boolean) {
…
} else if (boolean) {
…
} else {
…
swith (expression) {
case constante1 :
instr11;
instr12;
break;
case constante2 :
…
default :
…
}
On ne peut utiliser switch qu’avec des types primitifs d’une taille maximum de 32 bits (byte, short, int, char).
Si une instruction case ne contient pas de break alors les traitements associés au case suivant sont éxécutés.
Il est possible d’imbriquer des switch
L’opérateur ternaire : ( condition ) ? valeur−vrai : valeur−faux
Exemple :
if (niveau == 5) // equivalent à total = (niveau ==5) ? 10 : 5;
total = 10;
else total = 5 ;
System.out.println((sexe == « H ») ? « Mr » : « Mme »);
3.7.3. Les débranchements
break : permet de quitter immédiatement une boucle ou un branchement. Utilisable dans tous les controles de flot
continue : s’utilise dans une boucle pour passer directement à l’itération suivante
break et continue peuvent s’excuter avec des blocs nommés. Il est possible de préciser une étiquette pour indiquer le point de retour lors de la fin du traitement déclenché par le break.
Une étiquette est un nom suivi d’un deux points qui définit le début d’une instruction.
3.8. Les tableaux
Ils sont dérivés de la classe Object : il faut utiliser des méthodes pour y accéder dont font parti des messages de Object tel que equals() ou getClass(). Le premier élément possède l’indice 0.
3.8.1. La déclaration des tableaux
Java permet de placer les crochets après ou avant le nom du tableau dans la déclaration.
Exemple :
int tableau[] = new int[50]; // déclaration et allocation
OU int[] tableau = new int[50];
OU int tab[]; // déclaration
tab = new int[50]; //allocation
Java ne supporte pas directement les tableaux à plusieurs dimensions : il faut déclarer un tableau de tableau.
Exemple :
float tableau[][] = new float[10][10];
La taille des tableaux de la seconde dimension peut ne pas être identiques pour chaque occurrences.
Exemple :
int dim1[][] = new int[3][];
dim1[0] = new int[4];
dim1[1] = new int[9];
dim1[2] = new int[2];
Chaque élément du tableau est initialisé selon son type par l’instruction new : 0 pour les numériques, ‘\0′ pour les caractères, false pour les booléens et nil pour les chaines de caractères et les autres objets.
3.8.2. L’initialisation explicite d’un tableau
Exemple :
int tableau[5] = {10,20,30,40,50};
int tableau[3][2] = {{5,1},{6,2},{7,3}};
La taille du tableau n’est pas obligatoire si le tableau est initialisé à sa création.
Exemple :
int tableau[] = {10,20,30,40,50};
Le nombre d’élément de chaque lignes peut ne pas être identique :
Exemple :
int[][] tabEntiers = {{1,2,3,4,5,6},
{1,2,3,4},
{1,2,3,4,5,6,7,8,9}};
3.8.3. Le parcours d’un tableau
Exemple :
for (int i = 0; i < tableau.length ; i ++) { … }
La variable length retourne le nombre d’éléments du tableau.
Pour passer un tableau à une méthode, il suffit de déclarer les paramètres dans l’en tête de la méthode
public void printArray(String texte[]){ …
}
Les tableaux sont toujours transmis par référence puisque se sont des objets.
U n a c c è s a u n é l é m e n t d ‘ u n t a b l e a u q u i d é p a s s e s a c a p a c i t é , l è v e u n e e x c e p t i o n d u t y p e java.lang.arrayIndexOutOfBoundsException.
3.9. Les conversions de types
Lors de la déclaration, il est possible d’utiliser un cast :
Exemple :
int entier = 5;
float flottant = (float) entier;
La conversion peut entrainer une perte d’informations.
Il n’existe pas en java de fonction pour convertir : les conversions de type ce font par des méthodes. La bibliothèque de classes API fournit une série de classes qui contiennent des méthodes de manipulation et de conversion de types élémentaires.
Classe |
Role |
String |
pour les chaines de caractères Unicode |
Integer |
pour les valeurs entières (integer) |
Long |
pour les entiers long signés (long) |
Float |
pour les nombres à virgules flottante (float) |
Double |
pour les nombres à virgule flottante en double précision (double) |
Les classes portent le même nom que le type élémentaires sur lequel elles reposent avec la première lettre en majuscule.
Ces classes contiennent généralement plusieurs constructeurs. Pour y accéder, il faut les instancier puisque de sont des objets.
Exemple :
String montexte;
montexte = new String(«test»);
L’objet montexte permet d’accéder aux méthodes de la classe java.lang.String
3.9.1. La conversion d’un entier int en chaine de caractère String
Exemple :
int i = 10;
String montexte = new String();
montexte =montexte.valueOf(i);
valueOf est également définie pour des arguments de type boolean, long, float, double et char
3.9.2. La conversion d’une chaine de caractères String en entier int
Exemple :
String montexte = new String(« 10 »);
Integer nomnombre=new Integer(montexte);
int i = monnombre.intValue(); //convertion d’Integer en int
3.9.3. La conversion d’un entier int en entier long
Exemple :
int i=10;
Integer monnombre=new Integer(i);
long j=monnombre.longValue();
3.10. La manipulation des chaines de caractères
La définition d’un caractère :
Exemple :
char touche = ‘%’;
La définition d’une chaine :
Exemple :
String texte = « bonjour »;
Les variables de type String sont des objets. Partout ou des constantes chaines figurent entre guillemets, le compilateur
Java génère un objet de type String avec le contenu spécifié. Il est donc possible d’ecrire :
String texte = « Java Java Java ».replace(‘a’,’o’);
Les chaines ne sont pas des tableaux : il faut utiliser les méthodes de la classes String d’un objet instancié pour effectuer des manipulations.
Il est impossible de modifier le contenu d’un objet String contruit à partir d’une constante. Cependant, il est possible d’utiliser les méthodes qui renvoient une chaine pour modifier le contenu de la chaine
String texte = « Java Java Java »;
texte = texte.replace(‘a’,’o’);
Java ne fonctionne pas avec le jeu de caractères ASCII ou ANSI, mais avec Unicode (Universal Code). Ceci concerne les types char et les chaines de caractères. Le jeu de caractères Unicode code un caractère sur 2 octets. Les caractères 0 à 255 correspondent exactement au jeu de caractères ASCII
3.10.1. Les caractères spéciaux dans les chaines
Caractères spéciaux |
Affichage |
\’ |
Apostrophe |
\ » |
Guillemet |
\\ |
anti slash |
\t |
Tabulation |
\b |
retour arrière (backspace) |
\r |
retour chariot |
\f |
saut de page (form feed) |
\n |
saut de ligne (newline) |
\0ddd |
caractère ASCII ddd (octal) |
\xdd |
caractère ASCII dd (hexadécimal) |
\udddd |
caractère Unicode dddd (hexadécimal) |
3.10.2 L’addition de chaines
Java admet l’opérateur + comme opérateur de concaténation de chaines de caractères.
L’opérateur + permet de concatener plusieurs chaines. Il est possible d’utiliser l’opérateur +=
Exemple :
String texte = « »;
texte += « Hello »;
texte += « World3 »;
Cet opérateur sert aussi à concatener des chaines avec tous les types de bases. La variable ou constante est alors convertie en chaine et ajoutée à la précédente. La condition préalable est d’avoir au moins une chaine dans l’expression sinon le sinon ‘+’ est évalué comme opérateur mathématique.
Exemple :
System.out.println(« La valeur de Pi est : »+Math.PI); int duree = 121;
System.out.println(« durée = » +duree);
3.10.3. La comparaison de deux chaines
Il faut utiliser la méthode equals()
Exemple :
String texte1 = « texte 1 »;
String texte2 = « texte 2 »;
if ( texte1.equals(texte2) )…
3.10.4. La détermination de la longueur d’une chaine
La méthode length() permet de déterminer la longueur d’une chaine.
Exemple :
String texte = « texte »;
int longueur = texte.length();
3.10.5. La modification de la casse d’une chaine
Les méthodes Java toUpperCase() et toLowerCase() permettent respectivement d’obtenir une chaine tout en majuscule ou tout en minuscule.
Exemple :
String texte = « texte »;
String textemaj = texte.toUpperCase();
4. La programmation orientée objet
L’idée de base de la programmation orientée objet est de rassembler dans une même entité appelée objet les données et les traitements qui s’y appliquent.
Ce chapitre ce compose de plusieurs sections :
- Le concept de classe
Cette section présente le concept et la syntaxe de la déclaration d’une classe
- Les objets
Cette section présente la création d’un objet, sa durée de vie, le clonage d’objets, les références et la comparaison d’objets, l’objet null, les variables de classes, la variable this et l’opérateur instanceof.
- Les modificateurs d’accès
Cette section présente les modificateurs d’accès des entités classes, méthodes et attributs ainsi que les mots clés qui permettent de qualifier ces entités
- Les propriétés ou attributs
Cette section présente les données d’une classe : les propriétés ou attributs
- Les méthodes
Cette section présente la déclaration d’une méthode, la transmissions de paramètres, l’emmission de messages, la surcharge, la signature d’une méthode et le polymorphisme et des méthodes particulières : les constructeurs, le destructeur et les accesseurs
- L’héritage
Cette section présente l’héritage : son principe, sa mise en oeuvre, ces conséquences. Il présente aussi la redéfinition d’une méthode héritée et les interfaces
- Les packages
Cette section présente la définition et l’utilisation des packages
- Les classes internes
Cette section présente une extension du langage java qui permet de definir une classe dans une autre.
- La gestion dynamique des objets
Cette section présente rapidement la gestion dynamique des objets grace à l’introspection
4.1. Le concept de classe
Une classe est le support de l ‘encapsulation : c’est un ensemble de données et de fonctions regroupées dans une même entité. Une classe est une description abstraite d’un objet. Les fonctions qui opèrent sur les données sont appelées des méthodes. Instancier une classe consiste à créer un objet sur son modèle. Entre classe et objet il y a, en quelque sorte, le même rapport qu’entre type et variable.
java est un langage orienté objet : tout appartient à une classe sauf les variables de type primitives.
Pour accéder à une classe il faut en déclarer une instance de classe ou objet.
Une classe comporte sa déclaration, des variables et la définition de ses méthodes.
Une classe se compose en deux parties : un en−tête et un corps. Le corps peut être divisé en 2 sections : la déclaration des données et des constantes et la définition des méthodes. Les méthodes et les données sont pourvues d’attributs de visibilité qui gère leur accessibilité par les composants hors de la classe.
4.1.1. La syntaxe de déclaration d’une classe
modificateurs nom_de_classe [extends classe_mere] [implements interface] { … }
ClassModifiers class ClassName [extends SuperClass] [implemenents Interfaces] {
// insérer ici les champs et les méthodes
}
Les modificateurs de classe (ClassModifiers) sont :
Modificateur Role
la classe contient une ou des méthodes abstraites, qui n’ont pas de définition explicite. Une classe abstract déclarée abstract ne peut pas être instanciée : il faut définir une classe qui hérite de cette classe et qui implémente les méthodes nécessaires pour ne plus être abstraite.
la classe ne peut pas être modifiée, sa redéfinition grace à l’héritage est interdite. Les classes déclarées
final
final ne peuvent donc pas avoir de classes filles.
private la classe n’est accessible qu’à partir du fichier où elle est définie
public La classe est accessible partout
Les modificateurs abstract et final ainsi que public et private sont mutuellement exclusifs.
Le mot clé extends permet de spécifier une superclasse éventuelle : ce mot clé permet de préciser la classe mère dans une relation d’héritage.
Le mot clé implements permet de spécifier une ou des interfaces que la classe implémente. Cela permet de récupérer quelques avantages de l’héritage multiple.
L’ordre des méthodes dans une classe n’a pas d’importance. Si dans une classe, on rencontre d’abord la méthode A puis la méthode B, B peut être appelée sans problème dans A.
4.2. Les objets
Les objets contiennent des attributs et des méthodes. Les attributs sont des variables ou des objets nécessaires au fonctionnement de l’objet. En java, une application est un objet. La classe est la description d’un objet. Un objet est une instance d’une classe. Pour chaque instance d’une classe, le code est le même, seul les données sont différentes à chaque objet.
4.2.1. La création d’un objet : instancier une classe
Il est nécessaire de définir la déclaration d’une variable ayant le type de l’objet désiré. La déclaration est de la forme classe nom_de_variable
Exemple :
MaClasse m;
L’opérateur new se charge de créer une instance de la classe et de l’associer à la variable
m = new MaClasse();
Il est possible de tout reunir en une seule déclaration
Exemple :
MaClasse m = new MaClasse();
Chaque instance d’une classe nécessite sa propre variable. Plusieurs variables peuvent désigner un même objet.
En Java, tous les objets sont instanciés par allocation dynamique. Dans l’exemple, la variable m contient une référence sur l’objet instancié ( contient l’adresse de l’objet qu’elle désigne : attention toutefois, il n’est pas possible de manipuler ou d ‘effectuer des opérations directement sur cette adresse comme en C).
Si m2 désigne un objet de type MaClasse, l’instruction m2 = m ne définit pas un nouvel objet mais m et m2 désignent tous les deux le même objet.
L’opérateur new est un opérateur de haute priorité qui permet d’instancier des objets et d’appeler une méthode particulière de cet objet : le contructeur. Il fait appel à la machine virtuelle pour obtenir l’espace mémoire nécessaire à la représentation de l’objet puis appelle le constructeur pour initialiser l’objet dans l’emplacement obtenu. Il renvoie une valeur qui référence l’objet instancié.
Si l’opérateur new n’obtient pas l’allocation mémoire nécessaire il lève l’exception OutOfMemoryError.
Remarque sur les objets de type String : Un objet String est automatiquement créer lors de l’utilisation d’une constante chaine de caractères sauf si celle ci est déjà utilisée dans la classe. Ceci permet une simplification dans l’écriture des programmes.
Exemple :
String chaine = « bonjour »
et String chaine = new String(« bonjour »)
sont équivalents.
4.2.2. La durée de vie d’un objet
Les objets ne sont pas des éléments statiques et leur durée de vie ne correspond pas forcément à la durée d’exécution du programme.
La durée de vie d’un objet passe par trois étapes :
- la déclaration de l’objet et l’instanciation grace à l’opérateur new
Exemple :
nom_de_classe nom_d_objet = new nom_de_classe( … );
- l’utilisation de l’objet en appelant ces méthodes
- la suppression de l’objet : elle est automatique en java grace à la machine virtuelle. La restitution de la mémoire inutilisée est prise en charge par le récupérateur de mémoire (garbage collector). Il n’existe pas d’instruction delete comme en C++.
4.2.3. La création d’objets identiques
Exemple :
MaClasse m1 = new MaClasse();
MaClasse m2 = m1;
m1 et m2 contiennent la même référence et pointent donc tous les deux sur le même objet : les modifications faites à partir d’une des variables modifient l’objet.
Pour créer une copie d’un objet, il faut utiliser la méthode clone() : cette méthode permet de créer un deuxième objet indépendant mais identique à l’original. Cette méthode est héritée de la classe Object qui est la classe mère de toute les classes en Java.
Exemple :
MaClasse m1 = new MaClasse();
MaClasse m2 = m1.clone();
m1 et m2 ne contiennent plus la même référence et pointent donc sur des objets différents.
4.2.4. Les références et la comparaison d’objets
Les variables de type objet que l‘on déclare ne contiennent pas un objet mais une référence vers cette objet. Lorque l’on écrit c1 = c2 (c1 et c2 sont des objets), on copie la référence de l’objet c2 dans c1 : c1 et c2 réfèrent au même objet (ils pointent sur le même objet). L’opérateur == compare ces références. Deux objets avec des propriétés identiques sont deux objets distincts :
Exemple :
Rectangle r1 = new Rectangle(100,50);
Rectangle r2 = new Rectangle(100,50);
if (r1 == r1) { … } // vrai
if (r1 == r2) { … } // faux
Pour comparer l’égalité des variables de deux instances, il faut munir la classe d’un méthode à cette effet : la méthode equals héritée de Object.
Pour s’assurer que deux objets sont de la même classe, il faut utiliser la méthode getClass() de la classe Object dont toutes les classes héritent.
Exemple :
(obj1.getClass().equals(obj2.getClass())
4.2.5. L’objet null
L’objet null est utilisable partout. Il n’appartient pas à une classe mais il peut être utilisé à la place d’un objet de n’importe quelle classe ou comme paramètre. null ne peut pas etre utilisé comme un objet normal : il n’y a pas d’appel de méthodes et aucunes classes ne peut en hériter.
Le fait d’initialiser un variable référent un objet à null permet au ramasse miette de libérer la mémoire allouée à l’objet.
4.2.6. Les variables de classes
Elles ne sont définies qu’une seule fois quelque soit le nombre d’objets instanciés de la classe. Leur déclaration est accompagnée du mot clé static
Exemple :
public class MaClasse() {
static int compteur = 0;
}
L’appartenance des variables de classe à une classe entière et non à un objet spécifique permet de remplacer le nom de la variable par le nom de la classe.
Exemple :
MaClasse m = new MaClasse();
int c1 = m.compteur;
int c2 = MaClasse.compteur;
c1 et c2 possèdent la même valeur.
Ce type de variable est utile pour par exemple compter le nombre d’instanciation de la classe qui est faite.
4.2.7. La variable this
Cette variable sert à référencer dans une méthode l’instance de l’objet en cours d’utilisation. this est un objet qui est égale à l’instance de l’objet dans lequel il est utilisé.
Exemple :
private int nombre;
public maclasse(int nombre) {
nombre = nombre; // variable de classe = variable en paramètre du constructeur
}
Il est préférable d’écrire
this.nombre = nombre;
Cette référence est habituellement implicite :
Exemple :
class MaClasse() {
String chaine = « test » ;
Public String getChaine() { return chaine) ;
// est équivalent à public String getChaine (this.chaine);
}
This est aussi utilisé quand l’objet doit appeler une méthode en se passant lui même en paramètre de l’appel.
4.2.8. L’opérateur instanceof
L’opérateur instanceof permet de déterminer la classe de l’objet qui lui est passé en paramètre. La syntaxe est objet instanceof classe
Exemple :
void testClasse(Object o) {
if (o instanceof MaClasse )
System.out.println(« o est une instance de la classe MaClasse »); else System.out.println(« o n’est pas un objet de la classe MaClasse »);
}
Il n’est toutefois pas possible d’appeler une méthode de l’objet car il est passé en paramètre avec un type Object
Exemple :
void afficheChaine(Object o) {
if (o instanceof MaClasse)
System.out.println(o.getChaine());
- erreur à la compil car la méthode getChaine() //n’est pas définie dans la classe Object
}
Pour résoudre le problème, il faut utiliser la technique du casting (conversion).
Exemple :
void afficheChaine(Object o) {
if (o instanceof MaClasse)
{
MaClasse m = (MaClasse) o;
System.out.println(m.getChaine());
// OU System.out.println( ((MaClasse) o).getChaine() );
}
}
4.3. Les modificateurs d’accès
Ils se placent avant ou après le type de l’objet mais la convention veut qu’ils soient placés avant.
Ils s’appliquent aux classes et/ou aux méthodes et/ou aux attributs.
Ils ne peuvent pas être utilisés pour qualifier des variables locales : seules les variables d’instances et de classes peuvent en profiter.
Ils assurent le contrôle des conditions d’héritage, d’accès aux éléments et de modification de données par les autres objets.
4.3.1. Les mots clés qui gèrent la visibilité des entités
De nombreux langages orientés objet introduisent des attributs de visibilité pour reglémenter l’accès aux classes et aux objets, aux méthodes et aux données.
Il existe 3 modificateurs qui peuvent être utilisés pour définir les attributs de visibilité des entités (classes, méthodes ou attributs) : public, private et protected. Leur utilisation permet de définir des niveaux de protection différents (présenté dans un ordre croissant de niveau de protection offert):
Role |
||
Une variable, méthode ou classe déclarée public est visible par tout les |
||
autres objets. Dans la version 1.0, une seule classe public est permise |
||
public |
par fichier et son nom doit correspondre à celui du fichier. Dans la |
|
philosophie orientée objet aucune donnée d’une classe ne devraient être |
||
déclarée publique : il est préférable d’écrire des méthodes pour la |
||
consulter et la modifier |
||
Il n’existe pas de mot clé pour définir ce niveau, qui est le niveau par |
||
par défaut : package friendly |
défaut lorsqu’aucun modificateur n’est précisé. Cette déclaration permet |
|
à une entité (classe, méthode ou variable) d’être visible par toutes les |
||
classes se trouvant dans le même package. |
||
Si une classe, une méthode ou une variable est déclarée protected , |
||
protected |
seules les méthodes présentes dans le même package que cette classe |
|
ou ses sous classes pourront y acceder. On ne peut pas qualifier une |
||
classe avec protected. |
||
C’est le niveau de protection le plus fort. Les composants ne sont |
||
visibles qu’à l’intérieur de la classe : ils ne peuvent être modifiés que |
||
private |
par des méthodes définies dans la classe prévues à cet effet. Les |
|
méthodes déclarée private ne peuvent pas être en même temps déclarée |
||
abstract car elles ne peuvent pas être redéfinies dans les classes filles. |
||
Ces modificateurs d’accès sont mutuellement exclusifs.
4.3.2. Le mot clé static
Le mot clé static s’applique aux variables et aux méthodes.
Les variables d’instance sont des variables propres à un objet. Il est possible de définir une variable de classe qui est partagée entre toutes les instances d’une même classe : elle n’existe donc qu’une seule fois. Une telle variable permet de stocker une constante ou une valeur modifiée tour à tour par les instances de la classe. Elle se définit avec le mot clé static.
Exemple :
public class Cercle {
static float pi = 3.1416f;
float rayon;
public Cercle(float rayon) { this.rayon = rayon; } public float surface() { return rayon * rayon * pi;}
}
Il est aussi possible par exemple de mémoriser les valeurs min et max d’un ensemble d’objet de même classe.
Une méthode static est une méthode qui n’agit pas sur des variables d’instance mais uniquement sur des variables de classe. Les méthodes ainsi définies peuvent être appelée avec la notation classe.méthode au lieu de objet.méthode. Ces méthodes peuvent être utilisées sans instancier un objet de la classe.
Il n’est pas possible d’appeler une méthode d’instance ou d’accéder à une variable d’instance à partir d’une méthode de classe statique
4.3.3. Le mot clé final
Le mot cle final s’applique aux variables, aux méthodes et aux classes.
Une variable qualifiée de final signifie que la variable est constante. Le compilateur ne contrôle ni n’empéche la modification. On ne peut déclarer de variables final locale à une méthode. Les constantes sont qualifiées de final et static
Exemple :
public static final float PI = 3.1416f;
Une méthode final ne peut pas être redéfinie dans une sous classe. Une méthode possédant le modificateur final pourra être optimisée par le compilateur car il est garanti qu’elle ne sera pas sous classée.
Lorsque le modificateur final est ajouté à une classe, il est interdit de créer un classe qui en hérite.
Pour une méthode ou une classe, on renonce à l’héritage mais ceci peut s’avérer nécessaire pour des questions de sécurité ou de performance. Le test de validité de l’appel d’une méthode est bien souvent repoussé à l’éxécution, en fonction du type de l’objet appelé (c’est le polymorphisme). Ces tests ont un coup en terme de performance.
4.3.4. Le mot clé abstract
Le mot cle abstract s’applique aux méthodes et aux classes.
Abstract indique que la classe ne pourra être instanciée telle quelle. De plus, toutes les méthodes de cette classe abstract ne sont pas implémentées et devront être redéfinies par des méthodes complètes dans ses sous classes.
Abstract permet de créer une classe qui sera une sorte de moule. Toutes les classes dérivées pouront profiter des méthodes héritées et n’auront à implémenter que les méthodes déclarées abstract.
Exemple :
abstract class ClasseAbstraite {
ClasseBastraite() { … //code du constructeur }
void méthode() { … // code partagé par tous les descendants} abstract void méthodeAbstraite();
}
class ClasseComplete extends ClasseAbstraite {
ClasseComplete() { super(); … }
void méthodeAbstraite() { … // code de la méthode } // void méthode est héritée
}
Une méthode abstraite est une méthode déclarée avec le modificateur abstract et sans corps. Elle correspond à une méthode dont on veut forcer l’implémentation dans une sous classe. L’abstraction permet une validation du codage : une sous classe sans le modificateur abstract et sans définition explicite d’une ou des méthodes abstraites génère une erreur de compilation.
Une classe est automatiquement abstraite dès qu’une de ses méthodes est déclarée abstraite. Il est possible de définir une classe abstraite sans méthodes abstraites.
4.3.5. Le mot clé synchronized
Permet de gérer l’accès concurrent aux variables et méthodes lors de traitement de thread (exécution « simultanée » de plusieurs petites parties de code du programme)
4.3.6. Le mot clé volatile
Le mot cle volatile s’applique aux variables.
Précise que la variable peut être changée par un périphérique ou de manière asynchrone. Cela indique au compilateur de ne pas stocker cette variable dans des registres. A chaque utilisation, on lit la valeur et on réécrit immédiatement le résultat s’il a changé.
4.3.7. Le mot clé native
Une méthode native est une méthode qui est implémentée dans un autre langage. L’utilisation de ce type de méthode limite la portabilité du code mais permet une vitesse exécution plus rapide.
4.4. Les propriétés ou attributs
Les données d’une classe sont contenues dans les propriétés ou attributs. Ce sont des variables qui peuvent être des variables d’instances, des variables de classes ou des constantes.
4.4.1. Les variables d’instances
Une variable d’instance nécessite simplement une déclaration de la variable dans le corps de la classe.
Exemple :
public class MaClasse {
public int valeur1 ;
int valeur2 ;
protected int valeur3 ;
private int valeur4 ;
}
Chaque instance de la classe a accès à sa propre occurrence de la variable.
4.4.2. Les variables de classes
Les variables de classes sont définies avec le mot clé static
Exemple ( code java 1.1 ) :
public class MaClasse {
static int compteur ;
}
Chaque instance de la classe partage la même variable.
4.4.3. Les constantes
Les constantes sont définies avec le mot clé final : leur valeur ne peut pas être modifiée.
Exemple ( code java 1.1 ) :
public class MaClasse {
final double pi=3.14 ;
}
4.5. Les méthodes
Les méthodes sont des fonctions qui implémentent les traitements de la classe.
4.5.1. La syntaxe de la déclaration
La syntaxe de la déclaration d’une méthode est :
modificateurs type_retourné nom_méthode ( arg1, … ) {… }
// définition des variables locales et du bloc d’instructions
}
Le type retourné peut être élémentaire ou correspondre à un objet. Si la méthode ne retourne rien, alors on utilise void
Le type est le nombre d’arguments déclarés doivent correspondrent au type et au nombre d’arguments transmis. Il n’est pas possible d’indiquer des valeurs par défaut dans les paramètres. Les arguments sont passés par valeur : la méthode fait une copie de la variable qui lui est locale. Lorsqu’un objet est transmis comme argument à une méthode, cette dernière reçoit une référence qui désigne son emplacement mémoire d’origine et qui est une copie de la variable . Il est possible de modifier l’objet grace à ces méthodes mais il n’est pas possible de remplacer la référence contenue dans la variable passée en paramètre : ce changement n’aura lieu que localement à la méthode.
Les modificateurs de méthodes sont :
Modificateur |
Role |
|
public |
la méthode est accéssible aux méthodes des autres classes |
|
private |
l’usage de la méthode est réservée aux autres méthodes de la même classe |
|
protected |
la méthode ne peut être invoquée que par des méthodes de la classe ou de ses sous |
|
classes |
||
final |
la méthode ne peut être modifiée (redifinition lors de l’héritage interdite) |
|
la méthode appartient simultanément à tous les objets de la classe (comme une |
||
static |
constante déclarée à l’intérieur de la classe). Il est inutile d’instancier la classe pour |
|
appeler la méthode mais la méthode ne peut pas manipuler de variable d’instance. |
||
Elle ne peut utiliser que des variables de classes. |
||
synchronized |
la méthode fait partie d’un thread. Lorsqu’elle est appelée, elle barre l’accès à son |
|
instance. L’instance est à nouveau libérée à la fin de son éxécution. |
||
native |
le code source de la méthode est écrit dans un autre langage |
|
Sans modificateur, la méthode peut être appelée par toutes autres méthodes des classes du package auquel appartient la
La valeur de retour de la méthode doit être transmise par l’instruction return. Elle indique la valeur que prend la méthode et termine celle ci : toutes les instructions qui suivent return sont donc ignorées.
Exemple :
int add(int a, int b) {
return a + b;
}
Il est possible d’inclure une instruction return dans une méthode de type void : cela permet de quitter la méthode.
La méthode main() de la classe principale d’une application doit être déclarée de la façon suivante :
Déclaration d’une méthode main() :
public static void main (String args[]) { … }
Si la méthode retourne un tableau alors les [] peuvent être préciser après le type de retour ou après la liste des paramètres :
Exemple :
int[] valeurs() { … }
int valeurs()[] { … }
4.5.2. La transmission de paramètres
Lorsqu’un objet est passé en paramètre, ce n’est pas l’objet lui même qui est passé mais une référence sur l’objet. La référence est bien transmise par valeur et ne peut pas être modifiée mais l’objet peut être modifié via un message (appel d’une méthode).
Pour transmettre des arguments par référence à une méthode, il faut les encapsuler dans un objet qui prévoit les méthodes nécessaires pour les mises à jour.
Si un objet o transmet sa variable d’instance v en paramètre à une méthode m, deux situations sont possibles :
- si v est une variable primitive alors elle est passée par valeur : il est impossible de la modifier dans m pour que v en retour contiennent cette nouvelle valeur.
- si v est un objet alors m pourra modifier l’objet en utilisant une méthode de l’objet passé en paramètre.
4.5.3. L’emmission de messages
Un message est émis lorsqu’on demande à un objet d’exécuter l’une de ses méthodes.
La syntaxe d’appelle d’une méthode est : nom_objet.nom_méthode(parametre, … ) ;
Si la méthode appelée ne contient aucun paramètre, il faut laisser les parenthèses vides.
4.5.4. L’enchainement de références à des variables et à des méthodes
Exemple :
System.out.Println(« bonjour »);
Deux classes sont impliqués dans l’instruction : System et PrintStream. La classe System possède une variable nommée out qui est un objet de type PrintStream. Println() est une méthode de la classe PrintStream. L’instruction signifie : « utilise la méthode Println() de la variable out de la classe System ».
4.5.5. La surcharge de méthodes
La surcharge d’une méthode permet de définir plusieurs fois une même méthode avec des arguments différents. Le compilateur choisi la méthode qui doit être appellée en fonction du nombre et du type des arguments. Ceci permet de simplifier l’interface des classes vis à vis des autres classes.
Une méthode est surchargée lorqu’elle exécute des actions différentes selon le type et le nombre de paramètres transmis.
Exemple :
class affiche{
public void afficheValeur(int i) {
System.out.pintln(« nombre entier = » + i);
}
public void afficheValeur(float f) { System.out.println(« nombre flottant = » + f);
}
}
Il n’est pas possible d’avoir deux méthodes de même nom dont tous les paramètres sont identiques et dont seul le type retourné diffère.
Exemple :
class Affiche{
public float convert(int i){
return((float) i);
}
public double convert(int i){
return((double) i);
}
}
Résultat à la compilation :
C:\>javac Affiche.java
Affiche.java:5: Methods can’t be redefined with a different return type: double
convert(int) was float convert(int)
public double convert(int i){
^
1 error
4.5.6. La signature des méthodes et le polymorphisme
Il est possible de donner le même nom à deux méthodes différentes à condition que les signatures de ces deux méthodes soient différentes. La signature d’une méthode comprend le nom de la classe, le nom de la méthode et les types des paramètres. Cette facilité permet de mettre en oeuvre la polymorphisme.
Le polymorphisme est la capacité, pour un même message de correspondre à plusieurs formes de traitements selon l’objet auquel ce message est adressé. La gestion du polymorphisme est assurée par la machine virtuelle dynamiquement à l’exécution
4.5.7. Les constructeurs
La déclaration d’un objet est suivie d’une sorte d’initialisation par le moyen d’une méthode particulière appelée constructeur pour que les variables aient une valeur de départ. Elle n’est systématiquement invoquée que lors de la création d’un objet.
Le constucteur suit la définition des autres méthodes excepté que son nom doit obligatoirement correspondre à celui de la classe et qu’il n’est pas typé, pas même void, donc il ne peut pas y avoir d’instruction return dans un constructeur. On peut surcharger un constructeur.
La définition d’un constructeur est facultative. Si elle n’est pas définie, la machine virtuelle appelle un constructeur par défaut vide créé automatiquement. Dès qu’un constructeur est explicitement défini, Java considère que le programmeur prend en charge la création des constructeurs et que le mécanisme par défaut, qui correspond à un constructeur sans paramètres, est supprimé. Si on souhaite maintenir ce mécanisme, il faut définir explicitement un constructeur sans paramètres.
Il existe plusieurs manière de définir un constructeur :
- le constructeur simple : ce type de constructeur ne nécessite pas de définition explicite : son existence découle automatiquement de la définition de la classe.
Exemple :
public MaClasse() {}
2. le constructeur avec initialisation fixe : il permet de créer un constructeur par défaut
Exemple :
public MaClasse() {
nombre = 5;
}
- le constructeur avec initialisation des variables : pour spécifier les valeurs de données à initialiser on peut les passer en paramètres au constructeur
Exemple :
public MaClasse(int valeur) {
nombre = valeur;
}
4.5.8. Le destructeur
Un destructeur permet d’éxécuter du code lors de la libération de la place mémoire occupée par l’objet. En java, les destructeurs appellés finaliseurs (finalizers), sont automatiquement appellés par le garbage collector.
Pour créer un finaliseur, il faut redéfinir la méthode finalize() héritée de la classe Object.
4.5.9. Les accesseurs
L’encapsulation permet de sécuriser l’accès aux données d’une classe. Ainsi, les données déclarées private à l’intérieur d’une classe ne peuvent être accédées et modifiées que par des méthodes définies dans la même classe. Si une autre classe veut accéder aux données de la classe, l’opération n’est possible que par l’intermédiaire d’une méthode de la classe prévue à cet effet. Ces appels de méthodes sont appelés « échanges de message ».
Un accesseur est une méthode publique qui donne l’accès à une variable d’instance privée. Pour une variable d’instance, il peut ne pas y avoir d’accesseur, un seul accesseur en lecture ou un accesseur en lecture et un en écriture. Par convention, les accesseurs en lecture commencent par get et les accesseurs en écriture commencent par set.
Exemple :
private int valeur = 13;
public int getValeur(){
return(valeur);
}
public void setValeur(int val) {
valeur = val;
}
4.6. L’héritage
4.6.1. Le principe de l’héritage
L’héritage est un mécanisme qui facilite la réutilisation du code et la gestion de son évolution.
Grace à l’heritage, les objets d’une classe ont accès aux données et aux méthodes de la classe parent et peuvent les étendre. Les sous classes peuvent redéfinir les variables et les méthodes héritées. Pour les variables, il suffit de les redéclarer sous le même nom avec un type différent. Les méthodes sont redéfinies avec le même nom, les mêmes types et le même nombre d’arguments, sinon il s’agit d’une surcharge.
L’héritage successif de classes permet de définir une hiérarchie de classe qui ce compose de super classes et de sous classes. Une classe qui hérite d’une autre est une sous classe et celle dont elle hérite est une super classe. Une classe peut avoir plusieurs sous classes. Une classe ne peut avoir qu’une seule classe mère : il n’y a pas d’héritage multiple en java.
Object est la classe parente de toutes les classes en java. Toutes les variables et méthodes contenues dans Object sont accessibles à partir de n’importe quelle classe car par héritage succéssif toutes les classes héritent d’Object.
4.6.2. La mise en oeuvre de l’héritage
On utilise le mot clé extends pour indiquer qu’une classe hérite d’une autre. En l’absence de ce mot réservé associé à une classe, le compilateur considère la classe Object comme classe parent.
Exemple :
class Fille extends Mere { … }
Pour invoquer une méthode d’une classe parent, il suffit d’indiquer la méthode préfixée par super. Pour appeler le constructeur de la classe parent il suffit d’écrire super(paramètres) avec les paramètres adéquats.
Le lien entre une classe fille et une classe parent est géré par le langage : une évolution des règles de gestion de la classe parent conduit à modifier automatiquement la classe fille dès que cette dernière est recompilée.
En java, il est obligatoire dans un constructeur d’une classe fille de faire appel explicitement ou implicitement au constructeur de la classe mère.
4.6.3. L’accès aux propriétés héritées
Les variables et méthodes définies avec le modificateur d’accès public restent publiques à travers l’héritage et toutes les autres classes.
Une variable d’instance définie avec le modificateur private est bien héritée mais elle n’est pas accessible directement mais via les méthodes héritées.
Si l’on veut conserver pour une variable d’instance une protection semblable à celle assurée par le modificateur private, il faut utiliser le modificateur protected. La variable ainsi définie sera hérité dans toutes les classes descendantes qui pourront y accéder librement mais ne sera pas accessible hors de ces classes directement.
4.6.4 Le transtypage induit par l’héritage facilitent le polymorphisme
L’héritage définit un cast implicite de la classe fille vers la classe mere : on peut affecter à une référence d’une classe n’importe quel objet d’une de ses sous classes.
Exemple : la classe Employe hérite de la classe Personne
Personne p = new Personne («Dupond», «Jean»); Employe e = new Employe(«Durand», «Julien», 10000);
p = e ; // ok : Employe est une sous classe de Personne Objet obj;
obj = e ; // ok : Employe herite de Personne qui elle même hérite de Object
Il est possible d’écrire le code suivant si Employe hérite de Personne
Exemple :
Personne[] tab = new Personne[10];
tab[0]:= new Personne( «Dupond»,«Jean»);
tab[1]:= new Employe(«Durand», «Julien», 10000);
Il est possible de surcharger une méthode héritée : la forme de la méthode à éxécuter est choisie en fonction des paramètres associés à l’appel.
Compte tenu du principe de l’héritage, le temps d’exécution du programme et la taille du code source et de l’éxécutable augmentent.
4.6.5. La redéfinition d’une méthode héritée
La redéfinition d’une méthode héritée doit impérativement conserver la déclaration de la méthode parent (type et nombre de paramètres, la valeur de retour et les exceptions propagées doivent être identique).
Si la signature de la méthode change, ce n’est plus une redéfinition mais une surcharge. Cette nouvelle méthode n’est pas héritée : la classe mere ne possède pas de méthode possédant cette signature.
4.6.6. Les interfaces et l’héritage multiple
Avec l’héritage multiple, une classe peut hériter en même temps de plusieurs super classes. Ce mécanisme n’existe pas en java. Les interfaces permettent de mettre en œuvre un mécanisme de remplacement.
Une interface est un ensemble de constantes et de déclarations de méthodes correspondant un peu à une classe abstraite. C’est une sorte de standard auquel une classe peut répondre. Tous les objets qui se conforment à cette interface (qui implémentent cette interface) possèdent les méthodes et les constantes déclarée dans celle−ci. Plusieurs interfaces peuvent être implémentées dans une même classe.
Les interfaces se déclarent avec le mot cle interface et sont intégrées aux autres classes avec le mot clé implements. Une interface est implicitement déclarée avec le modificateur abstract.
Déclaration d’une interface :
[public] interface nomInterface [extends nomInterface1, nomInterface2 … ] { // insérer ici des méthodes ou des champs static
}
Implémentation d’une interface :
Modificateurs class nomClasse [extends superClasse] [implements nomInterface1, nomInterface 2, …] {
//insérer ici des méthodes et des champs
}
Exemple :
interface AfficheType {
void AfficheType();
}
class Personne implements AfficheType {
public void afficheType() {
System.out.println(« Je suis une personne »);
}
}
class Voiture implements AfficheType {
public void afficheType() {
System.out.println(« Je suis une voiture »);
}
}
Exemple : déclaration d’un interface à laquelle doit se conformer tout individus
interface Individu {
String getNom();
String getPrenom();
Date getDateNaiss();
}
Toutes les méthodes d’une interface sont publiques et abstraites : elles sont implicitement déclarées comme telles.
Une interface peut être d’accès public ou package. Si elle est publique, toutes ses méthodes sont publiques même si elles ne sont pas déclarées avec le modificateur public. Si elle est d’accès package, il s’agit d’une interface d’implémentation pour les autres classes du package et ses méthodes ont le même accès package : elles sont accessible à toutes les classes du packages.
Les seules variables que l’on peut définir dans une interface sont des variables de classe qui doivent être constantes : elles sont donc implicitement déclarées avec le modificateur static et final même si elles sont définies avec d’autres modificateurs.
Toute classe qui implémente cette interface doit au moins posséder les méthodes qui sont déclarées dans l’interface.
L’interface ne fait que donner une liste de méthodes qui seront à définir dans les classes qui implémentent l’interface.
Les méthodes déclarées dans une interface publique sont implicitement publiques et elles sont héritées par toutes les classes qui implémentent cette interface. Une tel classe doit, pour être instanciable, définir toutes les méthodes héritées de l’interface.
Une classe peut implémenter une ou plusieurs interfaces tout en héritant de sa classe mère.
L’implémentation d’une interface définit un cast : l’implémentation d’une interface est une forme d’héritage. Comme pour l’héritage d’une classe, l’héritage d’une classe qui implémente une interface définit un cast implicite de la classe fille vers cette interface. Il est important de noter que dans ce cas il n’est possible de faire des appels qu’à des méthodes de l’interface. Pour utiliser des méthodes de l’objet, il faut définir un cast explicite : il est préférable de controler la classe de l’objet pour éviter une exception ClassCastException à l’éxecution
4.6.7. Des conseils sur l’héritage
Lors de la création d’une classe « mère » il faut tenir compte des points suivants :
- la définition des accès aux variables d’instances, très souvent privées, doit être réfléchie entre protected et private
- pour empecher la redéfinition d’une méthode (surcharge) il faut la déclarer avec le modificateur final
Lors de la création d’une classe fille, pour chaque méthode héritée qui n’est pas final, il faut envisager les cas suivant :
- la méthode héritée convient à la classe fille : on ne doit pas la redéfinir
- la méthode héritée convient mais partiellement du fait de la spécialisation apportée par la classe fille : il faut la redéfinir voir la surcharger. La plupart du temps une redéfinition commencera par appeler la méthode héritée (via super) pour garantir l ‘évolution du code
- la méthode héritée ne convient pas : il faut redéfinir ou surcharger la méthode sans appeler la méthode héritée lors de la redéfinition.
4.7. Les packages
4.7.1. La définition d’un package
En java, il existe un moyen de regrouper des classe voisines ou qui couvrent un même domaine : ce sont les packages. Pour réaliser un package, on écrit un nombre quelconque de classes dans plusieurs fichiers d’un même repertoire et au début de chaque fichier on met la directive ci dessous ou nom−du−package doit être identique au nom du répertoire :
package nomPackage;
La hiérarchie d’un package se retrouve dans l’arborescence du disque dur puisque chaque package est dans un répertoire nommé du nom du package.
Remarque : Il est préférable de laisser les fichiers source .java avec les fichiers compilés .class
D’une façon générale, l’instruction package associe toutes les classes qui sont définies dans un fichier source à un même package.
Le mot clé package doit être la première instruction dans un fichier source et il ne doit être présent qu’une seule fois dans le fichier source (une classe ne peut pas appartenir à plusieurs packages).
4.7.2. L’utilisation d’un package
Pour utiliser ensuite le package ainsi créé, on l’importe dans le fichier :
import nomPackage.*;
Pour importer un package, il y a trois méthodes si le chemin de recherche est correctement renseigné :
Exemple |
Role |
|
import nomPackage; |
les classes ne peuvent pas être simplement désignées par leur nom et il |
|
faut aussi préciser le nom du package |
||
import nomPackage.*; |
toutes les classes du package sont importées |
|
import nomPackage.nomClasse; |
appel à une seule classe : l’avantage de cette notation est de réduire le |
|
temps de compilation |
||
Attention : l’astérisque n’importe pas les sous paquetages. Par exemple, il n’est pas possible d’écrire import java.*.
Il est possible d’appeler une méthode d’un package sans inclure ce dernier dans l’application en precisant son nom complet :
nomPackage.nomClasse.nomméthode(arg1, arg2 … )
Il existe plusieurs types de packages : le package par défaut (identifié par le point qui représente le répertoire courant et permet de localiser les classes qui ne sont pas associées à un package particulier), les packages standards qui sont empaquetés dans le fichier classes.zip et les packages personnels
Le compilateur implémente automatiquement une commande import lors de la compilation d’un programme Java même si elle ne figure pas explicitement au début du programme : import java.lang.*; Ce package contient entre autre les classes de base de tous les objets java dont la classe Object.
Un package par défaut est systématiquement attribué par le compilateur aux classes qui sont définies sans déclarer explicitement une appartenance à un package. Ce package par défaut correspond au répertoire courant qui est le
4.7.3. La collision de classes.
Deux classes entre en collision lorsqu’elles portent le même nom mais qu’elles sont définies dans des packages différents.
Dans ce cas, il faut qualifier explicitement le nom de la classe avec le nom complet du package.
4.7.4. Les packages et l’environnement système
Les classes Java sont importées par le compilateur (au moment de la compilation) et par la machine virtuelle (au moment de l’éxécution). Les techniques de chargement des classes varient en fonction de l’implémentation de la machine virtuelle. Dans la plupart des cas, une variable d’environnement CLASSPATH référence tous les répertoires qui hébergent des packages succeptibles d’être importés.
Exemple sous Windows :
CLASSPATH = .;C:\Java\JDK\Lib\classes.zip; C:\rea_java\package
L’importation des packages ne fonctionne que si le chemin de recherche spécifié dans une variable particulière pointe sur les packages, sinon le nom du package devra refléter la structure du répertoire ou il se trouve. Pour déterminer l’endroit où se trouvent les fichiers .class à importer, le compilateur utilise une variable d’environnement dénommée CLASSPATH. Le compilateur peut lire les fichiers .class comme des fichiers indépendants ou comme des fichiers ZIP dans lesquels les classes sont reunies et compressées.
4.8. Les classes internes
Les classes internes (inner classes) sont une extension du langage java introduite dans la version 1.1 du JDK. Ce sont des classes qui sont définies dans une autre classe. Les difficultés dans leur utilisation concerne leur visibilité et leur accès aux membres de la classe dans laquelle elles sont définies.
Exemple très simple :
public class ClassePrincipale1 {
class ClasseInterne {
}
}
Les classes internes sont particulièrement utiles pour :
- permettre de définir une classe à l’endroit ou une seule autre en a besoin
- définir des classes de type adapter (essentiellement à partir du JDK 1.1 pour traiter des évenements émis par les interfaces graphiques)
- définir des méthodes de type callback d’une façon générale
Pour permettre de garder une compatibilité avec la version précédente de la JVM, seul le compilateur a été modifié. Le compilateur interprète la syntaxe des classes internes pour modifier le code source et générer du byte code compatible avec la première JVM.
Il est possible d’imbriquer plusieurs classes internes. Java ne possèdent pas de restrictions sur le nombre de classes qu’il est ainsi possible d’imbriquer. En revanche une limitation peut intervenir au niveau du système d’exploitation en ce qui concerne la longueur du nom du fichier .class généré pour les différentes classes internes.
Si plusieurs classes internes sont imbriquées, il n’est pas possible d’utiliser un nom pour la classe qui soit déjà attribuée à une de ces classes englobantes. Le compilateur génèrera une erreur à la compilation.
Exemple :
public class ClassePrincipale6 {
class ClasseInterne1 {
class ClasseInterne2 {
class ClasseInterne3 {
}
}
}
}
Le nom de la classe interne utilise la notation qualifié avec le point préfixé par le nom de la classe principale. Ainsi, pour utiliser ou accéder à une classe interne dans le code, il faut la préfixer par le nom de la classe principale suivi d’un point.
Cependant cette notation ne représente pas physiquement le nom du fichier qui contient le byte code. Le nom du fichier qui contient le byte code de la classe interne est modifié par le compilateur pour éviter des conflits avec d’autres nom d’entité : à partir de la classe principale, les points de séparation entre chaque classe interne sont remplacé par un caractère $ (dollard).
Par exemple, la compilation du code de l’exemple précédent génère quatres fichiers contenant le byte code : ClassePrincipale6$ClasseInterne1$ClasseInterne2ClasseInterne3.class ClassePrincipale6$ClasseInterne1$ClasseInterne2.class ClassePrincipale6$ClasseInterne1.class
ClassePrincipale6.class
L’utilisation du signe $ entre la classe principale et la classe interne permet d’éviter des confusions de nom entre le nom d’une classe appartenant à un package et le nom d’une classe interne.
L’avantage de cette notation est de créer un nouvel espace de nommage qui dépend de la classe et pas d’un package. Ceci renforce le lien entre la classe interne et sa classe englobante.
C’est le nom du fichier qu’il faut préciser lorsque l’on tente de charger la classe avec la méthode forName() de la classe Class. C’est aussi sous cette forme qu’est restitué le résultat d’un appel aux méthodes getClass().getName() sur un objet qui est une classe interne.
Exemple :
public class ClassePrincipale8 {
public class ClasseInterne {
}
public static void main(String[] args) { ClassePrincipale8 cp = new ClassePrincipale8(); ClassePrincipale8.ClasseInterne ci = cp. new ClasseInterne() ; System.out.println(ci.getClass().getName());
}
}
Resultat :
java ClassePrincipale8
ClassePrincipale8$ClasseInterne
L’accessibilité à la classe interne respecte les règles de visibilité du langage. Il est même possible de définir une classe interne private pour limiter son accès à sa seule classe principale.
Exemple :
public class ClassePrincipale7 {
private class ClasseInterne {
}
}
Il n’est pas possible de déclarer des membres statiques dans une classe interne :
Exemple :
public class ClassePrincipale10 {
public class ClasseInterne {
static int var = 3;
}
}
Resultat :
javac ClassePrincipale10.java
ClassePrincipale10.java:3: Variable var can’t be static in inner class ClassePri ncipale10. ClasseInterne. Only members of interfaces and top−level classes can be static.
static int var = 3;
^
1 error
Pour pouvoir utiliser une variable de classe dans une classe interne, il faut la déclarer dans sa classe englobante.
Il existe quatres types de classes internes :
- les classes internes non statiques : elles sont membres à part entière de la classe qui les englogent et peuvent accéder à tous les membres de cette dernière
- les classes internes locales : elles sont définies dans un block de code. Elles peuvent être static ou non.
- les classes internes anonymes : elles sont définies et instanciées à la volée sans posséder de nom
- les classe internes statiques : elles sont membres à part entière de la classe qui les englogent et peuvent accéder uniquement aux membres statiques de cette dernière
4.8.1. Les classes internes non statiques
Les classes internes non statiques (member inner−classes) sont définies dans une classe dite » principale » (top−level class) en tant que membre de cette classe. Leur avantage est de pouvoir accéder aux autres membres de la classe principale même ceux déclarés avec le modificateur private.
Exemple :
public class ClassePrincipale20 {
private int valeur = 1;
class ClasseInterne {
public void afficherValeur() {
System.out.println(« valeur = « +valeur);
}
}
public static void main(String[] args) { ClassePrincipale20 cp = new ClassePrincipale20(); ClasseInterne ci = cp. new ClasseInterne(); ci.afficherValeur();
}
}
C:\testinterne>javac ClassePrincipale20.java
C:\testinterne>java ClassePrincipale20
valeur = 1
Le mot clé this fait toujours référence à l’instance en cours. Ainsi this.var fait référence à la variable var de l’instance courante. L’utilisation du mot clé this dans une classe interne fait donc référence à l’instance courante de cette classe interne.
Exemple :
public class ClassePrincipale16 {
class ClasseInterne {
int var = 3;
public void affiche() {
System.out.println(« var = « +var);
System.out.println(« this.var = « +this.var);
}
}
ClasseInterne ci = this. new ClasseInterne();
public static void main(String[] args) { ClassePrincipale16 cp = new ClassePrincipale16(); ClasseInterne ci = cp. new ClasseInterne(); ci.affiche();
}
}
Resultat :
C:\>java |
ClassePrincipale16 |
|
var |
= |
3 |
this.var |
= |
3 |
Une classe interne a accès à tous les membres de sa classe principale. Dans le code, pour pouvoir faire référence à un membre de la classe principale, il suffit simplement d’utiliser son nom de variable.
Exemple :
public class ClassePrincipale17 {
int valeur = 5;
class ClasseInterne {
int var = 3;
public void affiche() {
System.out.println(« var = « +var);
System.out.println(« this.var = « +this.var);
System.out.println(« valeur = « +valeur);
}
}
ClasseInterne ci = this. new ClasseInterne();
public static void main(String[] args) { ClassePrincipale17 cp = new ClassePrincipale17(); ClasseInterne ci = cp. new ClasseInterne(); ci.affiche();
}
}
C:\testinterne>java ClassePrincipale17
var |
= 3 |
this.var = 3
valeur = 5
La situation se complique un peu plus, si la classe principale et la classe interne possède tous les deux un membre de même nom. Dans ce cas, il faut utiliser la version qualifée du mot clé this pour accéder au membre de la classe principale. La qualification se fait avec le nom de la classe principale ou plus généralement avec le nom qualifié d’une des classes englobantes.
Exemple :
public class ClassePrincipale18 {
int var = 5;
class ClasseInterne {
int var = 3;
public void affiche() {
System.out.println(« var = « +var);
System.out.println(« this.var = « +this.var);
System.out.println(« ClassePrincipale18.this.var = «
+ClassePrincipale18.this.var);
}
}
ClasseInterne ci = this. new ClasseInterne();
public static void main(String[] args) {
ClassePrincipale18 cp = new ClassePrincipale18();
ClasseInterne ci = cp. new ClasseInterne();
ci.affiche();
}
}
Resultat :
C:\>java ClassePrincipale18 |
||
var |
= 3 |
|
this.var |
= |
3 |
ClassePrincipale18.this.var |
= |
5 |
Comme une classe interne ne peut être nommée du même nom que l’une de ces classes englobantes, ce nom qualifié est unique et il ne risque pas d’y avoir de confusion.
Le nom qualifié d’une classe interne est nom_classe_principale.nom_classe_interne. C’est donc le même principe que celui utilisé pour qualifié une classe contenue dans un package. La notation avec le point est donc légèrement étendue.
L’accès au membre de la classe principale est possible car le compilateur modifie le code de la classe principale et celui de la classe interne pour fournir à la classe interne une référence sur la classe principale.
Le code de la classe interne est modifié pour :
- ajouter une variable privée finale du type de la classe principale nommée this$0
- ajouter un paramètre supplémentaire dans le constructeur qui sera la classe principale et qui va initialiser la variable this$0
- utiliser cette variable pour préfixer les attributs de la classe principale utilisés dans la classe interne.
La code de la classe principale est modifié pour :
- ajouter une méthode static pour chaque champ de la classe principale qui attend en paramètre un objet de la classe principale. Cette méthode renvoie simplement la valeur du champ. Le nom de cette méthode est de la forme access$0
- modifier le code d’instanciation de la classe interne pour appeler le constructeur modifié
Dans le byte code généré, une variable privée finale contient une référence vers la classe principale. Cette variable est nommée this$0. Comme elle est générée par le compilateur, cette variable n’est pas utilisable dans le code source. C’est à partir de cette référence que le compilateur peut modifier le code pour accéder aux membres de la classe principale.
Pour pouvoir avoir acces aux membres de la classe principale, le compilateur génère dans la classe principale des accesseurs sur ces membres. Ainsi, dans la classe interne, pour accéder à un membre de la classe principale, le compilateur appelle un de ces accesseurs en utilisant la référence stockée. Ces méthodes ont un nom de la forme access$numero_unique et sont bien sûre inutilisables dans le code source puisqu’elles sont générées par le compilateur.
En tant que membre de la classe principale, une classe interne peut être déclarée avec le modificateur private ou protected.
Une classe peut faire référence dans le code source à son unique instance lors de l’éxécution via le mot clé this. Une classe interne possède au moins deux références :
- l’instance de la classe interne elle même
- éventuellement les instances des classes internes dans laquelle la classe interne est imbriquée
- l’instance de sa classe principale
Dans la classe interne, il est possible pour accéder à une de ces instances d’utiliser le mot clé this préfixé par le nom de la
classe suivi d’un point :
nom_classe_principale.this
nom_classe_interne.this
Le mot this seul désigne toujours l’instance de la classe courante dans son code source, donc this seul dans une classe interne désigne l’instance de cette classe interne.
Une classe interne non statique doit toujours être instanciée relativement à un objet implicite ou explicite du type de la classe principale. A la compilation, le compilateur ajoute dans la classe interne une référence vers la classe principale contenu dans une variable privée nommée this$0. Cette référence est initialisée avec un paramètre fourni au constructeur de la classe interne. Ce mécanisme permet de lier les deux instances.
La création d’une classe interne nécessite donc obligatoirement une instance de sa classe principale. Si cette instance n’est pas accessible, il faut en créer une et utiliser une notation particulière de l’opérateur new pour pouvoir instancier la classe interne. Par défaut, lors de l’instanciation d’une classe interne, si aucune instance de la classe principale n’est utilisée, c’est l’instance courante qui est utilisée (mot clé this).
Exemple :
public class ClassePrincipale14 {
class ClasseInterne {
}
ClasseInterne ci = this. new ClasseInterne();
}
Pour créer une instance d’une classe interne dans une méthode statique de la classe principale, (la méthode main() par exemple), il faut obligatoirement instancier un objet de la classe principale avant et utiliser cet objet lors de la création de l’instance de la classe interne. Pour créer l’instance de la classe interne, il faut alors utiliser une syntaxe particulière de l’opérateur new.
Exemple :
public class ClassePrincipale15 {
class ClasseInterne {
}
ClasseInterne ci = this. new ClasseInterne();
static void maMethode() {
ClassePrincipale15 cp = new ClassePrincipale15(); ClasseInterne ci = cp. new ClasseInterne();
}
}
Il est possible d’utiliser une syntaxe condensée pour créer les deux instances en une seul et même ligne de code.
Exemple :
public class ClassePrincipale19 {
class ClasseInterne {
}
static void maMethode() {
ClasseInterne ci = new ClassePrincipale19(). new ClasseInterne();
}
}
Une classe peut hériter d’une classe interne. Dans ce cas, il faut obligatoirement fournir aux contructeurs de la classe une référence sur la classe principale de la classe mère et appeler explicitement dans le constructeur le constructeur de cette classe principale avec une notation particulière du mot clé super
Exemple :
public class ClassePrincipale9 {
public class ClasseInterne {
}
class ClasseFille extends ClassePrincipale9.ClasseInterne { ClasseFille(ClassePrincipale9 cp) {
cp. super();
}
}
}
Une classe interne peut être déclarée avec les modificateurs final et abstract. Avec le modificateur final, la classe interne ne pourra être utilisée comme classe mère. Avec le modificateur abstract, la classe interne devra être étendue pour pouvoir être instanciée.
4.1. Les classes internes locales
Ces classes internes locales (local inner−classes) sont définies à l’intérieure d’une méthode ou d’un bloc de code. Ces classes ne sont utilisables que dans le bloc de code où elles sont définies. Les classes internes locales ont toujours accès aux membres de la classe englobante.
Exemple :
public class ClassePrincipale21 {
int varInstance = 1;
public static void main(String args[]) { ClassePrincipale21 cp = new ClassePrincipale21(); cp.maMethode();
}
class ClasseInterne {
public void affiche() {
System.out.println(« varInstance = » + varInstance);
}
}
ClasseInterne ci = new ClasseInterne();
ci.affiche();
}
}
Resultat :
C:\testinterne>javac ClassePrincipale21.java
C:\testinterne>java ClassePrincipale21
varInstance = 1
Leur particularité, en plus d’avoir un accès aux membres de la classe principale, est d’avoir aussi un accès à certaines variables locales du bloc ou est définie la classe interne.
Ces variables définies dans la méthode (variables ou paramètres de la méthode) sont celles qui le sont avec le mot clé final. Ces variables doivent être initialisée avant leur utilisation par la classe interne. Celles ci sont utilisables n’importe ou dans le code de la classe interne.
Le modificateur final désigne une variable dont la valeur ne peut être changée une fois qu’elle a été initialisée.
Exemple :
public class ClassePrincipale12 {
public static void main(String args[]) { ClassePrincipale12 cp = new ClassePrincipale12(); cp.maMethode();
}
public void maMethode() {
int varLocale = 3;
class ClasseInterne {
public void affiche() {
System.out.println(« varLocale = » + varLocale);
}
}
ClasseInterne ci = new ClasseInterne();
ci.affiche();
}
}
Resultat :
javac ClassePrincipale12.java
ClassePrincipale12.java:14: Attempt to use a non−final variable varLocale from a different method. From enclosing blocks, only final local variables are availab le.
System.out.println(« varLocale = » + varLocale);
^
1 error
Cette restriction est imposée par la gestion du cycle de vie d’une variable locale. Une telle variable n’existe que durant l’éxécution de cette méthode. Une variable finale est une variable dont la valeur ne peut être modifiée après son initialisation. Ainsi, il est possible sans risque pour le compilateur d’ajouter un membre dans la classe interne et de copier le contenu de la variable finale dedans.
Exemple :
public class ClassePrincipale13 {
public static void main(String args[]) { ClassePrincipale13 cp = new ClassePrincipale13(); cp.maMethode();
}
public void maMethode() {
final int varLocale = 3;
class ClasseInterne {
public void affiche(final int varParam) { System.out.println(« varLocale = » + varLocale); System.out.println(« varParam = » + varParam);
}
}
ClasseInterne ci = new ClasseInterne();
ci.affiche(5);
}
}
Resultat :
C:\>javac ClassePrincipale13.java
C:\>java ClassePrincipale13
varLocale = 3
varParam = 5
Pour permettre à une classe interne locale d’accéder à une variable locale utilisée dans le bloc de code ou est définie la classe interne, la variable doit être stockée dans un endroit ou la classe interne pourra y accéder. Pour que cela fonctionne, le compilateur ajoute les variables nécessaires dans le constructeur de la classe interne.
Les variables accédées sont dupliquées dans la classe interne par le compilateur. Il ajoute pour chaque variable un membre privé dans la classe interne dont le nom est de la forme val$nom_variable. Comme la variable accédée est déclarée finale, cette copie peut être faite sans risque. La valeur de chacune de ces variables est fournie en paramètre du constructeur qui a été modifié par le compilateur.
Une classe qui est définie dans un bloc de code n’est pas un membre de la classe englobante : elle n’est donc pas accessible en dehors du bloc de code ou elle est définie. Ces restrictions sont équivalentes à la déclaration d’une variable dans un bloc de code.
Les variables ajoutées par le compilateur sont préfixées par this$ et val$. Ces variables et le constructeur modifié par le compilateur ne sont pas utilisables dans le code source.
Etant visible uniquement dans le bloc de code qui la définie, une classe interne locale ne peut pas utiliser les modificateurs public, private, protected et static dans sa définition. Leur utilisation provoque une erreur à la compilation.
Exemple :
public class ClassePrincipale11 {
public void maMethode() {
public class ClasseInterne {
}
}
}
javac ClassePrincipale11.java
ClassePrincipale11.java:2: ‘}’ expected.
public void maMethode() {
^
ClassePrincipale11.java:3: Statement expected.
public class ClasseInterne {
^
ClassePrincipale11.java:7: Class or interface declaration expected.
}
^
3 errors
4.8.3. Les classes internes anonymes
Les classes internes anonymes (anonymous inner−classes) sont des classes internes qui ne possèdent pas de nom. Elles ne peuvent donc être instanciées qu’à l’endroit ou elles sont définies.
Ce type de classe est très pratique lorsqu’une classe doit être utilisée une seule fois : c’est par exemple le cas d’une classe qui doit être utilisée comme un callback.
Une syntaxe particulière de l’opérateur new permet de déclarer et instancier une classe interne :
new classe_ou_interface () {
- définition des attributs et des méthodes de la classe interne
}
Cette syntaxe particulière utilise le mot clé new suivi d’un nom de classe ou interface que la classe interne va respectivement étendre ou implémenter. La définition de la classe suit entre deux accolades. Une classe interne anonyme peut soit hériter d’une classe soit implémenter une interface mais elle ne peut pas explicitement faire les deux.
Si la classe interne étend une classe, il est possible de fournir des paramètres entre les parenthèses qui suivent le nom de la classe. Ces arguments éventuels fournis au moment de l’utilisation de l’opérateur new sont passés au constructeur de la super classe. En effet, comme la classe ne possède pas de nom, elle ne possède pas non plus de constructeur.
Les classes internes anonymes qui implémentent une interface héritent obligatoirement de classe Object. Comme cette classe ne possèdent qu’un constructeur sans paramètre, il n’est pas possible lors de l’instanciation de la classe interne de lui fournir des paramètres.
Une classe interne anonyme ne peut pas avoir de constructeur puisqu’elle ne possède pas de nom mais elle peut avoir des initialisateurs.
Exemple :
public void init() {
boutonQuitter.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
);
}
Les classes anonymes sont un moyen pratique de déclarer un objet sans avoir a lui trouver un nom. La contre partie est que cette classe ne pourra être instanciée dans le code qu’à l’endroit ou elle est définie : elle est déclarée et instanciée en un seul et unique endroit.
Le compilateur génère un fichier ayant pour nom la forme suivante : nom_classe_principale$numéro_unique. En fait, le compilateur attribut un numéro unique à chaque classe interne anonyme et c’est ce numéro qui est donné au nom du fichier préfixé par le nom de la classe englobante et d’un signe ‘$’.
4.8.4 Les classes internes statiques
Les classes internes statiques (static member inner−classes) sont des classes internes qui ne possèdent pas de référence vers leur classe principale. Elles ne peuvent donc pas accéder aux membres d’instance de leur classe englobante. Elles peuvent toutefois avoir accès aux variables statiques de la classe englobante.
Pour les déclarer, il suffit d’utiliser en plus le modificateur static dans la déclaration de la classe interne.
Leur utilisation est obligatoire si la classe est utilisée dans une méthode statique qui par définition peut être appelée dans avoir d’instance de la classe et que l’on ne peut pas avoir une instance de la classe englobante. Dans le cas contraire, le compilateur indiquera une erreur :
Exemple :
public class ClassePrincipale4 {
class ClasseInterne {
public void afficher() {
System.out.println(« bonjour »);
}
}
public static void main(String[] args) {
new ClasseInterne().afficher();
}
}
Resultat :
javac ClassePrincipale4.java
ClassePrincipale4.java:10: No enclosing instance of class ClassePrincipale4 is i n scope; an explicit one must be provided when creating inner class ClassePrinci pale4. ClasseInterne, as in « outer. new Inner() » or « outer. super() ».
new ClasseInterne().afficher();
^
1 error
En déclarant la classe interne static, le code se compile et peut être exécuté
Exemple :
public class ClassePrincipale4 {
static class ClasseInterne {
public void afficher() {
System.out.println(« bonjour »);
}
}
public static void main(String[] args) {
new ClasseInterne().afficher();
}
}
Resultat :
javac ClassePrincipale4.java
bonjour
Comme elle ne possède pas de référence sur sa classe englobante, une classe interne statique est en fait traduite par le compilateur comme une classe principale. En fait, il est difficile de les mettre dans une catégorie (classe principale ou classe interne) car dans le code source c’est une classe interne (classe définie dans une autre) et dans le byte code généré c’est une classe principale.
Ce type de classe n’est pas très employée.
4.9. La gestion dynamique des objets
Tout objet appartient à une classe et Java sait la reconnaitre dynamiquement.
Java fournit dans son API un ensemble de classes qui permettent d’agir dynamiquement sur des classes. Cette technique est appellée introspection et permet :
- de décrire une classe ou une interface : obtenir son nom, sa classe mère, la liste de ces méthodes, de ses variables de classe, de ses constructeurs et de ses variables d’instances
- d’agir sur une classe en envoyant, à un objet Class des message comme à tout autre objet. Par exemple, créer dynamiquement à partir d’un objet Class une nouvelle instance de la classe représentée
Voir le chapitre sur l’introspection.
5. La bibliothèque de classes java
Dans la version 1.0, le JDK contient 8 packages chacun étant constitués par un ensemble de classes qui couvrent un même domaine et apportent de nombreuses fonctionnalités. Les différentes versions du JDK sont constamment enrichis de packetages :
Packages |
JDK |
JDK |
JDK |
JDK |
Role |
|
1.0 |
1.1 |
1.2 |
1.3 |
|||
java.applet |
X |
X |
X |
X |
classes pour développer des |
|
applets |
||||||
java.awt |
X |
X |
X |
X |
classes définissant un toolkit |
|
pour interfaces graphiques |
||||||
java.awt.color |
X |
X |
classes qui permettent de |
|||
gérer et d’utiliser les couleurs |
||||||
classes qui permettent |
||||||
java.awt.datatransfer |
X |
X |
X |
d’échanger des données via le |
||
presse−papier |
||||||
java.awt.dnd |
X |
X |
classes qui permettent de |
|||
gérer cliquer/glisser |
||||||
classes qui permettent de |
||||||
java.awt.event |
X |
X |
X |
gérer les évenements |
||
utilisateurs |
||||||
java.awt.font |
X |
X |
classes qui permettent |
|||
d’utiliser les fontes |
||||||
classes qui permettent de |
||||||
java.awt.geom |
X |
X |
dessiner des formes |
|||
géométiques |
||||||
java.awt.im |
X |
|||||
java.awt.im.spi |
X |
|||||
java.awt.image |
X |
X |
X |
classes qui permettent |
||
d’afficher des images |
||||||
java.awt.image.renderable |
X |
X |
classes qui permettent de |
|||
modifier le rendu des images |
||||||
java.awt.print |
X |
X |
classes qui permettent de |
|||
réaliser des impressions |
||||||
java.beans |
X |
X |
X |
classes qui permettent de |
||
développer des composants |
||||||
réutilisables |
||||||
java.beans.beancontext |
X |
X |
||||
java.io |
X |
X |
X |
X |
classes pour gérer les flux |
|
java.lang |
X |
X |
X |
X |
classes de base du langage |
|
java.lang.ref |
X |
X |
||||
java.lang.reflect |
X |
X |
X |
classes pour la réflexion |
||
(introspection) |
||||||
java.math |
X |
X |
X |
classes pour des opérations |
||
mathématiques |
||||||
java.net |
X |
X |
X |
X |
classes pour utiliser les |
|
fonctionnalités réseaux |
||||||
classes pour le |
||||||
java.rmi |
X |
X |
X |
développement d’objets |
||
distribués |
||||||
java.rmi.activation |
X |
X |
||||
java.rmi.dgc |
X |
X |
X |
|||
java.rmi.registry |
X |
X |
X |
|||
java.rmi.server |
X |
X |
X |
classes pour gérer les objets |
||
serveurs de RMI |
||||||
java.security |
X |
X |
X |
classes pour gérer la |
||
signature et la certification |
||||||
java.security.acl |
X |
X |
X |
|||
java.security.cert |
X |
X |
X |
|||
java.security.interfaces |
X |
X |
X |
|||
java.security.spec |
X |
X |
||||
java.sql |
X |
X |
X |
classes JDBC pour l’accès |
||
aux bases de données |
||||||
java.text |
X |
X |
X |
classes pour formatter des |
||
objets en texte |
||||||
java.util |
X |
X |
X |
X |
classes définissant des |
|
utilitaires divers |
||||||
java.util.jar |
X |
X |
classes pour gérer les fichiers |
|||
jar |
||||||
java.util.zip |
X |
X |
X |
classes pour gérer les fichiers |
||
zip |
||||||
javax.accessibility |
X |
X |
||||
javax.naming |
X |
|||||
javax.naming.directory |
X |
|||||
X |
||||||
javax.naming.ldap |
X |
|||||
javax.naming.spi |
X |
|||||
javax.rmi |
X |
|||||
javax.rmi.CORBA |
X |
|||||
javax.sound.midi |
X |
|||||
javax.sound.midi.spi |
X |
|||||
javax.sound.sampled |
X |
|||||
javax.sound.sampled.spi |
X |
|||||
classes Swing pour |
||||||
javax.swing |
X |
X |
développer des interfaces |
|||
graphiques |
||||||
classes pour gérer les |
||||||
javax.swing.border |
X |
X |
bordures des composants |
|||
Swing |
||||||
javax.swing.colorchooser |
X |
X |
composant pour sélectionner |
|||
une couleur |
||||||
classes pour la gestion des |
||||||
javax.swing.event |
X |
X |
évenements utilisateur des |
|||
composants Swing |
||||||
javax.swing.filechooser |
X |
X |
composant pour sélectionner |
|||
un fichier |
||||||
classes pour la gestion de |
||||||
javax.swing.plaf |
X |
X |
l’aspect des composants |
|||
Swing |
||||||
javax.swing.plaf.basic |
X |
X |
||||
javax.swing.plaf.metal |
X |
X |
classes pour gérer l’aspect |
|||
metal des composants Swing |
||||||
javax.swing.plaf.multi |
X |
X |
||||
javax.swing.table |
X |
X |
||||
javax.swing.text |
X |
X |
||||
javax.swing.text.html |
X |
X |
||||
javax.swing.text.html.parser |
X |
X |
||||
javax.swing.text.rtf |
X |
X |
||||
javax.swing.tree |
X |
X |
classes pour gérer un |
|||
composant JTree |
||||||
classes pour gérer les |
||||||
javax.swing.undo |
X |
X |
annulations d’opérations |
|||
d’édition |
||||||
X |
|||||
org.omg.CORBA |
X |
X |
|||
org.omg.CORBA_2_3 |
X |
||||
org.omg.CORBA_2_3.portable |
X |
||||
org.omg.CORBA.DynAnyPackage |
X |
X |
|||
org.omg.CORBA.ORBPackage |
X |
X |
|||
org.omg.CORBA.portable |
X |
X |
|||
org.omg.CORBA.TypeCodePackage |
X |
X |
|||
org.omg.CosNaming |
X |
X |
|||
org.omg.CosNaming.NamingContextPackage |
X |
X |
|||
org.omg.SendingContext |
X |
||||
org.omg.stub.java.rmi |
X |
||||
Ce chapitre contient les sections suivantes :
- Présentation du package java.lang
- La classe Object
Présentation de la classe Object qui est la classe mère de toutes les classes en java.
-
- La classe String
Présentation de la classe String qui représente les chaines de caractères non modifiables
-
- La classe StringBuffer
Présentation de la classe StringBuffer qui représente les chaines de caractères modifiables
-
- Les wrappers
Présentation des wrappers qui sont des classes qui encapsulent des types primitifs et permettent de faire des conversions.
- Présentation rapide du package awt
- Présentation rapide du package java.io
- Le package java.util
- La classe StringTokenizer
Présentation de la classe StringTokenizer qui permet de découper une chaine selon des séparateurs.
-
- La classe Random
Présentation de la classe Random qui permet de générer des nombres aléatoires.
-
- Les classes Date et Calendar
Présentation des classes qui permettent selon de la version du JDK de gerer les dates.
-
- La classe Vector
Présentation de la classe Vector qui définit une sorte de tableau dynamique.
-
- La classe Hashtable
Présentation de la classe Hastable qui définit une sorte de dictionnaire.
-
- L’interface Enumeration
Présentation de cette interface qui définit des méthodes pour le parcours séquentiel de collections.
-
- Les expressions régulières
Le JDK 1.4 propose une api pour l’utilisation des expressions régulières avec java.
- Présentation rapide du package java.net
- Présentation rapide du package java.applet
5.1. Présentation du package java.lang
Ce package de base contient les classes fondamentales tel que Object, Class, Math, System, String, StringBuffer, Thread, les wrapper etc …
La classe Math est détaillée dans le chapitre « les fonctions mathématiques ».
La classe Class est détaillée dans le chapitre « la gestion dynamique des objets et l’introspection ».
La classe Thread est détaillée dans le chapitre « le multitache ».
Il contient également plusieurs classes qui permettent de demander des actions au système d’exploitation sur laquelle la machine virtuelle tourne, par exemple ClassLoader, Runtime, SecurityManager.
Ce package est implicitement importé dans tous les fichiers sources par le compilateur.
5.1.1. La classe Object
C’est la super classe de toutes les classes Java : toutes ces méthodes sont donc héritées par toutes les classes.
5.1.1.1. La méthode getClass()
La méthode getClass() renvoie un objet de la classe Class qui représente la classe de l’objet.
Le code suivant permet de connaitre le nom de la classe de l’objet
Exemple :
String nomClasse = monObject.getClass().getName();
5.1.1.2. La méthode toString()
La méthode toString() de la classe Object renvoie le nom de la classe , suivi du séparateur @, lui même suivi par la valeur de hachage de l’objet.
5.1.1.3. La méthode equals()
La méthode equals() implémente une comparaison par défaut. Sa définition dans Object compare les références : donc obj1.equals(obj2) ne renverra true que si obj1 et obj2 désignent le même objet. Dans une sous classe de Object, pour laquelle on a besoin de pouvoir dire que deux objets distincts peuvent être égaux, il faut redéfinir la méthode equals héritée de Object.
5.1.1.4. La méthode finalize()
A l’inverse de nombreux langage orienté objet tel que le C++ ou Delphi, le programmeur Java n’a pas à se préoccuper de la destruction des objets qu’il instancie. Ceux−ci sont détruits et leur emplacement mémoire est récupéré par le ramasse miette de la machine virtuelle dès qu’il n’y a plus de référence sur l’objet.
La machine virtuelle garantit que toutes les ressources Java sont correctement libérées mais, quand un objet encapsule une ressource indépendante de Java (comme un fichier par exemple), il peut être préférable de s’assurer que la ressource sera libérée quand l’objet sera détruit. Pour cela, la classe Object définit la méthode protected finalize, qui est appelée quand le ramasse miettes doit récupérer l’emplacement de l’objet ou quand la machine virtuelle termine son exécution
import java.io.*;
public class AccesFichier {
private FileWriter fichier;
public AccesFichier(String s) {
try {
fichier = new FileWriter(s);
}
catch (IOException e) {
System.out.println(« Impossible d’ouvrir le fichier
5.1.1.5. La méthode clone()
Si x désigne un objet obj1, l’exécution de x.clone() renvoie un second objet obj2, qui est une copie de obj1 : si obj1 est ensuite modifié, obj2 n’est pas affecté par ce changement.
Par défaut, la méthode clone(), héritée de Object fait une copie variable par variable : elle offre donc un comportement acceptable pour de très nombreuses sous classe de Object. Cependant comme le processus de duplication peut être délicat
- gérer pour certaines classes (par exemple des objets de la classe Container), l’héritage de clone ne suffit pas pour qu’une classe supporte le clonage.
Pour permettre le clonage d’une classe, il faut implémenter dans la classe l’interface Cloneable.
La première chose que fait la méthode clone() de la classe Object, quand elle est appelée, est de tester si la classe implémente Cloneable. Si ce n’est pas le cas, elle lève l’exception CloneNotSupportedException.
5.1.2. La classe String
Une chaine de caractères est contenue dans un objet de la classe String
On peut initialiser une variable String sans appeler explicitement un contructeur : le compilateur se charge de créer un objet.
Exemple : deux déclarations de chaines identiques.
String uneChaine = «bonjour»;
String uneChaine = new String(«bonjour»);
Les objets de cette classe ont la particularité d’être constants. Chaque traitement qui vise à transformer un objet de la classe est implémenté par une méthode qui laisse l’objet d’origine inchangé et renvoie un nouvel objet String contenant les modifications.
Exemple :
private String uneChaine;
void miseEnMajuscule(String chaine) {
uneChaine = chaine.toUpperCase()
}
Il est ainsi possible d’enchainer plusieurs méthodes :
Exemple :
uneChaine = chaine.toUpperCase().trim();
L’opérateur + permet la concatenation de chaines de caractères.
La comparaison de deux chaine doit se faire via la méthode equals() qui compare les objets eux même et non l’opérateur == qui compare les réferences de ces objets :
Exemple :
String nom1 = new String(« Bonjour »); String nom2 = new String(« Bonjour »); System.out.println(nom1 == nom2); // affiche false System.out.println( nom1.equals(nom2)); // affiche true
Cependant dans un soucis d’efficacité, le compilateur ne duplique pas 2 constantes chaines de caractères : il optimise l’espace mémoire utilisé en utilisant le même objet. Cependant, l’appel explicite du constructeur ordonne au compilateur de créer un nouvel objet.
Exemple :
String nom1 = «Bonjour»;
String nom2 = «Bonjour»;
String nom3 = new String(«Bonjour»);
System.out.println(nom1 == nom2); // affiche true
System.out.println(nom1 == nom3); // affiche false
La classe String possède de nombreuses méthodes dont voici les principales :
Méthodes la classe String |
Role |
charAt(int) |
renvoie le nieme caractère de la chaine |
compareTo(String) |
compare la chaine avec l’argument |
concat(String) |
ajoute l’argument à la chaine et renvoie la nouvelle chaine |
endsWith(String) |
vérifie si la chaine se termine par l’argument |
equalsIgnoreCase(String) |
compare la chaine sans tenir compte de la casse |
indexOf(String) |
renvoie la position de début à laquelle l’argument est contenu dans la chaine |
lastIndexOf(String) |
renvoie la dernière position à laquelle l’argument est contenu dans la chaine |
lenght() |
renvoie la longueur de la chaine |
replace(char,char) |
renvoie la chaine dont les occurences d’un caractère ont remplacées |
startsWidt(String int) |
Vérifie si la chaine commence par la sous chaine |
substring(int,int) |
renvoie une partie de la chaine |
toLowCase() |
renvoie la chaine en minuscule |
toUpperCase() |
renvoie la chaine en majuscule |
trim() |
enlève les caractères non significatifs de la chaine |
5.1.3. La classe StringBuffer
Les objets de cette classe contiennent des chaines de caractères variables, ce qui permet de les agrandir ou de les réduire.
Cette objet peut être utilisé pour construire ou modifier une chaine de caractères chaque fois que l’utilisation de la classe
String nécessiterait de nombreuses instanciations d’objets temporaires.
Par exemple, si str est un objet de type String, le compilateur utilisera la classe StringBuffer pour traiter la concaténation de « abcde »+str+« z » en générant le code suivant : new StringBuffer().append(« abcde ».append(str).append(« z »).toString();
Ce traitement aurait pu être réalisé avec trois appels à la méthode concat() de la classe String mais chacun des appels aurait instancié un objet StringBuffer pour réaliser la concaténation, ce qui est couteux en temps exécution
La classe StringBuffer dispose de nombreuses méthodes qui permettent de modifier le contenu de la chaine de caractère
Exemple ( code java 1.1 ) : une méthode uniquement pédagogique
public class MettreMaj {
static final String lMaj = « ABCDEFGHIJKLMNOPQRSTUVWXYZ »; static final String lMin = « abcdefghijklmnopqrstuvwxyz »;
public static void main(java.lang.String[] args) { System.out.println(MetMaj(« chaine avec MAJ et des min »));
}
public static String MetMaj(String s) {
StringBuffer sb = new StringBuffer(s);
for ( int i = 0; i <sb.length(); i++) {
int index = lMin.indexOf(sb.charAt(i));
if (index >=0 ) sb.setCharAt(i,lMaj.charAt(index));
}
return sb.toString();
}
}
Résultat :
CHAINE AVEC MAJ ET DES MIN
5.1.4. Les wrappers
Les objet de type wrappers (enveloppeurs) représentent des objets qui encapsulent une données de type primitif et qui fournissent un ensemble de méthodes qui permettent notamment de faire de conversions.
Ces classes offrent toutes les services suivants :
- un constructeur qui permet une instanciation à partir du type primitif et un constructeur qui permet une instanciation à partir d’un objet String
- une méthode pour fournir la valeur primitive représentée par l’objet
- une méthode equals() pour la comparaison.
Les méthodes de conversion opèrent sur des instances, mais il est possible d’utiliser des méthodes statiques.
Exemple :
int valeur =
Integer.valueOf(« 999 »).intValue();
Ces classes ne sont pas interchangeable avec les types primitif d’origine car il s’agit d’objet.
Exemple :
Float objetpi = new Float(«3.1415»);
System.out.println(5*objetpi); // erreur à la compil
Pour obtenir la valeur contenue dans l’objet, il faut utiliser la méthode typeValue() ou type est le nom du type standard
Exemple :
Integer Entier = new Integer(«10»);
int entier = Entier.intValue();
Les classes Integer, Long, Float et Double définissent toutes les constantes MAX_VALUE et MIN_VALUE qui représentent leurs valeurs minimales et maximales.
Lorsque l’on effectue certaines opérations mathématiques sur des nombres à virgules flottantes (float ou double), le résultat peut prendre l’une des valeurs suivantes :
- NEGATIVE_INFINITY : infini négatif causé par la division d’un nombre négatif par 0.0
- POSITIVE_INFINITY : infini positif causé par la division d’un nombre positif par 0.0
- NaN: n’est pas un nombre (Not a Number) causé par la division de 0.0 par 0.0
Il existe des méthodes pour tester le résultat :
Float.isNaN(float); //idem avec double
Double.isInfinite(double); // idem avec float
Exemple :
float res = 5.0f / 0.0f;
if (Float.isInfinite(res)) { … };
La constante Float.NaN n’est ni égale à un nombre dont la valeur est NaN ni à elle même. Float.NaN == Float.NaN retourne False
Lors de la division par zéro d’un nombre entier, une exception est levée.
Exemple :
System.out.println(10/0);
Exception in thread « main » java.lang.ArithmeticException: / by zero at test9.main(test9.java:6)
5.1.5. La classe System
Cette classe possède de nombreuses fonctionnalités pour utiliser des services du système d’exploitation.
5.1.5.1. L’utilisation des flux d’entrée/sortie standard
La classe System défini trois variables statiques qui permettent d’utiliser les flux d’entrée/sortie standards du système d’exploitation.
Variable |
Type |
Rôle |
in |
PrintStream |
Entrée standard du système. Par défaut, c’est le clavier. |
out |
InputStream |
Sortie standard du système. Par défaut, c’est le moniteur. |
err |
PrintStream |
Sortie standard des erreurs du système. Par défaut, c’est le moniteur. |
Exemple :
System.out.println(« bonjour »);
La classe système possède trois méthodes qui permettent de rediriger ces flux.
5.1.5.2. Les variables d’environnement et les propriétés du système
JDK 1.0 propose la méthode statique getEnv() qui renvoie la valeur de la propriété système dont le nom est fourni en paramètre.
Depuis le JDK 1.1, cette méthode est deprecated car elle n’est pas multi−système. Elle est remplacée par un autre mécanisme qui n’interroge pas directement le système mais qui recherche les valeurs dans un ensemble de propriétés. Cet ensemble est consituté de propriétés standard fournies par l’environnement java et par des propriétés ajoutés par l’utilisateur.
Voici une liste non exhaustive des propriétés fournies par l’environnement java :
Nom de la propriété |
Rôle |
java.version |
Version du JRE |
java.vendor |
Auteur du JRE |
java.vendor.url |
URL de l’auteur |
java.home |
Répertoire d’installation de java |
java.vm.version |
Version de l’implémentation la JVM |
java.vm.vendor |
Auteur de l’implémentation de la JVM |
java.vm.name |
Nom de l’implémentation de la JVM |
java.specification.version |
Version des spécifications de la JVM |
java.specification.vendor |
Auteur des spécifications de la JVM |
java.specification.name |
Nom des spécifications de la JVM |
java.ext.dirs |
Chemin du ou des répertoires d’extension |
os.name |
Nom du système d’exploitation |
os.arch |
Architecture du système d’exploitation |
Version du système d’exploitation |
|
file.separator |
Séparateur de fichiers (exemple : « / » sous Unix, « \ » sous Windows) |
path.separator |
Séparateur de chemin (exemple : « : » sous Unix, « ; » sous Windows) |
line.separator |
Séparateur de ligne |
user.name |
Nom du user courant |
user.home |
Répertoire d’accueil du user courant |
user.dir |
Répertoire courant au moment de l’initialisation de la propriété |
Pour définir ces propres propriétés, il faut utiliser l’option −D de l’interpreteur java en utilisant la ligne de commande.
La méthode statique getProperty() permet d’obtenir la valeur de la propriété dont le nom est fourni en paramètre. Une version surchargée de cette méthode permet de préciser un second paramètre qui contiendra la valeur par défaut, si la propriété n’est pas définie.
Exemple : |
|
public class TestProperty { |
|
public static void main(String[] args) { |
|
System.out.println(« java.version |
= »+System.getProperty(« java.version »)); |
System.out.println(« java.vendor |
= »+System.getProperty(« java.vendor »)); |
System.out.println(« java.vendor.url |
= »+System.getProperty(« java.vendor.url »)); |
System.out.println(« java.home |
= »+System.getProperty(« java.home »)); |
System.out.println(« java.vm.specification.version |
= » |
+System.getProperty(« java.vm.specification.version »)); |
|
System.out.println(« java.vm.specification.vendor |
= » |
+System.getProperty(« java.vm.specification.vendor »)); |
|
System.out.println(« java.vm.specification.name |
= » |
+System.getProperty(« java.vm.specification.name »)); |
|
System.out.println(« java.vm.version |
= »+System.getProperty(« java.vm.version »)); |
System.out.println(« java.vm.vendor |
= »+System.getProperty(« java.vm.vendor »)); |
System.out.println(« java.vm.name |
= »+System.getProperty(« java.vm.name »)); |
System.out.println(« java.specification.version |
= » |
+System.getProperty(« java.specification.version »)); |
|
System.out.println(« java.specification.vendor |
= » |
+System.getProperty(« java.specification.vendor »)); |
|
System.out.println(« java.specification.name |
= » |
+System.getProperty(« java.specification.name »)); |
|
System.out.println(« java.class.version |
= » |
+System.getProperty(« java.class.version »)); |
|
System.out.println(« java.class.path |
= » |
+System.getProperty(« java.class.path »)); |
|
System.out.println(« java.ext.dirs |
= »+System.getProperty(« java.ext.dirs »)); |
System.out.println(« os.name |
= »+System.getProperty(« os.name »)); |
System.out.println(« os.arch |
= »+System.getProperty(« os.arch »)); |
System.out.println(« os.version |
= »+System.getProperty(« os.version »)); |
System.out.println(« file.separator |
= »+System.getProperty(« file.separator »)); |
System.out.println(« path.separator |
= »+System.getProperty(« path.separator »)); |
System.out.println(« line.separator |
= »+System.getProperty(« line.separator »)); |
System.out.println(« user.name |
= »+System.getProperty(« user.name »)); |
System.out.println(« user.home |
= »+System.getProperty(« user.home »)); |
System.out.println(« user.dir |
= »+System.getProperty(« user.dir »)); |
} |
|
} |
|
Par défaut, l’accès aux propriétés système est restreint par le SecurityManager pour les applets.
5.1.6. La classe Runtime
La classe Runtime permet d’intéragir avec le système dans lequel l’application s’éxécute : obtenir des informations sur le système, arrêt de la machine virtuelle, éxecution d’un programme externe
Cette classe ne peut pas être instanciée mais il est possible d’obtenir une instance en appelant la méthode statique getRuntime().
La méthode exec() permet d’éxécuter des commandes du système d’exploitation ou s’éxécute la JVM. Elle lance la commande de manière asynchrone et renvoie un objet de type Process pour obtenir des informations sur le processus lancé.
L’inconvénient d’utiliser cette méthode est que la commande executée est dépendante du système d’exploitation.
Cette section est en cours d’écriture
5.2. Présentation rapide du package awt java
AWT est une collection de classes pour la réalisation d’applications graphique ou GUI (Graphic User Interface)
Les composants qui sont utilisés par les classes définies dans ce package sont des composants dit « lourds » : ils dépendent entièrement du système d’explotation. D’ailleurs leur nombre est limité car ils sont communs à plusieurs systèmes d’exploitation pour assurer la portabilité. Cependant, la représentation d’une interface graphique avec awt sur plusieurs systèmes peut ne pas être identique.
La chapitre « Création d’interface graphique avec AWT » détaille l’utilisation de ce package,
5.2.1. Le package java.image
Ce package permet la gestion des images
5.2.2. Le package java.awt.perr
Ce package contient des classes qui réalise l’interface avec le système d’exploitation.
5.3. Présentation rapide du package java.io
Ce package définit un ensemble de classes pour la gestion des flux d’entrées−sorties
Le chapitre les flux détaille l’utilisation de ce package
5.4. Le package java.util
Ce package contient un ensemble de classes utilitaires : la gestion des dates (Date et Calendar), la génération de nombres aléatoires (Random), la gestion des collections ordonnées ou non tel que la tablea de hachage (HashTable), le vecteur (Vector), la pile (Stack) …, la gestion des propriétés (Properties), des classes dédiées à l’internationalisation (ResourceBundle, PropertyResourceBundle, ListResourceBundle) etc …
Certaines de ces classes sont présentées plus en détail dans les sections suivantes.
5.4.1. La classe StringTokenizer
Cette classe permet de découper une chaine de caractères (objet de type String) en fonction de séparateurs. Le constructeur de la classe accepte 2 paramètres : la chaine à décomposer et une chaine contenant les séparateurs
Exemple ( code java 1.1 ) :
import java.util.*;
class test9 {
public static void main(String args[]) {
StringTokenizer st = new StringTokenizer(« chaine1,chaine2,chaine3,chaine4″, », »); while (st.hasMoreTokens()) {
System.out.println((st.nextToken()).toString());
}
}
}
C:\java>java test9
chaine1
chaine2
chaine3
chaine4
La méthode hasMoreTokens() fournit un contrôle d’itération sur la collection en renvoyant un booleen indiquant si il reste encore des éléments.
La méthode getNextTokens() renvoie le prochain élément sous la forme d’un objet String
5.4.2. La classe Random
La classe Random permet de générer des nombres pseudo−aléatoires. Après l’appel au constructeur, il suffit d’appeler la méthode correspondant au type désiré : nextInt(), nextLong(), nextFloat() ou nextDouble()
Méthodes |
valeur de retour |
nextInt |
entre Integer.MIN_VALUE et Interger.MAX_VALUE |
nextLong |
entre long.MIN_VALUE et long.MAX_VALUE |
nextFloat ou nextDouble |
entre 0.0 et 1.0 |
Exemple ( code java 1.1 ) :
import java.util.*;
class test9 {
public static void main (String args[]) {
Random r = new Random();
int a = r.nextInt() %10; //entier entre −9 et 9
System.out.println(« a = « +a);
}
}
5.4.3. Les classes Date et Calendar
En java 1.0, la classe Date permet de manipuler les dates.
Exemple ( code java 1.0 ) :
import java.util.*;
…
Date maintenant = new Date();
if (maintenant.getDay() == 1)
System.out.println( » lundi « );
Le constructeur d’un objet Date l’initialise avec la date et l’heure courante du système.
Exemple ( code java 1.0 ) : recherche et affichage de l’heure
import java.util.*;
import java.text.*;
public class TestHeure {
public static void main(java.lang.String[] args) {
Date date = new Date();
System.out.println(DateFormat.getTimeInstance().format(date));
}
}
Résultat :
22:05:21
La méthode getTime() permet de calculer le nombre de millisecondes écoulées entre la date qui est encapsulée dans l’objet qui reçoit le message getTime et le premier janvier 1970 à minuit GMT.
En java 1.1, de nombreuses méthodes et constructeurs de la classe Date sont deprecated, notamment celles qui permettent de manipuler les éléments qui composent la date et leur formattage : il faut utiliser la classe Calendar.
Exemple ( code java 1.1 ) :
import java.util.*;
public class TestCalendar {
public static void main(java.lang.String[] args) {
Calendar c = Calendar.getInstance();
if (c.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY)
System.out.println( » nous sommes lundi « );
if (c.get(Calendar.DAY_OF_WEEK) == Calendar.TUESDAY)
System.out.println( » nous sommes mardi « );
if (c.get(Calendar.DAY_OF_WEEK) == Calendar.WEDNESDAY)
System.out.println( » nous sommes mercredi « );
if (c.get(Calendar.DAY_OF_WEEK) == Calendar.THURSDAY)
System.out.println( » nous sommes jeudi « );
if (c.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY)
System.out.println( » nous sommes vendrei « );
if (c.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY)
System.out.println( » nous sommes samedi « );
if (c.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
System.out.println( » nous sommes dimanche « );
}
Résultat :
nous sommes lundi
5.4.4. La classe Vector
Un objet de la classe Vector peut être considéré comme une tableau évolué qui peut contenir un nombre indéterminé d’objets.
Les méthodes principales sont les suivantes :
Méthode |
Rôle |
|
void addElement(Object) |
ajouter un objet dans le vecteur |
|
boolean contains(Object) |
retourne true si l’objet est dans le vecteur |
|
Object elementAt(int) |
retourne l’objet à l’index indiqué |
|
Enumeration elements() |
retourne une enumeration contenant tous les éléments du vecteur |
|
Object firstElement() |
retourne le premier élément du vecteur (celui dont l’index est égal à |
|
zéro) |
||
int indexOf(Object) |
renvoie le rang de l’élément ou −1 |
|
void insertElementAt(Object, int) |
insérer un objet à l’index indiqué |
|
boolean isEmpty() |
retourne un booléen si le vecteur est vide |
|
Objet lastElement() |
retourne le dernier élément du vecteur |
|
void removeAllElements() |
vider le vecteur |
|
void removeElement(Object) |
supprime l’objet du vecteur |
|
void removeElementAt(int) |
supprime l’objet à l’index indiqué |
|
void setElementAt(object, int) |
remplacer l’élément à l’index par l’objet |
|
int size() |
nombre d’objet du vecteur |
|
On peut stocker des objets de classes différentes dans un vecteur mais les éléments stockés doivent obligatoirement être des objets (pour le type primitif il faut utiliser les wrappers tel que Integer ou Float mais pas int ou float).
Exemple ( code java 1.1 ) :
Vector v = new Vector();
v.addElement(new Integer(10));
v.addElement(new Float(3.1416));
v.insertElementAt(« chaine « ,1);
System.out.println( » le vecteur contient « +v.size()+ » elements « );
String retrouve = (String) v.elementAt(1);
System.out.println( » le 1er element = « +retrouve);
C:\$user\java>java test9
le vecteur contient 3 elements
le 1er element = chaine
Exemple ( code java 1.1 ) : le parcours d’un vecteur
Vector v = new Vector();
…
for (int i = 0; i < v.size() ; i ++) {
System.out.println(v.elementAt(i));
}
Pour un parcours en utilisant l’interface Enumeration, voir le chapitre correspondant.
5.4.5. La classe Hashtable
Les informations d’une Hastable sont stockées sous la forme clé − données. Cet objet peut être considéré comme un dictionnaire.
Exemple ( code java 1.1 ) :
Hashtable dico = new Hashtable();
dico.put(«livre1», « titre du livre 1 »);
dico.put(«livre2», «titre du livre 2 »);
Il est possible d’utiliser n’importe quel objet comme clé et comme donnée
Exemple ( code java 1.1 ) :
dico.put(«jour», new Date());
dico.put(new Integer(1),«premier»);
dico.put(new Integer(2),«deuxième»);
Pour lire dans la table, on utilise get(object) en donnant la clé en paramètre.
Exemple ( code java 1.1 ) :
System.out.println(« nous sommes le » +dico.get(«jour»));
La méthode remove(Object) permet de supprimer une entrée du dictionnaire correspondant à la clé passée en paramètre.
La méthode size() permet de connaitre le nombre d’association du dictionnaire.
5.4.6. L’interface Enumeration
L’interface Enumeration est utilisée pour permettre le parcours séquentiel de collections.
Enumeration est une interface qui définit 2 méthodes :
Méthodes |
Rôle |
retourne true si l’énumeration contient encore un ou plusieurs elements |
retourne l’objet suivant de l’énumération
Object nextElement() Elle leve une Exception NoSuchElementException si la fin de la collection est atteinte.
Exemple ( code java 1.1 ) : contenu d’un vecteur et liste des clés d’une Hastable
import java.util.*;
class test9 {
public static void main (String args[]) {
Hashtable h = new Hashtable();
Vector v = new Vector();
v.add(« chaine 1 »);
v.add(« chaine 2 »);
v.add(« chaine 3 »);
h.put(« jour », new Date());
h.put(new Integer(1), »premier »);
h.put(new Integer(2), »deuxième »);
System.out.println(« Contenu du vector »);
for (Enumeration e = v.elements() ; e.hasMoreElements() ; ) { System.out.println(e.nextElement());
}
System.out.println(« \nContenu de la hashtable »);
for (Enumeration e = h.keys() ; e.hasMoreElements() ; ) { System.out.println(e.nextElement());
}
}
}
C:\$user\java>java test9
Contenu du vector
chaine 1
chaine 2
chaine 3
Contenu de la hashtable
jour
2
1
5.4.7. Les expressions régulières
Le JDK 1.4 propose une api en standard pour utiliser les expressions régulières. Les expressions régulières permettent de comparer une chaine de caractères àun motif pour vérifier qu’il y a concordance.
La package java.util.regexp contient deux classes et une exception pour gérer les expressions régulières :
Classe |
Rôle |
Matcher |
comparer une chaine de caractère avec un motif |
Pattern |
encapsule une version compilée d’un motif |
PatternSyntaxException |
exception levée lorsque le motif contient une erreur de syntaxe |
5.4.7.1. Les motifs
Les expressions régulières utilisent un motif. Ce motif est une chaine de caractères qui contient des caractères et des métacaractères. Les métacaractères ont une signification particulière et sont interprétés.
Il est possible de déspécialiser un métacaractère (lui enlever sa signification particulière à en le faisant précédé d’un caractère backslash. Ainsi pour utiliser le caractère backslash, il faut le doubler.
Les métacaractères reconnus par l’api sont :
métacaractères |
rôle |
() |
|
[] |
définir un ensemble de caractères |
{} |
définir une répétition du motif précédent |
\ |
déspécalisation du caractère qui suit |
^ |
debut de la ligne |
$ |
fin de la ligne |
| |
le motif precedent ou le motif suivant |
? |
motif precédent répété zero ou une fois |
* |
motif précédent repété zéro ou plusieurs fois |
+ |
motif précédent répété une ou plusieurs fois |
. |
un caractère quelconque |
Certains caractères spéciaux ont une notation particulière :
Notation |
Rôle |
\t |
tabulation |
\n |
nouvelle ligne (ligne feed) |
\\ |
backslash |
Il est possible de définir des ensembles de caractères à l’aide des caractères [ et ]. Il suffit d’indiquer les caractères de l’ensemble entre ces deux caractères
Exemple : toutes les voyelles
[aeiouy]
Il est possible d’utiliser une plage de caractères consécutifs en séparant le caractère de début de la plage et le caractère de fin de la plage avec un caractère –
Exemple : toutes les lettres minuscules
L’ensemble peut être l’union de plusieurs plages.
Exemple : toutes les lettres
[a−zA−Z]
Par défaut l’ensemble [] désigne tous les caractères. Il est possible de définir un ensemble de la forme tous sauf ceux précisés en utilisant le caractère ^ suivi des caractères à enlever de l’ensemble
Exemple : tous les caractères sauf les lettres
[^a−zA−Z]
Il existe plusieurs ensembles de caractères prédéfinis :
Notation |
Contenu de l’ensemble |
|
\d |
un chiffre |
|
\D |
tous sauf un chiffre |
|
\w |
une lettre ou un underscore |
|
\W |
tous sauf une lettre ou un underscore |
|
\s |
un séparateur (espace, tabulation, retour chariot, …) |
|
\S |
tous sauf un séparateur |
|
Plusieurs métacaractères permettent de préciser un critère de répétition d’un motif |
||
métacaractères |
rôle |
|
{n} |
répétition du motif précédent n fois |
|
{n,m} |
répétition du motif précédent entre n et m fois |
|
{n,} |
répétition du motif précédent |
|
? |
motif precédent répété zero ou une fois |
|
* |
motif précédent repété zéro ou plusieurs fois |
|
+ |
motif précédent répété une ou plusieurs fois |
|
Exemple : la chaine AAAAA
A{5}
5.4.7.2. La classe Pattern
Cette classe encapsule une representation compilée d’un motif d’une expression régulière.
La classe Pattern ne possède pas de constructeur public mais propose une méthode statique compile().
Exemple :
private static Pattern motif = null;
…
motif = Pattern.compile(« liste[0−9] »);
Une version surchargée de la méthode compile() permet de préciser certaines options dont la plus interressante permet de rendre insensible à la casse les traitements en utilisant le flag CASE_INSENSITIVE.
Exemple :
private static Pattern motif = null;
…
motif = Pattern.compile(« liste[0−9] »,Pattern.CASE_INSENSITIVE);
Cette méthode compile() renvoie une instance de la classe Pattern si le motif est syntaxiquement correcte sinon elle lève une exception de type PatternSyntaxException.
La méthode matches(String, String) permet de rapidement et facilement utiliser les expressions régulières avec un seul appel de méthode en fournissant le motif est la chaine à traiter.
Exemple :
if (Pattern.matches(« liste[0−9] », »liste2″)) {
System.out.println(« liste2 ok »);
} else {
System.out.println(« liste2 ko »);
}
5.4.7.3. La classe Matcher
La classe Matcher est utilisée pour effectuer la comparaison entre une chaine de caractères et un motif encapsulé dans un objet de type Pattern.
Cette classe ne possède aucun constructeur public. Pour obtenir une instance de cette classe, il faut utiliser la méthode matcher() d’une instance d’un objet Pattern en lui fournissant la chaine à traiter en paramètre.
Exemple :
motif = Pattern.compile(« liste[0−9] »);
matcher = motif.matcher(« liste1 »);
La méthodes matches() tente de comparer toute la chaine avec le motif et renvoie le résultat de cette comparaison.
Exemple :
motif = Pattern.compile(« liste[0−9] »);
matcher = motif.matcher(« liste1 »);
if (matcher.matches()) {
System.out.println(« liste1 ok »);
} else {
System.out.println(« liste1 ko »);
}
matcher = motif.matcher(« liste10 »);
if (matcher.matches()) {
System.out.println(« liste10 ok »);
} else {
System.out.println(« liste10 ko »);
}
Résultat :
liste1 ok
liste10 ko
La méthode lookingAt() tente de recherche le motif dans la chaine à traiter
Exemple :
motif = Pattern.compile(« liste[0−9] »);
matcher = motif.matcher(« liste1 »);
if (matcher.lookingAt()) {
System.out.println(« liste1 ok »);
} else {
System.out.println(« liste1 ko »);
}
matcher = motif.matcher(« liste10 »);
if (matcher.lookingAt()) {
System.out.println(« liste10 ok »);
} else {
System.out.println(« liste10 ko »);
}
Résultat :
liste1 ok
liste10 ok
La méthode find() permet d’obtenir des informations sur chaque occurrence ou le motif est trouvé dans la chaine à traiter.
Exemple :
matcher = motif.matcher(« zzliste1zz »);
if (matcher.find()) {
System.out.println(« zzliste1zz ok »);
} else {
System.out.println(« zzliste1zz ko »);
}
Résultat :
zzliste1zz ok
Il est possible d’appeler successivement cette méthode pour obtenir chacune des occurrences.
Exemple :
motif = Pattern.compile(« liste[0−9] »);
matcher = motif.matcher(« liste1liste2liste3 »);
while (matcher.find()) {
i++;
}
System.out.println(« nb occurences = » + i);
Les méthodes start() et end() permettent de connaitre la position de début et de fin dans la chaine dans l’occurrence en cours de traitement.
Exemple :
int i = 0;
motif = Pattern.compile(« liste[0−9] »);
matcher = motif.matcher(« liste1liste2liste3 »);
while (matcher.find()) {
i++;
}
System.out.println(« nb occurences = » + i);
Résultat :
pos debut : 0 pos fin : 6
pos debut : 6 pos fin : 12
pos debut : 12 pos fin : 18
nb occurences = 3
La classe Matcher propose aussi les méthodes replaceFirst() et replaceAll() pour facilement remplacer la première ou toutes les occurrences du motif trouvées par une chaine de caractères.
Exemple : remplacement de la première occurrence
motif = Pattern.compile(« liste[0−9] »);
matcher = motif.matcher(« zz liste1 zz liste2 zz »); System.out.println(matcher.replaceFirst(« chaine »));
Résultat :
zz chaine zz liste2 zz
Exemple : remplacement de toutes les occurrences
motif = Pattern.compile(« liste[0−9] »);
matcher = motif.matcher(« zz liste1 zz liste2 zz »); System.out.println(matcher.replaceAll(« chaine »));
Résultat :
zz chaine zz chaine zz
5.5. Présentation rapide du package java.net
Ce package contient un ensemble de classes pour permettre une interaction avec le réseau
Le chapitre l’interaction avec le reseau détaille l’utilisation de ce package
5.6. Présentation rapide du package java.applet
Ce package contient des classes à importer obligatoirement pour développer des applets
Le développement des applets est détaillé dans le chapitre les applets
6. Les fonctions mathématiques
La classe java.lang.Math contient une série de méthodes et variables mathématiques. Comme la classe Math fait partie du package java.lang, elle est automatiquement importée. Il n’est pas nécessaire de déclarer un objet de type Math car les méthodes sont toutes static
Exemple ( code java 1.1 ) : Calculer et afficher la racine carrée de 3
public class Math1 {
public static void main(java.lang.String[] args) { System.out.println( » = » + Math.sqrt(3.0));
}
}
6.1. Les variables de classe
PI représente pi dans le type double ( 3,14159265358979323846 )
E représente e dans le type double ( 2,7182818284590452354 )
Exemple ( code java 1.1 ) :
public class Math2 {
public static void main(java.lang.String[] args) { System.out.println( » PI = « +Math.PI);
System.out.println( » E = « +Math.E);
}
}
6.2. Les fonctions trigonométriques
Les méthodes sin(), cos(), tan(), asin(), acos(), atan() sont déclarées : public static double fonctiontrigo(double a)
Les angles doivent être exprimés en radians. Pour convertir des degrés en radian, il suffit du multiplier par PI/180
6.3. Les fonctions de comparaisons
max (n1, n2)
min (n1, n2)
Ces méthodes existent pour les types int, long, float et double : elles déterminent respectivement les valeurs maximales et minimales des deux paramètres.
public class Math1 {
public static void main(String[] args) {
System.out.println( » le plus grand = » + Math.max(5, 10));
System.out.println( » le plus petit = » + Math.min(7, 14));
}
}
Résultat :
le plus grand = 10
le plus petit = 7
6.4. Les arrondis
6.4.1. La méthode round(n)
Cette méthode ajoute 0,5 à l’argument et restitue la plus grande valeur entière (int) inférieure ou égale au résultat. La méthode est définie pour les types float et double.
Exemple ( code java 1.1 ) :
public class Math1 {
public static void main(String[] args) { System.out.println( » round(5.0) = « +Math.round(5.0)); System.out.println( » round(5.2) = « +Math.round(5.2)); System.out.println( » round(5.5) = « +Math.round(5.5)); System.out.println( » round(5.7) = « +Math.round(5.7)); }
}
Résultat :
round(5.0) = 5
round(5.2) = 5
round(5.5) = 6
round(5.7) = 6
6.4.2. La méthode rint(double)
Cette méthode effectue la même opération mais renvoie un type double
Exemple ( code java 1.1 ) :
public class Math1 {
public static void main(String[] args) { System.out.println( » rint(5.0) = « +Math.rint(5.0)); System.out.println( » rint(5.2) = « +Math.rint(5.2)); System.out.println( » rint(5.5) = « +Math.rint(5.5)); System.out.println( » rint(5.7) = « +Math.rint(5.7)); }
}
rint(5.0) = 5.0
rint(5.2) = 5.0
rint(5.5) = 6.0
rint(5.7) = 6.0
6.4.3. La méthode floor(double)
Cette méthode renvoie l’entier le plus proche inférieur ou égal à l’argument
Exemple ( code java 1.1 ) :
public class Math1 {
public static void main(String[] args) { System.out.println( » floor(5.0) = « +Math.floor(5.0)); System.out.println( » floor(5.2) = « +Math.floor(5.2)); System.out.println( » floor(5.5) = « +Math.floor(5.5)); System.out.println( » floor(5.7) = « +Math.floor(5.7)); }
}
résultat :
floor(5.0) = 5.0
floor(5.2) = 5.0
floor(5.5) = 5.0
floor(5.7) = 5.0
6.4.4. La méthode ceil(double)
Cette méthode renvoie l’entier le plus proche supérieur ou égal à l’argument
Exemple ( code java 1.1 ) :
public class Math1 {
public static void main(String[] args) { System.out.println( » ceil(5.0) = « +Math.ceil(5.0)); System.out.println( » ceil(5.2) = « +Math.ceil(5.2)); System.out.println( » ceil(5.5) = « +Math.ceil(5.5)); System.out.println( » ceil(5.7) = « +Math.ceil(5.7)); }
}
résultat :
ceil(5.0) = 5.0
ceil(5.2) = 6.0
ceil(5.5) = 6.0
ceil(5.7) = 6.0
6.4.5. La méthode abs(x)
Cette méthode donne la valeur absolue de x (les nombre négatifs sont convertis en leur opposé). La méthode est définie pour les types int, long, float et double.
Exemple ( code java 1.1 ) :
public class Math1 {
public static void main(String[] args) {
System.out.println( » abs(−5.7) = « +abs(−5.7));
}
}
Résultat :
abs(−5.7) = 5.7
6.4.6. La méthode IEEEremainder(double, double)
Cette méthode renvoie le reste de la division du premier argument par le deuxieme
Exemple ( code java 1.1 ) :
public class Math1 {
public static void main(String[] args) { System.out.println( » reste de la division de 3 par 10 = «
+Math.IEEEremainder(10.0, 3.0) );
}
}
Résultat :
reste de la division de 3 par 10 = 1.0
6.5. Les Exponentielles et puissances
6.5.1. La méthode pow(double, double)
Cette méthode elève le premier argument à la puissance indiquée par le second.
Exemple ( code java 1.1 ) :
public static void main(java.lang.String[] args) { System.out.println( » 5 au cube = « +Math.pow(5.0, 3.0) );
}
Résultat :
5 au cube = 125.0
6.5.2. La méthode sqrt(double)
Cette méthode calcule la racine carrée de son paramètre.
Exemple ( code java 1.1 ) :
public static void main(java.lang.String[] args) { System.out.println( » racine carree de 25 = « +Math.sqrt(25.0) );
}
Résultat :
racine carree de 25 = 5.0
6.5.3. La méthode exp(double)
Cette méthode calcule l’exponentielle de l’argument
Exemple ( code java 1.1 ) :
public static void main(java.lang.String[] args) { System.out.println( » exponentiel de 5 = « +Math.exp(5.0) );
}
Résultat :
exponentiel de 5 = 148.4131591025766
6.5.4. La méthode log(double)
Cette méthode calcule le logarithme naturel de l’argument
Exemple ( code java 1.1 ) :
public static void main(java.lang.String[] args) { System.out.println( » logarithme de 5 = « +Math.log(5.0) );
}
Résultat :
logarithme de 5 = 1.6094379124341003
6.6. La génération de nombres aléatoires
6.6.1. La méthode random()
Cette méthode renvoie un nombre aléatoire compris entre 0.0 et 1.0.
Exemple ( code java 1.1 ) :
public static void main(java.lang.String[] args) { System.out.println( » un nombre aléatoire = « +Math.random() );
}
Résultat :
un nombre aléatoire = 0.8178819778125899
7. La gestion des exceptions
Les exceptions représentent le mécanisme de gestion des erreurs intégré au langage java. Il se compose d’objets représentant les erreurs et d’un ensemble de trois mots clés qui permettent de détecter et de traiter ces erreurs ( try, catch et finally ) et de les lever ou les propager (throw et throws).
Lors de la détection d’une erreur, un objet qui hérite de la classe Exception est créé (on dit qu’une exception est levée) et propagé à travers la pile d’execution jusqu’à ce qu’il soit traitée.
Ces mécanismes permettent de renforcer la sécurité du code java.
Exemple ( code java 1.1 ) : une exception levée à l’exécution non capturée
public class TestException {
public static void main(java.lang.String[] args) { int i = 3;
int j = 0;
System.out.println(« résultat = » + (i / j));
}
}
Résultat :
C:>java TestException
Exception in thread « main » java.lang.ArithmeticException: / by zero
at tests.TestException.main(TestException.java:23)
Si dans un bloc de code on fait appel à une méthode qui peut potentiellement générer une exception, on doit soit essayer de la récupérer avec try/catch, soit ajouter le mot clé throws dans la déclaration du bloc. Si on ne le fait pas, il y a une erreur à la compilation. Les erreurs et exceptions du paquetage java.lang échappe à cette contrainte. Throws permet de déléguer la responsabilité des erreurs vers la méthode appelante
Ce procédé présente un inconvénient : de nombreuses méthodes des packages java indiquent dans leur déclaration qu’elles peuvent lever une exception. Cependant ceci garantie que certaines exceptions critiques seront prises explicitement en compte par le programmeur.
7.1. Les mots clés try, catch et finally
Le bloc try rassemble les appels de méthodes susceptibles de produire des erreurs ou des exceptions. L’instruction try est suivie d’instructions entre des accolades.
Exemple ( code java 1.1 ) :
operation_risquée1;
opération_risquée2;
} catch (ExceptionInteressante e) { traitements
} catch (ExceptionParticulière e) { traitements
} catch (Exception e) { traitements
} finally {
traitement_pour_terminer_proprement;
}
Si un événement indésirable survient dans le bloc try, la partie éventuellement non exécutée de ce bloc est abandonnée et le premier bloc catch est traité. Si catch est défini pour capturer l’exception issue du bloc try alors elle est traitée en exécutant le code associé au bloc. Si le bloc catch est vide (aucunes instructions entre les accolades) alors l’exception capturée est ignorée.
Si il y a plusieurs type d’erreurs et d’exceptions à intercepter, il faut définir autant de bloc catch que de type d’événement . Par type d’exception, il faut comprendre « qui est du type de la classe de l’exception ou d’une de ces sous classes ». Ainsi dans l’ordre séquentiel des clauses catch, un type d’exception de ne doit pas venir après un type d’une exception d’une super classe. Il faut faire attention à l’ordre des clauses catch pour traiter en premier les exceptions les plus précises (sous classes) avant les exceptions plus générales. Un message d’erreur est émis par le compilateur dans le cas contraire.
Exemple ( code java 1.1 ) : erreur à la compil car Exception est traité en premier alors que ArithmeticException est une sous classe de Exception
public class TestException {
public static void main(java.lang.String[] args) {
- Insert code to start the application here. int i = 3;
int j = 0; try {
System.out.println(« résultat = » + (i / j));
}
catch (Exception e) {
}
catch (ArithmeticException e) {
}
}
}
Résultat :
C:\tests>javac TestException.java
TestException.java:11: catch not reached.
catch (ArithmeticException e) {
^
1 error
Si l’exception générée est une instance de la classe déclarée dans la clause catch ou d’une classe dérivée, alors on exécute le bloc associé. Si l’exception n’est pas traitées par un bloc catch, elle sera transmise au bloc de niveau supérieur. Si l’on ne se trouve pas dans un autre bloc try, on quitte la méthode en cours, qui regénère à son tour une exception dans la méthode appelante.
L’exécution totale du bloc try et d’un bloc d’une clause catch sont mutuellement exclusives : si une exception est levée, l’exécution du bloc try est arrêtée et si elle existe, la clause catch adéquate est exécutée.
La clause finally définit un bloc qui sera toujours exécuté, qu’une exception soit levée ou non. Ce bloc est facultatif. Il est aussi exécuté si dans le bloc try il y a une instruction break ou continue.
7.2. La classe Throwable
Cette classe descend directement de Object : c’est la classe de base pour le traitements des erreurs.
Cette classe possède deux constructeurs :
Méthode |
Rôle |
Throwable()
La chaine en paramètre permet de définir un message qui décrit
Throwable(String)
l’exception et qui pourra être consultée dans un bloc catch.
Les principales méthodes de la classe Throwable sont :
Méthodes |
Rôle |
Deprecated |
|
String getMessage( ) |
lecture du message |
||
void printStackTrace( ) |
affiche l’exception et l’état de la pile |
||
d’execution au moment de son appel |
|||
void printStackTrace(PrintStream s) |
Idem mais envoie le résultat dans un flux |
||
Exemple ( code java 1.1 ) :
public class TestException {
public static void main(java.lang.String[] args) {
- Insert code to start the application here. int i = 3;
int j = 0; try {
System.out.println(« résultat = » + (i / j));
}
catch (ArithmeticException e) { System.out.println(« getmessage »); System.out.println(e.getMessage()); System.out.println( » « ); System.out.println(« toString »); System.out.println(e.toString()); System.out.println( » « ); System.out.println(« printStackTrace »); e.printStackTrace();
}
}
}
Résultat :
C:>java TestException
getmessage
/ by zero
toString
java.lang.ArithmeticException: / by zero
printStackTrace
java.lang.ArithmeticException: / by zero
at tests.TestException.main(TestException.java:24)
7.3. Les classes Exception, RunTimeException et Error
Ces trois classes descendent de Throwable : en fait, toutes les exceptions dérivent de la classe Throwable.
La classe Error représente une erreur grave intervenue dans la machine virtuelle Java ou dans un sous système Java.
L’application Java s’arrête instantanement dès l’apparition d’une exception de la classe Error.
La classe Exception représente des erreurs moins graves. La classe RuntimeException n’ont pas besoin d’être détectées impérativement par des blocs try/catch
7.4. Les exceptions personnalisées
Pour générer une exception, il suffit d’utiliser le mot clé throw, suivi d’un objet dont la classe dérive de Thowable. Si l’on veut générer une exception dans une méthode avec throw, il faut l’indiquer dans la déclaration de la méthode, en utilisant le mot clé throws.
En cas de nécessité, on peut créer ces propres exceptions. Elles descendent des classes Exception ou RunTimeException mais pas de la classe Error. Il est préférable (par convention) d’inclure le mot « Exception » dans le nom de la nouvelle classe.
Exemple ( code java 1.1 ) :
public class SaisieErroneeException extends Exception { public SaisieErroneeException() {
super();
}
public SaisieErroneeException(String s) {
super(s);
}
}
public class TestSaisieErroneeException {
public static void controle(String chaine) throws SaisieErroneeException {
if (chaine.equals(« ») == true)
throw new SaisieErroneeException(« Saisie erronee : chaine vide »);
}
public static void main(java.lang.String[] args) { String chaine1 = « bonjour »;
String chaine2 = « »;
try {
controle(chaine1);
}
catch (SaisieErroneeException e) { System.out.println(« Chaine1 saisie erronee »);
};
try {
controle(chaine2);
}
catch (SaisieErroneeException e) { System.out.println(« Chaine2 saisie erronee »);
};
}
}
Les méthodes pouvant lever des exceptions doivent inclure une clause throws nom_exception dans leur en tête. L’objectif est double : avoir une valeur documentaire et préciser au compilateur que cette méthode pourra lever cette exception et que toute méthode qui l’appelle devra prendre en compte cette exception (traitement ou propagation).
Si la méthode appelante ne traite pas l’erreur ou ne la propage pas, le compilateur génère l’exception nom_exception must be caught or it must be déclared in the thows clause of this méthode.
Java n’oblige la déclaration des exceptions dans l’en tête de la méthode que pour les exceptions dites controlées (checked). Les exception non controlées (unchecked) peuvent être capturée mais n’ont pas a être déclarée. Les exceptions et erreurs qui héritent de RunTimeException et de Error sont non controlées. Toutes les autres exceptions sont controlées.
8. Le multitâche
Un thread est une unité d’exécution faisant partie d’un programme. Cette unité fonctionne de façon autonome et parallèlement à d’autres threads. En fait, sur une machine monoprocesseur, chaque unité se voit attribuer des intervalles de temps au cours desquels elles ont le droit d’utiliser le processeur pour accomplir leurs traitements.
La gestion de ces unités de temps par le système d’exploitation est appelée scheduling. Il existe deux grands types de scheduler:
le découpage de temps utilisé par Windows et Macintosh OS jusqu’à la version 9. Ce système attribue un intervalle de temps prédéfini quel que soit le thread et la priorité qu’il peut avoir la préemption utilisée par les systèmes de type Unix. Ce système attribut les intervalles de temps en tenant compte de la priorité d’exécution de chaque thread. Les threads possédant une priorité plus élevée s’exécutent avant ceux possédant une priorité plus faible.
Le principal avantage des threads est de pouvoir répartir différents traitements d’un même programme en plusieurs unités distinctes pour permettre leurs exécutions « simultanées ».
La classe java.lang.Thread et l’interface java.lang.Runnable sont les bases pour le développement des threads en Java. Par exemple, pour exécuter des applets dans un thread, il faut que celles-ci implémentent l’interface Runnable.