Pour manipuler des valeurs on ne peut se contenter d’utiliser uniquement des littéraux. Il faut trouver un moyen de stocker ces valeurs, de les modifier, de les consulter, etc. Bref d’utiliser des variables: on donne dans notre code un nom à un emplacement qui va nous servir à manipuler des valeurs.
En Java
la création d’une variable se fait pas l’association d’un nom et d’un type
(on ne peut pas avoir de variable sans décrire précisément ce que l’on veut stocker!).
Exemple: déclaration de deux variables entières en jshell
(ici en mode edit
)
int ix ; ix=10 ; int iy=33 ;
L’exécution affiche:
ix ==> 0 ix ==> 10 iy ==> 33
Remarques sur la syntaxe:
Type NomVariable;
(la variable sera utilisée ultérieurement)
Type NomVariable = valeur;
=
(qu’il ne faudra pas confondre
avec l'égalité que nous verrons un peu plus loin!).
Ici on a un véritable contrat qui lie le nom ix
à son utilisation liée au type java int
.
Cette association entre un nom de variable et un type s’appelle un symbole
(plaisanterie: pensez-y, le contraire d’un symbole est "diabolique"!) . »
![]() | Attention |
---|---|
Quant on a simplement déclaré la variable en écrivant C’est une spécificité de |
Un exemple de contrôle lié au contrat de type:
ix=33.33 | Error: | incompatible types: possible lossy conversion from double to int | ix=33.33 | ^---^
Le contrat lié à la variable ix
est d'être compatible avec un entier int
: on ne peut donc y affecter un double
.
Il y a toutefois quelques arrangements possibles:
jshell> double dx = 33 dx ==> 33.0
Ici on a pu affecter un int
à un double. Il y a compatibilité ascendante et donc "promotion automatique".
Quelques manipulations de variables:
int iz= 99 ; double dy = .33 ; double dz = dy + 1. ; dz = dy * iz ; // opération: double * int -> double dz = dz + 1 ; int iv = dz /2 ; //ça ne peut pas marcher!
L’exécution donne:
iz ==> 99 dy ==> 0.33 dz ==> 1.33 dz ==> 32.67 dz ==> 33.67 | Error: | incompatible types: possible lossy conversion from double to int | int iv= dz /2 ; | ^---^
On remarquera ici:
double
et int
rend un double
(dans une opération le résultat est du type le plus "fort").
dz/2
étant alors un double
on ne peut pas l’affecter à un int
Par contre ceci est possible:
jshell> int iw = (int) (dz/2) iw ==> 16
On a mis ici en place une conversion explicite (avec une perte de précision).
![]() | Attention |
---|---|
Ce dispositif syntaxique La conversion comme ici et le forçage de type que nous verrons plus tard: ces deux opérations sont très (très!) différentes et il ne faut pas les confondre! (le terme anglais cast est malheureusement utilisé pour les deux et quand vous verrez dans un forum francophone l’horrible franglisme "caster" méfiez vous!) |
Il nous faut maintenant faire le point sur les différents types numériques scalaires et leurs compatibilités.
type | taille | plage de valeurs |
---|---|---|
byte
| 1 octet | de -27 à 27 -1 |
short
| 2 octets | de -215 à 215-1 |
int
| 4 octets | de -231 à 231-1 |
long
| 8 octets | de -263 à 263-1 |
déclarations de valeurs entières.
int val ; byte toutPetit ; val = 666 ; toutPetit = -77 ; int val2 = 96 ; // declare la variable et l'initialise int autreVal = 0xBABE ; // notation hexadécimale long gros = 17273747576L ; // noter "L" à la fin long plutotGros = 0xBABEL ; // même notation "L" // notation binaire int val = 0b00110001; long big = 0B1010000101000101101000010100010110100001010001011010000101000101L; // on peut utiliser un 'b' ou un 'B'
Note: on peut utiliser un l minuscule en fin de littéral long
mais nous ne vous le conseillons pas
(on peut confondre avec un 1).
![]() | |
Les types |
Pour faciliter la lecture il est possible d’insérer des caractères de soulignement à l’intérieur des valeurs littérales:
int montant = 1_250_000 ; long numCarteCredit = 1234_5678_9012_3456L; long octetsEnHex = 0xFF_EC_DE_5E; long octets = 0b11010010_01101001_10010100_10010010;
La règle peut s’exprimer de manière simpliste: on peut mettre un "petit" dans un plus "grand" (promotion implicite) mais pas un "grand" dans un plus petit (sauf conversion explicite).
byte petit= -77 ; int plusGrand = petit ; //ça marche petit = 777 ; // ça marche pas
Résultat:
petit ==> -77 plusGrand ==> -77 | Error: | incompatible types: possible lossy conversion from int to byte | petit = 777 ; // ça marche pas | ^-^
Par contre :
petit = (byte) 777 ; // conversion avec perte de données petit ==> 9
Les représentations binaires donnent lieu à des extensions particulières :
byte unOctet = -1 ; // 8 bits à 1 int surQuatreOctets = unOctet; // 32 bits à 1
Pour les opérations qui mélangeraient différents types entiers:
long
parmi les opérandes alors le résultat est un long
int
(même si on opère, par exemple, entre deux byte
)
Donc :
jshell> byte b1 = 1 ; b1 ==> 1 jshell> byte b2 = 2 ; b2 ==> 2 jshell> byte faute = b1 + b2 | Error: | incompatible types: possible lossy conversion from int to byte | byte faute = b1 + b2; | ^-----^
Représentation binaire portable (basée approximativement sur la norme IEEE754).
type | taille |
---|---|
float
| 4 octets |
double
| 8 octets |
![]() | |
Java tient compte de valeurs spéciales prévues par la norme comme
|
déclarations de numériques flottants et valeurs littérales.
double val = 3.14 ; double dx = 3. ; double dy = .3 ; double grand = 6.02E23 ; // notation scientifique double toutPetit = 123.4E-128 ; float fval = 2.718F ; // lettre F en fin
![]() | |
Comme pour les entiers: ne pensez pas faire des économies de place en utilisant des |
Pour faciliter la lecture il est possible d’insérer des caractères de soulignement à l’intérieur des valeurs littérales:
jshell> double val = 3_940.14_18 val ==> 3940.1418
--exercice: sur les valeurs "flottantes"-
Les affectations et les résultats de calcul suivent la hiérarchie : int < long < float < double
.
Dans les calculs il vaut mieux rester, autant que possible, dans le périmètre du type initial
(il n’est pas garanti par ex. qu’il y ait un flottant correspondant exactement à un int
donné)
jshell> int iv = 16_777_217 iv ==> 16777217 jshell> float fv = iv fv ==> 1.6777216E7
Comme dans les langages C/C++ l’affectation est une opération qui rend un résultat.
affectations.
//regroupement de déclarations int ia, ib = 20 ; // tous valent 20 int iy ; int ix = 10 ; int iz = ( iy = ix + 10 ) ; // étrange! mais parfois utile!
Il y a également des opérations combinées affectation/opération_arithmétique.
Historiquement il fallait aider le compilateur du langage C
:
s’il avait à compiler : x = x + n
il appliquait à un bas niveau les
mêmes opérations que pour x = y + n
.
Or il disposait déjà de x
… Donc pour indiquer au compilateur
une optimisation on écrivait x+=n
!
Cette syntaxe a été conservé en Java car elle permet des expressions (relativement) élégantes.
On peut donc écrire: -= += *= %=
affectations optimisées.
x = x + 7 ; // peut être écrit d'une manière différente x += 7 ; // opération combinée avec une affectation y *= 4 ; // se font avec tous les opérateurs arithmétiques
Une autre optimisation historique concerne l’incrémentation/décrémentation: plutôt que d'écrire
x += 1
on peut suggérer au compilateur d’appeler directement une instruction de bas niveau
consacrée à l’incrémentation.
On a donc la possibilité d'écrire :
++x
et --x
pré-incrémentation (ou décrémentation): la valeur de la variable est modifiée avant
que le résultat soit utilisé dans le calcul.
x
++ et x--
post-incrémentation (ou décrémentation): la valeur est utilisée dans le calcul puis
le contenu de la variable est modifié (effet de bord dans l’expression).
incrementation/decrementation.
int cpt = 5 ; int y = cpt-- ; // Y vaut 5 et cpt 4 int z = --cpt ; // Z et cpt valent 3 cpt++ ; // incrementation - pourrait être: "++cpt" -