Développons en Java
v 2.30   Copyright (C) 1999-2022 .   
2. Les notions et techniques de base en Java 4. La programmation orientée objet Imprimer Index Index avec sommaire Télécharger le PDF

 

3. La syntaxe et les éléments de bases de Java

 

chapitre    3

 

Niveau : niveau 1 Fondamental 

 

Ce chapitre détaille la syntaxe et les éléments de bases du langage Java. Cette syntaxe est largement inspirée de celle du langage C.

Ce chapitre contient plusieurs sections :

 

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 caractère ';' (point-virgule).

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 mots réservés du langage Java

Java 11 utilise 54 mots réservés qui ne peuvent pas être utilisés comme identifiant.

Les mots réservés (reserved words) peuvent être regroupés en deux catégories :

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.

abstract continue for new switch
assert (Java 1.4) default goto package synchronized
boolean do if private this
break double implements protected throw
byte else import public throws
case enum (Java 5) instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp (Java 1.2) volatile
const float native super while
_ (Java 9)        

Les mots clé const et goto sont réservés mais ne sont pas utilisés actuellement.

Plusieurs séquences sont fréquemment considérées à tort comme des mots clés du langage :

A partir de Java 9, plusieurs autres instructions ont une signification particulière dans le contexte de la définition d'un module dans un fichier module-info.java :

exports

open

provides

to

uses

module

opens

requires

transitive

with

 

3.3. 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 identifiants permettent aux développeurs d'utiliser un élément ailleurs dans le code.

Exemple :
public class MaClasse {
  public static void main(String[] args) {
    int valeur = 10;
  }
}

Dans l'exemple ci-dessus, plusieurs identifiants sont utilisés dans le code source :

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.

La première lettre est un caractère Unicode pour laquelle la valeur passée en paramètre de la méthode isJavaIdentifierStart() de la classe Character renvoie true. Le premier caractère ne peut donc pas être un chiffre.

Une lettre peut être entre-autre :

La quasi-totalité des caractères Unicode peuvent être utilisés ce qui permet d'écrire des identifiants dans toutes les langues. Cependant plusieurs caractères ne peuvent pas être utilisés dans un identifiant : c'est notamment le cas des caractères qui ont une utilité dans le langage comme par exemple un espace, une tabulation, les caractères #, @, !, -, *, + , /, %, (, ), ...

Les identifiants Java sont sensibles à la casse.

La taille d'un identifiant n'est pas limitée mais pour des raisons de lisibilité, il est préférable qu'ils ne soient pas trop petit ni trop grand.

Remarque : depuis Java 9, l'identifiant composé uniquement d'un caractère underscore n'est plus valide car _ est devenu un mot clé du langage.

Un identifiant ne peut pas être :

Les identifiants utilisés pour un type doivent respecter les règles générales de définition d'un identifiant. Depuis Java 10, l'identifiant d'un type ne peut pas être var qui est utilisé de manière particulière dans le contexte de la définition d'une variable locale.

Exemple d'identifiants valides :

