Introduction en Java
Introduction en Java
Introduction en java
INFO 3 IFT TOAMASINA
JIMMY MENAVOARA
PLAN DU COURS
Il n'y a pas de compilation spécifique pour chaque plateforme. Le code reste 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.
Cette indépendance est assurée au niveau du code source grâce à Unicode et au niveau du
bytecode.
Java est orienté objet
Comme la plupart des langages récents, Java est orienté objet. Chaque fichier source contient
la définition d'une ou plusieurs classes qui sont utilisées les unes avec les autres pour former
une application. Java n'est pas complètement objet car il définit des types primitifs (entier,
caractère, flottant, booléen,).
Java est simple
Le choix de ses auteurs a été d'abandonner des éléments mal compris ou mal exploités des
autres langages tels que la notion de pointeurs (pour éviter les incidents en manipulant
directement la mémoire), l'héritage multiple et 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. Si une telle conversion doit être réalisée, le développeur doit obligatoirement
utiliser un cast ou une méthode statique fournie en standard pour la réaliser.
Java assure la gestion de la mémoire
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 de
mémoire laissées libres suite à la destruction des objets.
Java est sûr
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 pas y avoir d'accès direct à la
mémoire. L'accès au disque dur est réglementé dans une applet. Les applets fonctionnant sur le
Web sont soumises aux restrictions suivantes dans la version 1.0 de Java :
• 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
étant une 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 ils proviennent.
Historique de java
Depuis sa première diffusion publique le 23 mai 1995, le langage et les plateformes Java
ont été marqués par de nombreux événements dont les principaux sont :
Années Evènements
Objet
• Concept :
Une entité du monde réel (resp. virtuel) qui se caractérise par :
• Identité : propriété qui distingue chaque objet
• Etat : valeur de tous les attributs à un instant donné
• Comportement : caractérisé par les opérations qu’il peut exécuter
• Exemple :
Le point x = 2, y=5 ;
Classe
Concept :
• Abstraction d’un ensemble d’objets qui ont une structure identique et un même
comportement
• Sorte de moule pour créer des objets
• Une classe a un :
o Nom
o Des attributs //partie dynamique
o Des comportements //partie statique
Exemple :
Soit la classe Point qui représente un point. Sa description est comme suit :
• Nom : point
• Attributs : x, y
• Comportements : déplacer, afficher, getX, getY, setX, setY, etc, …
Encapsulation
• Protection d’accès aux données d’un objet. Il est impossible d’agir directement aux
données, il faut passer par les comportements ou méthodes
• Matérialisation à l’aide des modificateurs d’accès
Exemple :
Public class Point{
private int x ;
private int y ;
}
Héritage
Polymorphisme
Java 9 définit 54 mots réservés qui ne peuvent pas être utilisés comme identifiant.
Historiquement, les mots réservés (reserved words) peuvent être regroupés en deux catégories
:
• 51 mots clés (keywords) dont 3 ne sont pas utilisés
• 3 valeurs littérales (literals) : true, false et null
Les mots clés sont des mots réservés utilisés dans le code source Java en ayant un rôle particulier
dans la syntaxe du code.
Les mots clé const et goto sont réservés mais ne sont pas utilisés actuellement.
A partir de Java 9, le mot-clé _ (le caractère underscore) est non utilisé pour le moment mais il
est réservé pour une éventuelle utilisation future dans les déclarations de certaines variables.
A partir de Java 17, le mot-clé réservé strictfp est obsolète et ne devrait plus être utilisé dans le
code.
Plusieurs séquences sont fréquemment considérées à tort comme des mots clés du langage et
sont en fait des valeurs littérales :
• true et false : sont des valeurs littérales booléennes
• null qui est une valeur littérale
Différents types de mots-clés sont définis dans le langage Java en fonction de la version utilisée
:
• mot-clé réservé depuis Java 1.0
• mot-clé restreint (restricted keyword) à partir de Java 9
• identifiant restreint (restricted identifier) à partir de Java 10
mot-clé contextuel (contextual keyword) qui remplace les notions antérieures d'identifiant
restreint et de mot-clé restreint dans la JLS à partir de Java 17
Le langage Java définit plusieurs mots-clés contextuels (pour certains nommés initialement
mots-clés restreints ou identifiants restreints) depuis Java 9.
Les identifiants
Les identifiants (identifiers) sont utilisés pour nommer des éléments dans du code source Java
notamment les variables, les classes, les méthodes, les paramètres, les packages, les interfaces,
les énumérations, les étiquettes, les modules, ... Les identifiants permettent aux développeurs
d'utiliser un élément ailleurs dans le code.
Dans l'exemple ci-dessus, plusieurs identifiants sont utilisés dans le code source :
• MaClasse : est le nom d'une classe
• main : est le nom d'une méthode
• String : est le nom d'une classe du JDK
• args : est le nom d'un paramètre
• valeur : est le nom d'une variable locale
Les spécifications de Java imposent des règles strictes pour définir un identifiant valide.
Un identifiant est une séquence d'un ou plusieurs caractères (lettres et chiffres) dont le premier
est obligatoirement une lettre.
Exemple d’identifiants valides
$maVariable ma_variable_initialisée
variable_avec_un_nom_tres_long_avec_chaque_mot_separe_par_un_underscore
aa bb Contient un espace
Remarque : il est important de prêter une attention particulière lors de l'utilisation d'un
identifiant pour que celui-ci soit suffisamment significatif dans la compréhension de ce qu'il
représente.
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 caractère " ; ".
Une variable, identifié par un nom, permet d'accéder à une valeur. En Java, une variable est
obligatoirement typée statiquement à sa définition : une fois la variable définie, son type ne
peut pas changer.
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 où 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 :
• soit un type élémentaire dit aussi type primitif déclaré sous la forme
type_élémentaire variable;
• soit une classe déclarée sous la forme classe variable ;
Rappel : les noms de variables en Java peuvent commencer par une lettre, par le caractère de
soulignement ou par le signe dollar. Le reste du nom peut comporter des lettres ou des nombres
mais jamais d'espace. Il est possible de définir plusieurs variables de même type en séparant
chacune d'elles par une virgule.
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.
Il est possible en une seule instruction de faire la déclaration et l'affectation d'une valeur à une
variable ou plusieurs variables.
Les types élémentaires ont une taille identique quel que 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 laquelle le code
s'exécute.
Les types élémentaires commencent tous par une minuscule.
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 création. Cette
initialisation ne s'applique pas aux variables locales des méthodes de la classe.
Les valeurs par défaut lors de l'initialisation automatique des variables d'instances sont :
boolean false
char \u000
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 associative 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'écriture d'une opération d'affectation
associée à un opérateur mathématique :
= a = 10 équivalent à : a = 10
+= a += 10 équivalent à : a = a + 10
-= a -= 10 équivalent à : a = a - 10
*= a *= 10 équivalent à : a = a * 10
/= a/=10 équivalent à : a = a / 10
%= a %= 10 Reste de la division
^= a^=10 équivalent à : a = a ^ 10
Les comparaisons
== a == 10 Egalité
!= a != 10 diffèrent de
| a|b OU binaire
|| a || b OU logique (pour
expressions booléennes) :
l'évaluation de l'expression
cesse dès qu'elle devient
vraie
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 une erreur est
émise à la compilation. 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.
Il est nécessaire de mettre l'opération entre parenthèses 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.
X Y X/Y X%Y
0 0 NaN NaN
L’incrémentation et la décrémentation ;
l'opérateur ET &
l'opérateur OU |
l'opérateur OU logique ||
l'opérateur ternaire ?:
les opérateurs d'assignement =, +=, -=, *=, /=, %=, ^=, |=, <<=, >>=, >>>=
l'opérateur arrow ->
Les structures de contrôles
Si avant l'instruction while, le booléen est false, alors le code de la boucle ne sera jamais
exécuté.
Attention : ne pas mettre de ; après les parenthèses de la condition sinon cela produira une
boucle infinie qui consomme énormément de CPU uniquement pour tester la condition.
La condition est évaluée en fin de chaque itération : la boucle est donc exécutée au moins une
fois quelle que soit la valeur du booléen lors de la première itération.
Les boucles for :
La boucle for permet de réaliser une boucle dont la définition est composée de plusieurs parties
optionnelles. La syntaxe générale est :
Attention : comme toutes les parties sont optionnelles, il est facile de faire une boucle infinie
qui va consommer 100% de CPU jusqu'à ce que la JVM soit tuée.
La nouvelle forme de l'instruction for, spécifiée dans la JSR 201, permet de simplifier l'écriture
du code pour réaliser une telle itération et laisse le soin au compilateur de générer le bytecode
nécessaire.
L'utilisation de la syntaxe de l'instruction for améliorée peut être renforcée en combinaison avec
les génériques, ce qui évite l'utilisation d'un cast.
La syntaxe de l'instruction for améliorée peut aussi être utilisée pour parcourir tous les éléments
d'un tableau.
Cela permet d'éviter la déclaration et la gestion dans le code d'une variable contenant l'index
courant lors du parcours du tableau.
Les branchements conditionnels
Les instructions de branchements conditionnels permettent l'exécution d'un bloc de code selon
le résultat de l'évaluation d'une condition.
L’instruction if :
L'instruction if permet d'exécuter l'instruction ou le bloc de code qui la suit si l'évaluation de la
condition booléenne est vraie.
Il est possible d'utiliser une instruction optionnelle else qui précise une instruction ou un bloc
de code à exécuter si l'évaluation de la condition booléenne est fausse. La syntaxe générale est
de la forme :
if (boolean) {
...
} else if (boolean) {
...
} else {
...
}
L’opérateur ternaire :
L'opérateur ternaire est un raccourci syntaxique pour une instruction if/else qui renvoie
simplement une valeur selon l'évaluation de la condition.
La syntaxe générale est de la forme :
Si une instruction case ne contient pas de break alors les traitements associés au case suivant
sont exécutés : cette fonctionnalité se nomme le fall through. Cela permet d'exécuter les mêmes
instructions pour plusieurs valeurs.
Avant Java 7, on ne peut utiliser l'instruction switch qu'avec des types primitifs d'une taille
maximum de 32 bits (byte, short, int, char) ou une énumération. L'utilisation d'une chaîne de
caractères dans une instruction switch provoquait une erreur à la compilation "Incompatible
Types. Require int instead of String".
A partir de Java SE 7, il est possible d'utiliser un objet de type String dans l'expression fournie
à l'instruction switch.
L'instruction switch compare la valeur de la chaîne de caractères avec la valeur fournie à chaque
instruction case comme si elle utilisait la méthode String.equals(). Dans les faits, le compilateur
utilise la méthode String.hashCode() pour faire la comparaison. Le compilateur va ainsi générer
un code qui est plus optimisé que le code équivalent avec des instructions if/else.
Important : il est nécessaire de vérifier que la chaîne de caractères évaluée par l'instruction
switch ne soit pas null sinon une exception de type NullPointerException est levée.
Le test réalisé par l'instruction switch est sensible à la casse : il faut donc en tenir compte si un
test ne l'est pas.
A partir de Java 14, l'instruction switch a été revue en profondeur pour proposer 4 formes
syntaxiques, une utilisation comme instruction ou comme une expression, la possibilité de gérer
plusieurs valeurs dans un case. Ces nouvelles possibilités sont détaillées dans une section
suivante.
Il est possible d'imbriquer des instructions switch même si cela n'est pas recommandé pour des
raisons de lisibilité.
Pour certains cas d'usage, l'instruction switch peut être remplacée avantageusement par une
utilisation du polymorphisme.
Les débranchements
Remarque : l'instruction break, lorsqu'elle est utilisée à l'intérieur d'un ensemble de boucles
imbriquées, n'interrompt que la boucle la plus interne.
L’instruction continue :
L'instruction continue s'utilise dans une boucle pour passer directement à l'itération suivante.
La syntaxe est de la forme :
continue;
L'instruction continue peut être utilisée à l'intérieur des structures de contrôle de type boucles.
À l'intérieur de la boucle, lorsqu'une instruction continue est rencontrée, le contrôle saute
directement au début de la boucle pour aller à l'itération suivante : cela met immédiatement fin
à l'exécution des instructions de l'itération en cours.
L'instruction continue est utilisée pour sauter une condition particulière et poursuivre
l'exécution à la prochaine itération.
L'instruction continue est utilisable avec toutes les types de boucle en Java :
Dans le cas d'une boucle for, le mot clé continue force le contrôle à sauter immédiatement à
l'instruction d'incrémentation.
• dans le cas d'une boucle while ou do-while, le contrôle saute immédiatement à l'expression
booléenne
• dans le cas d'une boucle for améliorée, le contrôle saute à l'élément suivant
Exemple avec une boucle for :
Exemple avec une boucle while :
Il est possible d'utiliser une instruction continue dans des boucles imbriquées. L'instruction
continue permet d'ignorer le reste des traitements de l'itération en cours. Dans le cas de boucles
imbriquées, elle passe à l'itération suivante de la boucle la plus interne.
Exemple avec une boucle for améliorée
L’instruction return :
L'instruction return est utilisée pour sortir immédiatement de l'exécution d'une méthode et
retourner le contrôle à l'appelant.
Elle peut être utilisée dans une méthode avec ou sans valeur de retour.
Pour les méthodes qui définissent un type de retour, l'instruction return doit être immédiatement
suivie de la valeur de retour.
Dans une telle méthode, toutes les branches de sortie de la méthode doivent avoir une
instruction return qui fournit la valeur retournée pour la branche.
Pour régler l'erreur, il faut utiliser une instruction return dans la branche else de l'instruction if.
L'instruction return peut être utilisée à différents endroits dans une méthode, mais il faut
toujours s'assurer qu'elle doit être la dernière instruction à être exécutée dans une méthode. Si
ce n'est pas le cas, une erreur est émise par le compilateur.
L'instruction return ne doit pas nécessairement être la dernière instruction d'une méthode, mais
elle doit être la dernière instruction à être exécutée dans une méthode.
Dans une méthode qui ne retourne pas de valeur, il est possible d'utiliser une instruction return
pour arrêter immédiatement les traitements de la méthode.
Les tableaux
Les tableaux permettent de stocker un ensemble fini d'éléments d'un type particulier. L'accès à
un élément particulier se fait grâce à son indice. Le premier élément d'un tableau possède
l'indice 0.
Même si leur déclaration est spécifique, ce sont des objets : ils sont donc dérivés de la classe
Object. Il est possible d'utiliser les méthodes héritées telles que equals() ou getClass().
La déclaration et l’allocation des tableaux :
La déclaration d'un tableau à une dimension requiert un type, le nom de la variable permettant
de faire référence au tableau et une paire de crochets. Java permet de placer les crochets sur le
type ou sur le nom de la variable dans la déclaration.
L'allocation de la mémoire et donc l'obtention d'une référence pour accéder au tableau se fait
en utilisation l'opérateur new, suivi d'une paire de crochets qui doit contenir le nombre
maximum d'éléments que peut contenir le tableau.
La déclaration et l'allocation peut se faire sur une même ligne ou sur des lignes distinctes.
Pour passer un tableau à une méthode, il suffit de déclarer le paramètre dans la signature de la
méthode.
Les tableaux sont toujours transmis par référence puisque ce sont des objets.
Java ne supporte pas directement les tableaux à plusieurs dimensions : il faut déclarer un tableau
de tableau en utilisant une paire de crochet pour chaque dimension.
Un accès a un élément d'un tableau qui dépasse sa capacité, lève une exception du type
java.lang.arrayIndexOutOfBoundsException.
A partir de Java 5, le parcours de l'intégralité des éléments d'un tableau peut être réalisé en
utilisant la version améliorée de l'instruction for. La syntaxe générale est de la forme :
for ( type element_courant : type[] ) {
// code à exécuter sur l'élément courant identifié par la variable element_courant
}
Les conversions de types
Lors de la déclaration, il est possible d’utiliser un cast :
Classe Rôle
Les classes portent le même nom que le type élémentaire sur lequel elles reposent avec la
première lettre en majuscule.
Ces classes contiennent généralement plusieurs constructeurs ou des méthodes statiques pour
obtenir des instances.
Des surcharges de la méthode valueOf() sont également définies pour des arguments de type
boolean, long, float, double et char.
La conversion d'une chaîne de caractères String en entier int
L'autoboxing et l'unboxing
Le concept de classe
Modificateurs Rôle
Les modificateurs abstract et final ainsi que public et private sont mutuellement exclusifs.
Marquer une classe comme final peut permettre au compilateur et à la JVM de réaliser quelques
petites optimisations.
Le mot clé extends permet de spécifier une super-classe é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.
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, seules les données sont différentes à chaque objet.
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 :
nom_de_classe nom_de_variable
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 constructeur. 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.
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 grâce à l'opérateur new.
Exemple :
nom_de_classe nom_d_objet = new nom_de_classe( ... );
Le littéral nul
Le littéral null est utilisable partout où il est possible d'utiliser une référenc à un objet. Il
n'appartient pas à une classe mais il peut être utilisé à la place d'un objet de n'importe quel type
ou comme paramètre. null ne peut pas être utilisé comme un objet normal : il n'y a pas d'appel
de méthodes et aucune classe ne peut en hériter.
Le fait d'affecter null une variable référençant un objet pourra permettre au ramasse-miettes de
libérer la mémoire allouée à l'objet si aucune autre référence n'existe encore sur lui.
Les variables de classe
Elles ne sont définies qu'une seule fois quel que soit le nombre d'objets instanciés de la classe.
Leur déclaration est accompagnée du mot clé static.
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.
Ce type de variable est utile pour, par exemple, compter le nombre d'instanciations de la classe.
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 égal à l'instance de l'objet dans lequel il est utilisé.
Il est préférable de préfixer la variable d'instance par le mot clé this.
This est aussi utilisé quand l'objet doit appeler une méthode en se passant lui-même en
paramètre de l'appel.
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.
Dans le cas ci-dessus, même si o est une instance de MaClasse, il n'est pas permis d'appeler une
méthode de MaClasse car o est de type Objet.
Modificateur Rôle
Il est aussi possible par exemple de mémoriser les valeurs min et max d'un ensemble d'objets
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. Ces méthodes peuvent être utilisées sans instancier
un objet de la classe. Les méthodes ainsi définies peuvent être appelées avec la notation
classe.methode() au lieu de objet.methode() : la première forme est fortement recommandée
pour éviter toute confusion. 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.
Le mot clé final
Le mot clé final s'applique aux variables de classe ou d'instance ou locales, aux méthodes, aux
paramètres d'une méthode et aux classes. Il permet de rendre l'entité sur laquelle il s'applique
non modifiable une fois qu'elle est déclarée pour une méthode ou une classe et initialisée pour
une variable.
Une variable qualifiée de final signifie que la valeur de la variable ne peut plus être modifiée
une fois que celle-ci est initialisée.
Une fois la variable déclarée final initialisée, il n'est plus possible de modifier sa valeur. Une
vérification est opérée par le compilateur.
Une méthode déclarée 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 une 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'exécution, en fonction du type de l'objet appelé (c'est la notion de
polymorphisme qui sera détaillée ultérieurement). Ces tests ont un coût en termes de
performance.
Quatre types de variables sont implicitement déclarés final :
• un champ d'une interface
• une variable locale déclarée comme ressource d'une instruction try-with-resources
• un paramètre d'exception d'une clause multi-catch
• un champ correspondant à un composant d'un record
Remarque : un unique paramètre d'exception d'une clause catch n'est jamais déclaré final
implicitement, mais peut être effectivement final.
Le mot clé abstract
Le mot clé 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
pourront profiter des méthodes héritées et n'auront à implémenter que les méthodes déclarées
abstract.
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.
Les variables d'instances
Une variable d'instance nécessite simplement une déclaration de la variable dans le corps de la
classe.
Les méthodes
Les méthodes sont des fonctions qui implémentent les traitements de la classe.
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 et le nombre d'arguments déclarés doivent correspondre 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 grâce à ses 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 Rôle
Sans modificateur, la méthode peut être appelée par toutes autres méthodes des classes du
package auquel appartient la classe.
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.
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 : public static void main (String args[]) { ... }
Cette déclaration de la méthode main() est imposée par la machine virtuelle pour reconnaitre le
point d'entrée d'une application. Si la déclaration de la méthode main() diffère, une exception
sera levée lors de la tentative d'exécution par la machine virtuelle.
Si la méthode retourne un tableau alors les caractères [] peuvent être précisés après le type de
retour ou après la liste des paramètres :
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 constructeur 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 aucun constructeur n'est explicitement défini
dans la classe, le compilateur va créer un constructeur par défaut sans argument. Dès qu'un
constructeur est explicitement défini, le compilateur 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, n'est pas mis en oeuvre. Si on souhaite maintenir ce mécanisme,
il faut définir explicitement un constructeur sans paramètres en plus des autres constructeurs.
Il existe plusieurs manières de définir un constructeur :
1. 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.
2. le constructeur avec initialisation fixe : il permet de créer un constructeur par
défaut.
L’héritage
L'héritage est un mécanisme qui facilite la réutilisation du code et la gestion de son évolution.
Elle définit une relation entre deux classes :
• une classe mère ou super-classe
• une classe fille ou sous-classe qui hérite de sa classe mère
Le principe de l’héritage
Grâce à l'héritage, les objets d'une classe fille ont accès aux données et aux méthodes de la
classe parente 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 se 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éritages
successifs toutes les classes héritent d'Object.
La mise en œuvre 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 mère.
Pour invoquer une méthode d'une classe mère, il suffit d'indiquer la méthode préfixée par super.
Pour appeler le constructeur de la classe mère, il suffit d'écrire super(paramètres) avec les
paramètres adéquats.
Le lien entre une classe fille et une classe mère est géré par la plate-forme : une évolution des
règles de gestion de la classe mère 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.
Les packages
En Java, il existe un moyen de regrouper des classes voisines ou qui couvrent un même domaine
: ce sont les packages.
La définition d’un package
Pour réaliser un package, on écrit un nombre quelconque de classes dans plusieurs fichiers d'un
même répertoire et au début de chaque fichier on met la directive ci-dessous où nom-du-
package doit être composé des répertoires séparés par un caractère point : package nom-du
package;
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.
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).
Les importations
Pour pouvoir utiliser un type en Java, il faut utiliser le nom pleinement qualifié du type qui
inclue le nom du package avec la notation utilisant un point.
Afin de réduire la verbosité lors de l'utilisation de type, il est possible d'utiliser les imports. Ils
utilisent le mot clé import qui définit un alias du nom pleinement qualifié d'un type vers
simplement le nom du type.
Il y a deux manières d'utiliser les imports :
• Préciser un nom de classe ou d'interface qui sera l'unique entité importée
• Ou indiquer un package suivi d'un point et d'un caractère * indiquant toutes les
classes et interfaces définies dans le package
Exemple Rôle