Variables

images/whitebelt.png

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:

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"!) . »

[Avertissement]Attention

Quant on a simplement déclaré la variable en écrivant int ix; jshell lui a attribué une valeur par défaut.

C’est une spécificité de jshell. en java ce ne sera qu’exceptionnellement le cas: vous serez obligé de fixer explicitement une valeur avant de pouvoir utiliser la variable.

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:

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

[Avertissement]Attention

Ce dispositif syntaxique (type) (expression) se retrouve en java avec deux utilisations complètement différentes.

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.

Les types primitifs numériques

Les types entiers

images/whitebelt.png

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

les "littéraux" (valeurs entières)

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

[Note]

Les types byte et short ont une utilisation limitée à des cas particuliers: ne croyez pas faire des économies de place quand vous avez besoin d’une valeur entière. Préférez donc généralement les int et les long.

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;

--exercices--

Compatibilités entre types entiers

images/whitebelt.png

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

images/orangebelt.png

Pour les opérations qui mélangeraient différents types entiers:

  • si il y a un long parmi les opérandes alors le résultat est un long
  • toutes les autres opérations rendent un 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;
|               ^-----^

Numériques en virgule flottante

images/whitebelt.png

Représentation binaire portable (basée approximativement sur la norme IEEE754).

type taille
float 4 octets
double 8 octets
[Note]

Java tient compte de valeurs spéciales prévues par la norme comme Infinity (les infinis), zéro signé ou NaN (Not a Number -non nombre-).

Les valeurs "littérales" des nombres flottants.

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

[Note]

Comme pour les entiers: ne pensez pas faire des économies de place en utilisant des float faites plutôt systématiquement usage des double.

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"-

Compatibilités entre types numériques

images/orangebelt.png

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

--exercice: sur les opérations numériques-

Affectations optimisées

images/whitebelt.png

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" -

--exercice: sur les affectations optimisées-