_ (jusqu'à Java 8)

a

a1

_$

mavariable

MaVariable

MAVARIABLE

maVariable

ma_variable

mavariable1

ma1variable

mavariable_

_mavariable

ma$variable1

_maVariable

$maVariable

ma_variable_initialisée

identifiant en cyrilique

   

variable_avec_un_nom_tres_long_avec_chaque_mot_separe_par_un_underscore


Exemple d'identifiants invalides :

Identifiant invalide

Raison pour laquelle il est invalide

1aabbb

Commence par un chiffre

aa-bb

Contient un caractère moins

aa bb

Contient un espace

_

Le caractère underscore est devenu un mot clé en Java 9

aa*bb

Contient un caractère *

()_

Contient des parenthèses

true

Est une valeur littérale réservée

new

Est un mot clé du langage

aa+bb

Contient un caractère +

aa&bb

Contient un caractère &

aa@bb

Contient un caractère @

aa#bb

Contient un caractère #


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

Il est aussi recommandé de respecter Des normes de développement

 

3.4. Les identifiants restreints

Plusieurs identifiants sont considérés comme des identifiants restreints (restricted identifiers) car ils ont un rôle particulier dans certains contextes ce qui ne rend pas possible leur utilisation comme identifiant dans certains cas particuliers.

record (Java 16)

var (Java 10)

yield (Java 13)


Le nom d'un type (type identifier) ne peut pas être var, yield ou record.

Exemple ( code Java 10 ) :
public class var {
}

Résultat :
C:\java>javac -version
javac 10.0.2
 
C:\java>javac var.java
var.java:1: error: 'var' not allowed here
public class var {
             ^
  as of release 10, 'var' is a restricted local variable type and cannot be used for type 
declarations
1 error
 
C:\java>

Exemple ( code Java 13 ) :
public class yield {
}

En Java 13, le compilateur émet un avertissement si un type se nomme yield.

Résultat :
C:\java>javac -version
javac 13
 
C:\java>javac yield.java
yield.java:1: warning: 'yield' may become a restricted type name in a future release and 
may be unusable for type declarations or as the element type of an array
public class yield {
             ^
1 warning
 
C:\java>

A partir de Java 14, le compilateur émet si un type se nomme yield.

Résultat :
C:\java>javac -version
javac 14
 
C:\java>javac yield.java
yield.java:1: error: 'yield' not allowed here
public class yield {
             ^
  as of release 13, 'yield' is a restricted type name and cannot be used for type declarations
1 error
 
C:\java>

var a une signification spéciale lors de la déclaration d'une variable locale et d'un paramètre d'une expression lambda pour demander l'inférence du type par le compilateur.

yield a une signification particulière dans une instruction switch utilisée comme une expression pour indiquer une valeur de retour. Les invocations d'une méthode nommée yield() doivent être qualifiées de façon à se distinguer d'une utilisation de yield comme instruction.

Exemple :
public class TestYield {
 
  public void yield() {
  }
 
  public void traiter() {
    yield();
  }
}

Ce code se compile sans erreur jusqu'à Java 12

Résultat :
C:\java>javac -version
javac 12
 
C:\java>javac TestYield.java
 
C:\java>

En Java 13, le compilateur émet un avertissement lors de l'utilisation de yield comme identifiant.

Résultat :
C:\java>javac -version
javac 13
 
C:\java>javac TestYield.java
TestYield.java:7: warning: 'yield' may become a restricted identifier in a future release
    yield();
    ^
  (to invoke a method called yield, qualify the yield with a receiver or type name)
1 warning
 
C:\java>

A partir de Java 14, le compilateur émet une erreur car il faut obligatoirement qualifier l'invocation d'une méthode nommée yield().

Résultat :
C:\java>javac -version
javac 14
 
C:\java>javac TestYield.java
TestYield.java:7: error: invalid use of a restricted identifier 'yield'
    yield();
    ^
  (to invoke a method called yield, qualify the yield with a receiver or type name)
1 error
 
C:\java>

record a une signification particulière dans une déclaration d'une classe record.

Jusqu'à Java 12, un type peut se nommer record.

Exemple ( code Java 12 ) :
public class record {
}

En Java 14 et 15, le compilateur émet un avertissement si un type se nomme record.

Résultat :
C:\java>javac -version
javac 14
 
C:\java>javac record.java
record.java:1: warning: 'record' may become a restricted type name in a future release and
 may be unusable for type declarations or as the element type of an array
public class record {
             ^
1 warning
 
C:\java>

A partir de Java 16, le compilateur émet une erreur si un type se nomme record.

Résultat :
C:\java>javac -version
javac 16.0.1
 
C:\java>javac record.java
record.java:1: error: 'record' not allowed here
public class record {
             ^
  as of release 14, 'record' is a restricted type name and cannot be used for type declarations
1 error
 
C:\java>

Java définit aussi plusieurs mots clés restreints (restricted keywords) : ces mots clés sont considérés comme tel leur de leur utilisation dans un contexte particulier.

Dans le contexte de la définition d'un descripteur de modules, dix mots clés restreints existent : open, module, requires, transitive, exports, opens, to, uses, provides et with.

Dans tous les autres contextes, ils sont considérés comme des identifiants notamment pour permettre d'assurer la compatibilité dans du code antérieure. Il y a une exception : immédiatement à droite de requires dans un descripteur de module, transitive est considéré comme un mot clé sauf s'il est suivie d'un séparateur, auquel cas elle est comme un identifiant.

 

3.5. 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 ";".

Il existe trois types de commentaire en Java :

Type de commentaires Exemple
commentaire abrégé // commentaire sur une seule ligne
int N=1; // déclaration du compteur
commentaire multiligne
/* commentaires ligne 1
   commentaires ligne 2 */
commentaire de documentation automatique
/**
  * commentaire de la méthode
  * @param val la valeur à traiter
  * @since 1.0
  * @return la valeur de retour
  * @deprecated Utiliser la nouvelle méthode XXX
  */

 

3.6. La déclaration et l'utilisation de variables

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.

 

3.6.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 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 :

Exemple :
long nombre;
int compteur;
String chaine;

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.

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. A partir de Java 5, l'autoboxing permet la conversion automatique d'un type primitif vers son wrapper correspondant. Le mécanisme inverse est nommé unboxing.

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.6.2. Les types élémentaires

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.

Type Désignation Longueur Valeurs Commentaires
boolean valeur logique : true ou false
1 bit
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 du code Java
int entier signé
32 bits
-2147483648 à 2147483647
 
float virgule flottante simple précision (IEEE754)
32 bits
1.401e-045 à 3.40282e+038
 
double virgule flottante double précision (IEEE754)
64 bits
2.22507e-308 à 1.79769e+308
 
long entier long
64 bits
-9223372036854775808 à 9223372036854775807
 

Les types élémentaires commencent tous par une minuscule.

 

3.6.3. Le format des types élémentaires

Le format des nombres entiers :

Il existe plusieurs formats pour les 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 :

Il existe plusieurs formats pour les nombres décimaux. Les types float et double stockent des nombres flottants : pour être reconnus comme tels 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 pas de partie décimale.

Exemple :
float pi = 3.141f;
double valeur = 3d;
float flottant1 = +.1f , flottant2 = 1e10f; 

Par défaut un littéral représentant une valeur décimale est de type double : pour définir un littéral représentant une valeur décimale de type float il faut le suffixer par la lettre f ou F.

stop Attention :
float pi = 3.141; // erreur à la compilation 
float pi = 3.141f; // compilation sans erreur

Exemple :
double valeur = 1.1; 

Le format des caractères :

Un caractère est codé sur 16 bits 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.6.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 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 :

Type
Valeur par défaut
boolean
false
byte, short, int, long
0
float, double
0.0
char
\u000
classe
null

information Remarque : Dans une applet, il est préférable de faire les déclarations et initialisations dans la méthode init().

 

3.6.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 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 :

Opérateur
Exemple
Signification
=
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
<<=
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é

 

stop Attention : Lors d'une opération sur des opérandes de types 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.6.6. Les entiers exprimés en binaire (Binary Literals)

Avec Java 7, la valeur des types entiers (byte, short, int, et long) peut être exprimée dans le système binaire en utilisant le préfixe 0b ou 0B

Exemple ( code Java 7 ) :
  public static void testEntierBinaire() {
    byte valeurByte = (byte) 0b00010001;
    System.out.println("valeurByte = " + valeurByte);
    valeurByte = (byte) 0B10001;
    System.out.println("valeurByte = " + valeurByte);
    valeurByte = (byte) 0B11101111;
    System.out.println("valeurByte = " + valeurByte);
    short valeurShort = (short) 0b1001110111101;
    System.out.println("valeurShort = " + valeurShort);
    int valeurInt = 0b1000;
    System.out.println("valeurInt = " + valeurInt);
    valeurInt = 0b1001110100010110100110101000101;
    System.out.println("valeurInt = " + valeurInt);
    long valeurLong =
        0b010000101000101101000010100010110100001010001011010000101000101L;
    System.out.println("valeurLong = " + valeurLong);
  }

 

3.6.7. Utilisation des underscores dans les entiers littéraux

Il n'est pas facile de lire un nombre qui compte de nombreux chiffres : dès que le nombre de chiffres dépasse 9 ou 10 la lecture n'est plus triviale, ce qui peut engendrer des erreurs.

A partir de Java 7, il est possible d'utiliser un ou plusieurs caractères tiret bas (underscore) entre les chiffres qui composent un entier littéral. Ceci permet de faire des groupes de chiffres pour par exemple séparer les milliers, les millions, les milliards, ... afin d'améliorer la lisibilité du code.

Exemple ( code Java 7 ) :
    int maValeur = 123_1456_789;
    maValeur = 4_3;
    maValeur = 4___3;
    maValeur = 0x4_3;     
    maValeur = 0_43;             
    maValeur = 04_3;               
    maValeur = 0b1001110_10001011_01001101_01000101;
    long creditCardNumber = 1234_5678_9012_3456L;
    long numeroSecuriteSociale = 1_75_02_31_235_897L;
    long octetsDebutFichierClass = 0xCAFE_BABE;
    long maxLong = 0x7fff_ffff_ffff_ffffL;
    float pi = 3.141_593f;

Un nombre quelconque de caractères de soulignement (underscore) peut apparaître n'importe où entre les chiffres d'un littéral numérique. Le caractère underscore doit être placé uniquement entre deux chiffres. Il n'est donc pas possible de l'utiliser :

Exemple ( code Java 7 ) :
  // toutes ces expressions provoquent une erreur de compilation
  int maValeur = _43;            
  int maValeur = 43_;             
  int x5 = 0_x43;
  int x6 = 0x_43;           
  int x8 = 0x43_; 
  float pi1 = 3_.141593F;      
  float pi2 = 3._141593F;     
  long numeroSecuriteSociale = 1750231235897_L;

Le caractère underscore ne modifie pas la valeur mais facilite simplement sa lecture.

 

3.6.8. 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
? :
a ? b : c
opérateur conditionnel : renvoie la valeur b ou c selon l'évaluation de 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érieur d'une expression qui est analysée de gauche à droite:

L'usage des parenthèses permet de modifier cet ordre de priorité.

 

3.7. 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.7.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é promotion entière. Ce mécanisme fait partie des règles mises 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 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.

Exemple :
x = (short) ( x + y );

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.

Exemple :
/* test sur la division par zéro de nombres entiers */ 
class test3 {
  public static void main (String args[]) {
    int valeur=10;
    double resultat = valeur / 0;
    System.out.println("index = " + resultat);
  }
}

 

3.7.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 :

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 zéro de nombres flottants */ 

class test2 {
  public static void main (String args[]) {
    float valeur=10f; 
    double resultat = valeur / 0;
    System.out.println("index = " + resultat);
  }
}

 

3.7.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'issue 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 incrémentations préfixées et postfixées */

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.8. La priorité des opérateurs

Java définit les priorités dans les opérateurs comme suit ( du plus prioritaire au moins prioritaire )

les opérateurs postfix
expr++
expr--
les opérateurs unaires
++expr
--expr
+expr
-expr
˜
!
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
<
>
<=
>=
instanceof
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
||
l'opérateur ternaire
? :
les opérateurs d'assignement
=
+=
-=
*=
/=
%=
^=
|=
<<=
>>=
>>>=
l'opérateur arrow
->

L'ordre d'interprétation des opérateurs peut être modifié par l'utilisation de parenthèses.

 

3.9. Les structures de contrôles

Comme la quasi-totalité des langages de développement orientés objets, Java propose un ensemble d'instructions qui permettent d'organiser et de structurer les traitements. L'usage de ces instructions est similaire à celui rencontré avec leur équivalent dans d'autres langages.

 

3.9.1. Les boucles

Java propose plusieurs types de boucles :

Une boucle while() permet d'exécuter l'instruction ou le bloc de code qui la suit tant que la condition booléenne est évaluée à vrai. La condition est évaluée en début de chaque itération. La syntaxe générale est de la forme :

while ( boolean ) {
  // code à exécuter dans la boucle
}

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é.

Une boucle do ... while() permet d'exécuter l'instruction ou le bloc de code qui la suit tant que la condition booléenne est évaluée à vrai. La condition est évaluée en fin de chaque itération. La syntaxe générale est de la forme :

do {
  // ...
} while ( boolean );

Cette boucle est au moins exécutée une fois quelle que soit la valeur du booléen.

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 :

for ( initialisation; condition; modification ) {
    ...
}

Exemple :
int i =0;
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 optionnelles.

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 être 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 à l'aide d'une étiquette pour permettre de l'interrompre même si cela est peu recommandé :

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;
  }
}

Java 1.5 propose une nouvelle syntaxe pour les boucles for : les boucles for évoluées pour faciliter le parcours intégral des Iterator et des tableaux.

L'itération sur les éléments d'une collection est verbeuseuse avec la déclaration d'un objet de type Iterator.

Exemple :
import java.util.*;

public class TestForOld {

  public static void main(String[] args) {
    List liste = new ArrayList();
    for(int i = 0; i < 10; i++) {
      liste.add(i);
    }
    
    for (Iterator iter = liste.iterator(); iter.hasNext(); ) {
      System.out.println(iter.next());
    }
  }
}

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 code nécessaire.

Exemple (java 1.5) :
import java.util.*;

public class TestFor {

  public static void main(String[] args) {
    List liste = new ArrayList();
    for(int i = 0; i < 10; i++) {
      liste.add(i);
    }
    
    for (Object element : liste) {
      System.out.println(element);
    }
  }
}

L'utilisation de la syntaxe de l'instruction for évouée peut être renforcée en combinaison avec les generics, ce qui évite l'utilisation d'un cast.

Exemple (java 1.5) :
import java.util.*;
import java.text.*;

public class TestForGenerics {

  public static void main(String[] args) {
    List<Date> liste = new ArrayList();
    for(int i = 0; i < 10; i++) {
      liste.add(new Date());
    }
    
    DateFormat df = DateFormat.getDateInstance();

    for (Date element : liste) {
      System.out.println(df.format(element));
    }
  }
}

La syntaxe de l'instruction for évoluée peut aussi être utilisée pour parcourir tous les éléments d'un tableau.

Exemple (java 1.5) :
import java.util.*;

public class TestForArray {

  public static void main(String[] args) {
    int[] tableau = {0,1,2,3,4,5,6,7,8,9};
    
    for (int element : tableau) {
      System.out.println(element);
    }
  }
}

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. L'exemple précédent fait aussi usage de l'auto unboxing.

 

3.9.2. Les branchements conditionnels

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 vrai.

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'instruction switch permet d'exécuter du code selon l'évaluation de la valeur d'une expression. La syntaxe générale est de la forme :

switch (expression) {
    case constante1 :
        instr11;
        instr12;
        break;

    case constante2 :
        ...
        break;
    default :
        ...
}

Avant Java 7, on ne peut utiliser switch qu'avec des types primitifs d'une taille maximum de 32 bits (byte, short, int, char) ou une énumération. A partir de Java 7, il est possible d'utiliser un type String avec l'instruction switch.

Si une instruction case ne contient pas de break alors les traitements associés au case suivant sont exécutés. Cela permet d'exécuter les mêmes instructions pour plusieurs valeurs.

Il est possible d'imbriquer des switch même si cela n'est pas reommandé pour des raisons de lisiblité.

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 : ( condition ) ? valeur-vrai : valeur-faux

Exemple :
if (niveau == 5) // equivalent à total = (niveau ==5) ? 10 : 5;
total = 10;
else total = 5 ; 

System.out.println((genre == " H ") ? " Mr " : " Mme ");

Avant Java 7, l'instruction switch ne pouvaient être utilisée qu'avec des types primitifs ou des énumérations. 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".

Pour limiter l'utilisation d'instructions if/else utilisées avec des chaînes de caractères, il est possible d'utiliser l'instruction switch avec des énumérations.

A partir de Java SE 7, il est possible d'utiliser un objet de type String dans l'expression fournie à l'instruction Switch.

Exemple ( code Java 7 ) :
  public static Boolean getReponse(String reponse) {
    Boolean resultat = null;
    switch(reponse) {
      case "oui" : 
      case "Oui" :
        resultat = true;
        break;
      case "non" : 
      case "Non" :
        resultat = false;
        break;
      default: 
        resultat = null;
        break;
    }
    return resultat;
  }

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.

Exemple ( code Java 7 ) :
    public static Boolean getReponse(String reponse) {
    Boolean resultat = null;
    
    switch (reponse.toLowerCase()) {
      case "oui":
        resultat = true;
        break;
      case "non":
        resultat = false;
        break;
      default:
        resultat = null;
        break;
    }
    return resultat;
  }

L'instruction switch peut toujours être remplacée avantageusement par une utilisation du polymorphisme.

 

3.9.3. Les débranchements

L'instruction break permet de quitter immédiatement une boucle ou un branchement. Elle est utilisable dans tous les contrôles de flot.

L'instruction continue s'utilise dans une boucle pour passer directement à l'itération suivante

break et continue peuvent s'exécuter 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 caractère deux-points qui définit le début d'une instruction.

Remarque : l'utilisation dans le code des débranchements n'est pas recommandée.

 

3.10. 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().

 

3.10.1. 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.

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

Pour passer un tableau à une méthode, il suffit de déclarer le paramètre dans la signature de la méthode

Exemple :
public void afficher(String texte[]){
  // ... 
}

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.

Exemple :
float tableau[][] = new float[10][10];

La taille des tableaux de la seconde dimension peut ne pas être identique pour chaque occurrence.

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 null pour les chaînes de caractères et les autres objets.

 

3.10.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éments de chaque ligne 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.10.3. Le parcours d'un tableau

La variable length retourne le nombre d'éléments du tableau. Il est alors possible d'utiliser une boucle pour itérer sur chacun des éléments du tableau.

Exemple :
for (int i = 0; i < tableau.length; i ++) {
  // ...
}

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 évolué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
}

Exemple :
    String[] chaines = {"element1","element2","element3"};
    for (String chaine : chaines) {
      System.out.println(chaine);
    }

 

3.11. 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 se 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 Rôle
String pour les chaînes de caractères Unicode
Integer pour les valeurs entières (integer)
Long pour les entiers longs signés (long)
Float pour les nombres à virgule flottante (float)
Double pour les nombres à virgule flottante en double précision (double)

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.

 

3.11.1. La conversion d'un entier int en chaîne de caractères String

Exemple :
int    i        = 10;
String montexte = new String();
montexte = montexte.valueOf(i);

Des surcharges de la méthode valueOf() sont également définies pour des arguments de type boolean, long, float, double et char

 

3.11.2. La conversion d'une chaîne de caractères String en entier int

Exemple :
String  montexte  = "10";
Integer monnombre = new Integer(montexte);
int     i         = monnombre.intValue(); // conversion d'Integer en int

 

3.11.3. La conversion d'un entier int en entier long

Exemple :
int     i         = 10;
Integer monnombre = new Integer(i);
long    j         = monnombre.longValue();

 

3.12. L'autoboxing et l'unboxing

L'autoboxing permet de transformer automatiquement une variable de type primitif en un objet du type du wrapper correspondant. L'unboxing est l'opération inverse. Cette nouvelle fonctionnalité est spécifiée dans la JSR 201 et intégrée dans Java 1.5.

Par exemple, jusqu'à la version 1.4 de Java pour ajouter des entiers dans une collection, il était nécessaire d'encapsuler chaque valeur dans un objet de type Integer.

Exemple :
import java.util.*;

public class TestAvantAutoboxing {

  public static void main(String[] args) {

    List liste = new ArrayList();
    Integer valeur = null;
    for(int i = 0; i < 10; i++) {
      valeur = new Integer(i);
      liste.add(valeur);
    }
  }
}

Avec Java 1.5, l'encapsulation de la valeur dans un objet n'est plus obligatoire car elle sera réalisée automatiquement par le compilateur.

Exemple (java 1.5) :
import java.util.*;

public class TestAutoboxing {

  public static void main(String[] args) {
    List liste = new ArrayList();
    for(int i = 0; i < 10; i++) {
      liste.add(i);
    }    
  }
}

 


2. Les notions et techniques de base en Java 4. La programmation orientée objet Imprimer Index Index avec sommaire Télécharger le PDF    
Développons en Java
v 2.30   Copyright (C) 1999-2022 .