3. Les expressions et les
opérateurs
3.1 Type de
résultat, transtypage.
Tous
les opérateurs calculent un résultat. Quand les opérandes dans un operateur
sont de
même type, le type du
résultat coïncide avec le type
des opérandes (exception:le type du
résultat intégral moins de 4 bytes - par
exemple byte, short est toujours converti vers type int)
Si les
opérandes n'ont pas le même
type, avant
l'exécution on fait une conversion de type automatique d'un des
opérandes vers
le type de l'autre (si c'est possible). Si cette conversion n'est pas
possible
une faute de compilation est générée. Cette
conversion de type porte le nom transtypage implicite
(ou automatique).
Le transtypage automatique peut s'appliquer quand il n'y a pas de
risque de perte d’information. La perte de précision est
acceptable.
Les six
flèches noires dans la figure 3.1 indiquent les conversions sans
perte
d'information. Les trois flèches pointillées indiquent
celles pouvant souffrir d'une perte de précision
Fig. 3.1. Transtypage
automatique autorisé en Java
public class Oper {
public static void main(String arg[]){
int k,i1=4, i2 = 5;
double f1 =5.0,f2=5.0;
f1= i1/i2 +i1/f2;
}
}
• L'expression i1/i2 se fait sans transtypage
est le résultat est 0
• Dans l'expression i1/f2, i1 est convertis en
double est le résultat est 0.8
• Le résultat final est de type double
En Java, une variable de type caractère peut intervenir dans une
expression arithmétique car il est prévu une promotion
numérique de char en int. En effet, une valeur de type
caractère peut être considérée de deux
façons :
• comme le caractère concerné : a,
Z, fin de ligne...,
• comme le code de ce caractère,
c’est-à-dire une séquence de 16 bits; or à
ce dernier on peut toujours faire correspondre un nombre entier
Dans l'expression 'z' - 'w' (qui vaut 3) pour faire la soustraction,
d'abord les deux caractères sont convertis automatiquement en
int. La conversion de char (16 bits) en int (32 bits) se fait en
complétant le motif binaire initial par 16 bits à
zéro.
Parmi les conversions légales, on trouve celle de char en int,
mais pas celle de char en short. En effet, bien que ces deux types
soient de même taille, le type short est destiné à
des nombres avec signe, tandis que, par convention, la conversion
d’une valeur de type char doit toujours fournir un
résultat positif. Le type short s’avère alors trop
petit pour accueillir toutes les valeurs possibles.
Dans langage Java il y a encore une autre possibilité. Quand le
transtypage automatique n'est pas possible, on peut utiliser
transtypage explicite (caste). Ente parenthèses est introduit le
type dans lequel il faut faire la conversion.
int i = (int)56.4;
byte sh = (byte)456;
On force alors le type, indiquant ainsi au compilateur qu’on est
conscient du problème de risque de perte d’information.
C’est aussi le cas lorsqu’on veut prendre la partie
entière d’un réel.
public class Oper {
public static void main(String arg[]){
int in;
long ln=123L;
double db = 34.5;
in = ln;
//type mismatch
in = db;
//type mismatch
in = (int)ln;
//ok
in = (int)db;
//ok
}
}
Dans les expressions arithmétiques, les entiers de type byte ou
short sont considérés comme des int. Si b1, b2 et b3 sont
de type byte, b2 + b3 est considéré être un int.
L’affectation b1 = b2 + b3; et b1 = 130; sont
illégales alors que b1 = 5 est acceptée.
public class Oper {
public static void main(String arg[]){
byte b1,b2=2,b3=3;
b1 = 5;
//ok!
b1 = b2+b3;
// type mismatch
b1 =
(byte)(b2+b3); //ok!
b1=130;
//type mismatch
b1=(byte)130;
//ok!
}
}
3.2 La
priorité et l'associativité des opérateurs
Lorsque plusieurs opérateurs apparaissent dans une même
expression, il est nécessaire de savoir dans quel ordre ils
seront utilisés. Cet ordre est déterminé par la
priorité relative associée à chaque
opérateur. En cas de priorités identiques, les calculs
dans la pluparts des operateurs s’effectuent de gauche à
droite. On dit que l’on a affaire à une
associativité de gauche à droite. Il y a quelques
operateurs qui ont l'associativité de droite à gauche.
Le tableau suivant fournit la liste complète des
opérateurs de Java, classés par ordre de priorité
décroissante et accompagnés de leur mode
d’associativité (––> signifiant de gauche
à droite et <–– de droite à gauche).
Opérateurs
|
Associativité
|
() [] .
++(postfixé) --(postfixé)
|
––>
|
+(unaire) -(unaire) ++(préfixé) --(préfixé)
~ ! cast
new
|
<––
|
* / %
|
––>
|
+ -
|
––>
|
<< >> >>>
|
––>
|
< <= >
>=
instanceof
|
––>
|
== !=
|
––>
|
&
|
––>
|
^
|
––>
|
|
|
––>
|
&&
|
––>
|
||
|
––>
|
?:
|
––>
|
= += -= *= /= %= <<= >>= >>>= &= |= ^=
|
<––
|
Tab. 3.1. Les opérateurs de Java et leurs priorités
Notez qu’en Java, un certain nombre de notations se trouvent
considérées comme des opérateurs et, en tant que
tels, soumis à des règles de priorités:
– des références à des
éléments d’un tableau : opérateur [],
– des références à un champ ou à une
méthode d’un objet : opérateur .,
– des appels de méthodes : opérateur ().
3.3 Les
opérateurs arithmétiques
• + addition;
• - soustraction,
• * multiplication,
• / division.
De plus, il existe un opérateur de modulo noté % qui peut
porter sur des entiers ou sur des flottants. Avec des entiers, il
fournit le reste de la division entière de son premier
opérande par son second. Avec des flottants il fonction de la
même manière mais les résultats sont approximatifs.
public class Oper {
public static void main(String arg[]){
System.out.println("11%4:\t"+11%4);
System.out.println("23%6:\t"+23%6);
System.out.println("-11%3:\t "+
-11%3);
System.out.println("12.5%3.5:\t
"+12.5%3.5);
System.out.println("-15.2%7.5:\t
"+-15.2%7.5);
}
}
• Dans le cas des entiers, comme a
été déjà signalé, le
dépassement de capacité n’est jamais
détecté. On se contente de conserver les bits les moins
significatifs du résultat.
• Dans le cas des entiers, la division par
zéro (par / ou par %) conduit à une erreur
d’exécution.
• En Java, aucune opération sur les
flottants ne conduit à un arrêt de
l’exécution (pas même une division par zéro
!). En effet, les flottants sont codés en respectant les
conventions IEEE 754. Celles-ci imposent qu’il existe une valeur
particulière pour représenter l’infini positif,
l’infini négatif et une valeur non calculable (NaN- not a number).
public class Oper {
public static void main(String arg[]){
int i= 20000000;
System.out.println
("overloading: "+i*i); //dépassement de capacité
float x = 1e30f ;
float y ;
y = x*x ;
System.out.println (x + " a
pour carre : " + y) ;
float zero = 0.f ; //
division flottante par zero
y=4.5f;
float z = y/zero ;
System.out.println (y + "
divise par 0 = " + z) ;
System.out.println("l'oppose
de " + y + " = "+ -y);
float z1 = z/y ;
System.out.println (z + "/"
+ y + " = " + z1) ;
System.out.println
("0./0.:"+zero/zero);
}
}
opérateurs
d’incrémentation (++ et --)
Les opérateurs d’incrémentation (++ et --) de
variables (entières ou réelles) sont des operateurs
à un seul opérande. Les deux opérateurs sont les
seules en Java (avec l'affectation) qui ont un effet secondaire.
• Ils modifient la valeur de l'opérande
en ajoutant (dans le cas de ++) ou retranchant (dans le cas de --)
valeur 1. L'opérande doit être une variable pas une valeur
constante. Si non le compilateur affiche une erreur.
• Ils calculent un résultat. La valeur de
résultat est la valeur ancienne de l'opérande (avant
l’incrémentation) dans le cas où
l’opérateur suit la variable (j++) ; elle est la nouvelle
valeur (après l’incrémentation) dans le cas
contraire (++j).
public class Oper {
public static void main(String arg[]){
int k, m=3;
k = m++; //m
vaut 4, k vaut 3 - valeur ancienne
m=3;
k = ++m; //m
vaut 4, k vaut 4 - valeur novelle
m=3;
k = m--; //m
vaut 2, k vaut 3 - valeur ancienne
m=3;
k = --m; //m
vaut 2, k vaut 2 - valeur novelle
k= (m++)++;
//erreur! m++ n'est pas constante
}
}
Les priorités élevées de ces opérateurs
unaires permettent d’écrire des expressions assez
compliquées sans qu’il soit nécessaire
d’employer des parenthèses pour isoler leur
opérande.
Utilisation des opérateurs d’incrémentation
évite les conversions. Si bt est du type byte pour
incrémenter sa valeur on a le choix entre bt++; et
bt = (byte)(bt+1); Le dernier fait deux conversions
supplémentaires.
L’opérateur
d’affectation (=)
L’opérateur d’affectation (=) est un
opérateur binaire qui a aussi un effet secondaire.
• Il réalise une action – affecte
la valeur d'opérande à droite à l'opérande
à gauche. L'opérande à gauche doit être une
variable pas une valeur constante. Si non le compilateur affiche une
erreur.
• Il calcule un résultat qui peut
être utilise dans un calcul suivant. La valeur de résultat
est la valeur affectée.
Cet opérateur d’affectation peut faire intervenir
d’autres expressions, comme dans c=b+3; La faible priorité
de cet opérateur (elle est inférieure à celle de
tous les opérateurs arithmétiques et de comparaison) fait
qu’il y a d’abord évaluation de l’expression b
+ 3. La valeur ainsi obtenue est ensuite affectée à c.
• L'opérateur d’affectation
possède une associativité de droite à gauche.
C’est ce qui permet à une expression telle que : i = j =
5; d’évaluer d’abord l’expression j = 5 avant
d’en affecter la valeur (5) à la variable j. Bien entendu,
la valeur finale de cette expression est celle de i après
affectation, c’est-à-dire 5.
public class Oper {
public static void main(String arg[]){
int k, m=3,z=4,p;
p = k =(m = 2) *(z =
4);
//m vaut 2
//z vaut 4
//k vaut 8
//p vaut 8
p = k = 2;
//k vaut 2, p vaut 2
p = (k = 2);
//la
même chose
(p = k) = 2;
//erreur
– l'opérande de gauche (p=k) est constante
}
}
L'opérateur "=" couplé avec un opérateur
arithmétique permet de retourner dans l'opérande de
gauche l'opération arithmétique de celui-ci avec
l'opérande de droite. Ainsi c+=3; équivaut à c=c+3;
3.4 Les
opérateurs de comparaisons logique
• == Rend true si les deux
opérandes sont égaux.
• != Rend true si les deux
opérandes sont différents.
• < Rend true si
l'opérande de gauche est strictement inférieur à
l'opérande de droite.
• <= Rend true si
l'opérande de gauche est inférieur ou égal
à l'opérande de droite.
• > Rend true si
l'opérande de gauche est strictement supérieur à
l'opérande de droite.
• >= Rend true si
l'opérande de gauche est supérieur ou égal
à l'opérande de droite.
public class Oper {
public static void main(String arg[]){
System.out.println("25>11:"+(25>11)+ "\t17<=8: "+(17<=8));
}
}
3.5
Opérateurs logiques sur des booléens
• && Opérateur
ET (AND). Le résultat est true, si les deux opérandes ont
la valeur true.
• || Opérateur OU (OR).
Le résultat est true, si l'un des deux opérandes a la
valeur true.
• ! Opérateur NON
(NOT). Le résultat est true si le seul opérande a la
valeur false.
public class Oper {
public static void main(String arg[]){
boolean
b1=true,b2=true,b3=false,b4=false;
System.out.println("true
&& true: "+(b1&&b2)+ "\t true && false:
"+(b1&&b3));
System.out.println("true ||
false: "+(b1||b3)+ "\t false || false: "+(b3&&b4));
System.out.println("!true:
"+!b1+"\t !false: "+!b3);
}
}
3.6
Opérateurs logiques bit à bit
• & Opérateur ET
(AND).
• | Opérateur OU (OR).
• ~ Opérateur NON (NOT).
• >> Décalage
arithmétique à droite.
• >>> Décalage
logique à droite. Le signe de l'opérande de gauche n'est
pas gardé. On introduit toujours un zéro à gauche.
• << Décalage
à gauche. C'est une multiplication par 2
public class Oper {
public static void main(String arg[]){
System.out.println("7&8:
"+(7&8)+"\t7|8: "+(7|8));
System.out.println("~7: "+ (~7));
System.out.println("7 << 2:
"+(7 << 2));
System.out.println("-7 >>
2: "+(-7 >> 2)+"\t -7 >>> 2: "+(-7 >>>
2));
}
}
3.7
Opérateur ternaire
public class Oper {
public static void main(String arg[]){
int res,a=4, b=9;
res = a>b?a:b;
}
}