p3f20fq
5.2.1 Valoarea si tipul unei expresii
5.2.2 Ordinea de evaluare
5.2.3 Operatori
5.2.3.1 Operatori unari
5.2.3.1.1 Operatorii de preincrementare si postincrementare
5.2.3.1.2 Operatorul + unar
5.2.3.1.3 Operatorul - unar
5.2.3.1.4 Operatorul de complementare
5.2.3.1.5 Operatorul de negare logica
5.2.3.1.6 Casturi
5.2.3.2 Operatori binari
5.2.3.2.1 Operatori multiplicativi: *, /, %
5.2.3.2.2 Operatori aditivi: +, -, concatenare pentru siruri de caractere
5.2.3.2.3 Operatori de siftare: >>, <<, >>>
5.2.3.2.4 Operatori relationali: <, >, <=, >=, instanceof
5.2.3.2.5 Operatori de egalitate: ==, !=
5.2.3.2.6 Operatori la nivel de bit: &, |, ^
5.2.3.2.7 Operatori logici: &&, ||
5.2.3.3 Operatorul conditional ?:
5.2.3.4 Operatii intregi
5.2.3.5 Operatii flotante
5.2.3.6 Apeluri de metode
5.2.1 Valoarea si tipul unei expresii
Fiecare expresie a limbajului Java are un rezultat si un tip. Rezultatul poate
fi:
o valoare o variabila nimic
In cazul in care valoarea unei expresii are ca rezultat o variabila,
expresia poate apare in stanga unei operatii de atribuire. De exemplu
expresia:
tablouaii este o expresie care are ca rezultat o variabila si anume locatia elementului
cu indexul i din tabloul de elemente numit tablou.
O expresie nu produce nimic doar in cazul in care este un apel
de metoda si acest apel nu produce nici un rezultat (este declarat de tip void).
Fiecare expresie are in timpul compilarii un tip cunoscut. Acest tip
poate fi o valoare primitiva sau o referinta. In cazul expresiilor care
au tip referinta, valoarea expresiei poate sa fie si referinta neinitializata,
null.
5.2.2 Ordinea de evaluare
In Java operanzii sunt evaluati intotdeauna de la stanga spre
dreapta. Acest lucru nu trebuie sa ne indemne sa scriem cod care sa depinda
de ordinea de evaluare pentru ca, in unele situatii, codul rezultat este
greu de citit. Regula este introdusa doar pentru a asigura generarea uniforma
a codului binar, independent de compilatorul folosit.
Evaluarea de la stanga la dreapta implica urmatoarele aspecte:
in cazul unui operator binar, operandul din stanga este intotdeauna
complet evaluat atunci cand se trece la evaluarea operandului din dreapta.
De exemplu, in expresia:
( i++ ) + i daca i avea valoarea initiala 2, toata expresia va avea valoarea 5 pentru ca
valoarea celui de-al doilea i este luata dupa ce s-a executat incrementarea
i++.
in cazul unei referinte de tablou, expresia care numeste tabloul este
complet evaluata inainte de a se trece la evaluarea expresiei care da
indexul. De exemplu, in expresia:
( a = b )aii indexul va fi aplicat dupa ce s-a executat atribuirea valorii referinta b la
variabila referinta de tablou a. Cu alte cuvinte, rezultatul va fi al i-lea
element din tabloul b.
in cazul apelului unei metode, expresia care numeste obiectul este complet
evaluata atunci cand se trece la evaluarea expresiilor care servesc drept
argumente.
in cazul unui apel de metoda, daca exista mai mult decat un parametru,
la evaluarea parametrului numarul i, toti parametrii de la 1 la i-1 sunt deja
evaluati complet.
in cazul unei expresii de alocare, daca avem mai multe dimensiuni exprimate
in paranteze drepte, dimensiunile sunt de asemenea evaluate de la stanga
la dreapta. De exemplu, in expresia: new intai++iaii tabloul rezultat nu va fi o matrice patratica ci, daca i avea valoarea 3, de
dimensiune 3 x 4.
5.2.3 Operatori
5.2.3.1 Operatori unari
Operatorii unari se aplica intotdeauna unui singur operand. Acesti operatori
sunt, in general exprimati inaintea operatorului asupra caruia se
aplica. Exista insa si doua exceptii, operatorii de incrementare si decrementare
care pot apare si inainte si dupa operator, cu semnificatii diferite.
Operatorii unari sunt urmatorii:
++ preincrement
-- predecrement
++ postincrement
-- postdecrement
+ unar
- unar
I complementare
! negatie logica cast
5.2.3.1.1 Operatorii de preincrementare si postincrementare
Operatorii ++ preincrement si postincrement au acelasi rezultat final, si anume
incrementeaza variabila asupra careia actioneaza cu 1. Operandul asupra caruia
sunt apelati trebuie sa fie o variabila de tip aritmetic. Nu are sens sa apelam
un operand de incrementare asupra unei valori (de exemplu valoarea unei expresii
sau un literal), pentru ca aceasta nu are o locatie de memorie fixa in
care sa memoram valoarea dupa incrementare.
In cazul operatorului prefix, valoarea rezultat a acestei expresii este
valoarea variabilei dupa incrementare in timp ce, la operatorul postfix,
valoarea rezultat a expresiei este valoarea de dinainte de incrementare. De
exemplu, dupa executia urmatoarei secvente de instructiuni:
int i = 5; int j = i++; valoarea lui j este 5, in timp ce, dupa executia urmatoarei secvente de
instructiuni:
int i = 5; int j = ++i; valoarea lui j va fi 6. In ambele cazuri, valoarea finala a lui i va fi
6.
In cazul operatorilor -? predecrement si postdecrement, sunt valabile
aceleasi consideratii ca mai sus, cu diferenta ca valoarea variabilei asupra
careia se aplica operandul va fi decrementata cu 1. De exemplu, urmatoarele
instructiuni:
int i = 5; int j = i--; fac ca j sa aiba valoarea finala 5 iar urmatoarele instructiuni:
int i = 5; int j = --i; fac ca j sa aiba valoarea finala 4. In ambele cazuri, valoarea finala
a lui i este 4.
Operatorii de incrementare si de decrementare se pot aplica si pe variabile
de tip flotant. In asemenea cazuri, se converteste valoarea 1 la tipul
variabilei incrementate sau decrementate, dupa care valoarea rezultata este
adunata respectiv scazuta din vechea valoare a variabilei. De exemplu, urmatoarele
instructiuni:
double f = 5.6; double g = ++f; au ca rezultat final valoarea 6.6 pentru f si pentru g.
5.2.3.1.2 Operatorul + unar
Operatorul + unar se aplica asupra oricarei valori primitive aritmetice. Valoarea
ramane neschimbata.
5.2.3.1.3 Operatorul - unar
Operatorul - unar se aplica asupra oricarei valori primitive aritmetice. Rezultatul
aplicarii acestui operand este negarea aritmetica a valorii. In cazul
valorilor intregi, acest lucru este echivalent cu scaderea din 0 a valorii
originale. De exemplu, instructiunile:
int i = 5; int j = -i;
ii dau lui j valoarea -5 . In cazul valorilor speciale definite
de standardul IEEE pentru reprezentarea numerelor flotante, se aplica urmatoarele
reguli:
Daca operandul este NaN rezultatul negarii aritmetice este tot NaN pentru ca
NaN nu are semn.
Daca operandul este unul dintre infiniti, rezultatul este infinitul opus ca
semn.
Daca operandul este zero de un anumit semn, rezultatul este zero de semn diferit.
^
5.2.3.1.4 Operatorul de complementare
Operatorul de complementare I se aplica asupra valorilor primitive de tip intreg.
Rezultatul aplicarii operandului este complementarea bit cu bit a valorii originale.
De exemplu, daca operandul era de tip byte avand valoarea, in binar,
00110001, rezultatul va fi 11001110. In realitate, inainte de complementare
se face si extinderea valorii la un intreg, deci rezultatul va fi de fapt:
11111111 11111111 11111111 11001110.
5.2.3.1.5 Operatorul de negare logica
Operatorul de negare logica ! se aplica in exclusivitate valorilor de
tip boolean. In cazul in care valoarea initiala a operandului este
true rezultatul va fi false si invers.
5.2.3.1.6 Casturi
Casturile sunt expresii de conversie dintr-un tip intr-altul, asa cum
deja am aratat la paragraful destinat conversiilor. Rezultatul unui cast este
valoarea operandului convertita la noul tip de valoare exprimat de cast. De
exemplu, la instructiunile:
double f = 5.6; int i = ( int )f; double g = -5.6; int j = ( int )g; valoarea variabilei f este convertita la o valoare intreaga, anume 5,
si noua valoare este atribuita variabilei i. La fel, j primeste valoarea -5.
Sa mai precizam ca nu toate casturile sunt valide in Java. De exemplu,
nu putem converti o valoare intreaga intr-o valoare de tip referinta.
5.2.3.2 Operatori binari
Operatorii binari au intotdeauna doi operanzi. Operatorii binari sunt
urmatorii:
Operatori multiplicativi: *, /, %
Operatori aditivi: +, -, + (concatenare) pentru siruri de caractere
Operatori de siftare: >>, <<, >>>
Operatori relationali: <, >, <=, >=, instanceof
Operatori de egalitate: ==, !=
Operatori la nivel de bit: &, |, ^
Operatori logici: &&, ||
5.2.3.2.1 Operatori multiplicativi: *, /, %
Operatorii multiplicativi reprezinta respectiv operatiile de inmultire
(*), impartire (/) si restul impartirii (%). Prioritatea acestor
operatii este mai mare relativ la operatiile aditive, deci acesti operatori
se vor executa mai intai. Exemple:
10 * 5 == 50
10.3 * 5.0 == 51.5
10 / 2.5 == 4.0// impartire reala
3 / 2 == 1// impartire intreaga
7 % 2 == 1// restul impartirii intregi
123.5 % 4 == 3.5 // 4 * 30 + 3.5
123.5 % 4.5 == 2.0 // 4.5 * 27 + 2.0
Dupa cum observati, operanzii sunt convertiti mai intai la tipul
cel mai puternic, prin promovare aritmetica, si apoi se executa operatia. Rezultatul
este de acelasi tip cu tipul cel mai puternic.
In cazul operatorului pentru restul impartirii, daca lucram cu
numere flotante, rezultatul se calculeaza in felul urmator: se calculeaza
de cate ori este cuprins cel de-al doilea operand in primul (un
numar intreg de ori) dupa care rezultatul este diferenta care mai ramane,
intotdeauna mai mica strict decat al doilea operand.
5.2.3.2.2 Operatori aditivi: +, -, concatenare pentru siruri de caractere
Operatorii aditivi reprezinta operatiile de adunare (+), scadere (-) si concatenare
(+) de siruri. Observatiile despre conversia tipurilor facute la operatorii
multiplicativi raman valabile. Exemple:
2 + 3 == 5
2.34 + 3 == 5.34
34.5 - 23.1 == 11.4
"Acesta este" + " un sir" == "Acesta este un sir"
"Sirul: " + 1 == "Sirul: 1"
"Sirul: " + 3.4444 == "Sirul: 3.4444"
"Sirul: " + null = "Sirul: null"
"Sirul: " + true = "Sirul: true"
Object obiect = new Object();
"Sirul: " + obiect == "java.lang.Object@1393800"
La concatenarea sirurilor de caractere, lungimea sirului rezultat este suma
lungimii sirurilor care intra in operatie. Caracterele din sirul rezultat
sunt caracterele din primul sir, urmate de cele dintr-al doilea sir in
ordine.
Daca cel de-al doilea operand nu este de tip String ci este de tip referinta,
se va apela metoda sa toString, si apoi se va folosi in operatie rezultatul.
Metoda toString este definita in clasa Object si este mostenita de toate
celelalte clase.
Daca cel de-al doilea operand este un tip primitiv, acesta este convertit la
un sir rezonabil de caractere care sa reprezinte valoarea operandului.
5.2.3.2.3 Operatori de siftare: >>, <<, >>>
Operatorii de siftare se pot aplica doar pe valori primitive intregi.
Ei reprezinta respectiv operatiile de siftare cu semn stanga (<<)
si dreapta (>>) si operatia de siftare fara semn spre dreapta (>>>).
Siftarile cu semn lucreaza la nivel de cifre binare. Cifrele binare din locatia
de memorie implicata sunt mutate cu mai multe pozitii spre stanga sau
spre dreapta. Pozitia binara care reprezinta semnul ramane neschimbata.
Numarul de pozitii cu care se efectueaza mutarea este dat de al doilea operand.
Locatia de memorie in care se executa operatia este locatia in care
este memorat primul operand.
Siftarea cu semn la stanga reprezinta o operatie identica cu inmultirea
cu 2 de n ori, unde n este al doilea operand. Siftarea cu semn la dreapta reprezinta
impartirea intreaga. In acest caz, semnul este copiat in
mod repetat in locurile ramase goale. Iata cateva exemple:
255 << 3 == 2040
// 00000000 11111111 -> 00000111 11111000
255 >> 5 == 7
// 00000000 11111111 -> 00000000 00000111
Siftarea fara semn la dreapta, muta cifrele binare din operand completand
spatiul ramas cu zerouri:
0xffffffff >>> -1 == 0x00000001
0xffffffff >>> -2 == 0x00000003
0xffffffff >>> -3 == 0x00000007
0xffffffff >>> 3 == 0x1fffffff
0xffffffff >>> 5 == 0x07ffffff ^
5.2.3.2.4 Operatori relationali: <, >, <=, >=, instanceof
Operatorii relationali intorc valori booleene de adevarat sau fals. Ei
reprezinta testele de mai mic (<), mai mare (>), mai mic sau egal (<=),
mai mare sau egal (>=) si testul care ne spune daca un anumit obiect este
sau nu instanta a unei anumite clase (instanceof). Iata cateva exemple:
1 < 345 == true
1 <= 0 == false
Object o = new Object();
String s = new String(); o instanceof Obiect == true s instanceof String == true o instanceof String == false s instanceof Object == true
Sa mai observam ca String este derivat din Object.
5.2.3.2.5 Operatori de egalitate: ==, !=
Acesti operatori testeaza egalitatea sau inegalitatea dintre doua valori. Ei
reprezinta testul de egalitate (==) si de inegalitate (!=). Rezultatul aplicarii
acestor operatori este o valoare booleana.
Exemple:
( 1 == 1.0 ) == true
( 2 != 2 ) == false
Object o = new Object();
String s1 = "vasile";
String s2 = s1;
String s4 = "e";
String s3 = "vasil" + s4;
( o == s1 ) == false
( s1 == s2 ) == true // acelasi obiect referit
( s3 == s1 ) == false // acelasi sir de caractere
// dar obiecte diferite
Sa observam ca egalitatea a doua obiecte de tip String reprezinta egalitatea
a doua referinte de obiecte si nu egalitatea continutului sirului de caractere.
Doua referinte sunt egale daca refera exact acelasi obiect, nu daca obiectele
pe care le refera sunt egale intre ele. Egalitatea continutului a doua
siruri de caractere se testeaza folosind metoda equals, definita in clasa
String.
5.2.3.2.6 Operatori la nivel de bit: &, |, ^
Operatorii la nivel de bit reprezinta operatiile logice obisnuite, daca consideram
ca 1 ar reprezenta adevarul si 0 falsul. Operatorii la nivel de bit, considera
cei doi operanzi ca pe doua siruri de cifre binare si fac operatiile pentru
fiecare dintre perechile de cifre binare corespunzatoare in parte. Rezultatul
este un nou sir de cifre binare. De exemplu, operatia de si (&) logic are
urmatorul tabel de adevar:
1 & 1 == 1
1 & 0 == 0
0 & 1 == 0
0 & 0 == 0
Daca apelam operatorul & pe numerele reprezentate binar:
00101111
01110110 rezultatul este:
00100110
Primul numar reprezinta cifra 47, al doilea 118 iar rezultatul 38, deci:
47 & 118 == 38
In mod asemanator, tabela de adevar pentru operatia logica sau (|) este:
1 | 1 == 1
1 | 0 == 1
0 | 1 == 1
0 | 0 == 0 iar tabela de adevar pentru operatia logica de sau exclusiv (^) este:
1 ^ 1 == 0
1 ^ 0 == 1
0 ^ 1 == 1
0 ^ 0 == 0
Iata si alte exemple:
1245 ^ 2345 == 3572
128 & 255 == 128
127 & 6 == 6
128 | 255 == 255
127 | 6 == 127
32 ^ 64 == 96 ^
5.2.3.2.7 Operatori logici: &&, ||
Operatorii logici se pot aplica doar asupra unor operanzi de tip boolean. Rezultatul
aplicarii lor este tot boolean si reprezinta operatia logica de si (&&)
sau operatia logica de sau (||) intre cele doua valori booleene. Iata
toate posibilitatile de combinare:
true && true == true true && false == false false && true == false false && false == false true || true == true true || false == true false || true == true false || false == false
In cazul operatorului && este evaluat mai intai operandul
din stanga. Daca acesta este fals, operandul din dreapta nu mai este evaluat,
pentru ca oricum rezultatul este fals. Acest lucru ne permite sa testam conditiile
absolut necesare pentru corectitudinea unor operatii si sa nu executam operatia
decat daca aceste conditii sunt indeplinite.
De exemplu, daca avem o referinta si dorim sa citim o valoare de variabila
din obiectul referit, trebuie sa ne asiguram ca referinta este diferita de null.
In acest caz, putem scrie:
String s = "sir de caractere"; if( s != null && s.length < 5 ) ?
In cazul in care s este null, a doua operatie nu are sens si nici
nu va fi executata.
In mod similar, la operatorul ||, se evalueaza mai intai
primul operand. Daca acesta este adevarat, nu se mai evalueaza si cel de-al
doilea operand pentru ca rezultatul este oricum adevarat. Faptul se poate folosi
in mod similar ca mai sus:
if( s == null || s.length == 0 ) ?
In cazul in care s este null, nu se merge mai departe cu evaluarea.
5.2.3.3 Operatorul conditional ?:
Este singurul operator definit de limbajul Java care accepta trei operanzi.
Operatorul primeste o expresie conditionala booleana pe care o evalueaza si
alte doua expresii care vor fi rezultatul aplicarii operandului. Care dintre
cele doua expresii este rezultatul adevarat depinde de valoarea rezultat a expresiei
booleene. Forma generala a operatorului este:
ExpresieConditionala ? Expresie1 : Expresie2
Daca valoarea expresiei conditionale este true, valoarea operatiei este valoarea
expresiei 1. Altfel, valoarea operatiei este valoarea expresiei 2.
Cele doua expresii trebuie sa fie amandoua aritmetice sau amandoua
booleene sau amandoua de tip referinta.
Iata si un exemplu:
int i = 5; int j = 4; double f = ( i < j ) ? 100.5 : 100.4;
Parantezele nu sunt obligatorii.
Dupa executia instructiunilor de mai sus, valoarea lui f este 100.4. Iar dupa:
int aai = A 1, 2, 3, 4, 5 S; int bai = A 10, 20, 30 S; int k = ( ( a.length < b.length ) ? a : b )a0i; valoarea lui k va deveni 10.
5.2.3.4 Operatii intregi
Daca amandoi operanzii unei operator sunt intregi atunci intreaga
operatie este intreaga.
Daca unul dintre operanzi este intreg lung, operatia se va face cu precizia
de 64 de biti. Operanzii sunt eventual convertiti. Rezultatul este un intreg
lung sau o valoare booleana daca operatorul este un operator conditional.
Daca nici unul dintre operanzi nu e intreg lung, operatia se face intotdeauna
pe 32 de biti, chiar daca cei doi operanzi sunt intregi scurti sau octeti.
Rezultatul este intreg sau boolean.
Daca valoarea obtinuta este mai mare sau mai mica decat se poate reprezenta
pe tipul rezultat, nu este semnalata nici o eroare de executie, dar rezultatul
este trunchiat.
5.2.3.5 Operatii flotante
Daca un operand al unei operatii este flotant, atunci intreaga operatie
este flotanta. Daca unul dintre operanzi este flotant dublu, operatia este pe
flotanti dubli. Rezultatul este un flotant dublu sau boolean.
In caz de operatii eronate nu se genereaza erori. In loc de aceasta
se obtine rezultatul NaN. Daca intr-o operatie participa un NaN rezultatul
este de obicei NaN.
In cazul testelor de egalitate, expresia
NaN == NaN are intotdeauna rezultatul fals pentru ca un NaN nu este egal cu nimic.
La fel, expresia:
NaN != NaN este intotdeauna adevarata.
In plus, intotdeauna expresia:
-0.0 == +0.0 este adevarata, unde +0.0 este zeroul pozitiv iar -0.0 este zeroul negativ.
Cele doua valori sunt definite in standardul IEEE 754.
In alte operatii insa, zero pozitiv difera de zero negativ. De
exemplu 1.0 / 0.0 este infinit pozitiv iar 1.0 / -0.0 este infinit negativ.
5.2.3.6 Apeluri de metode
La apelul unei metode, valorile intregi nu sunt automat extinse la intreg
sau la intreg lung ca la operatii. Asta inseamna ca, daca un operand
este transmis ca intreg scurt de exemplu, el ramane intreg
scurt si in interiorul metodei apelate.