Sintaxa Java se aseamana foarte mult cu cea a limbajelor C si C++. Referatul
de fata isi propune sa prezinte o paralela intre Java si C/C++. Voi descrie
o suma de deosebiri si cateva asemanari dintre ele si voi prezenta elemente
care sunt in Java si nu sunt in C++ si elemente care au ramas in C++, dar au
fost eliminate din Java.
1. Tipuri de date s5k21kk
1.1. Tipurile de date primare
Java suporta 8 tipuri de date primare, descrise in tabelul de mai jos:
Tip Descriere byte intreg cu semn pe 8 biti short intreg cu semn pe 16 biti int intreg cu semn pe 32 de biti long intreg cu semn pe 64 de biti float numar in virgula mobila pe 32 de biti double numar in virgula mobila pe 64 de biti char caracter Unicode pe 16 biti boolean true sau false
Observati ca Java adauga doua tipuri de date: byte si boolean. (unele compilatoare
de C++ mai noi au adaugat si ele tipul boolean).
O diferenta importanta privind celelalte tipuri de date, care sunt comune celor
doua limbaje, este faptul ca tipurile Java au o dimensiune fixa si cunoscuta.
Acest lucru este foarte important pentru Java datorita scopului sau de a fi
portabil. De exemplu daca un tip de data int ocupa 16 biti pe o platforma si
32 de biti pe alta platforma, programul va avea probleme daca va trebui sa ruleze
pe ambele platforme. C++ garanteaza o anumita relatie intre tipurile primare
de date, de exemplu garanteaza ca un tip de data long este cel putin la fel
de mare cu un tip de data int. El nu garanteaza insa dimensiunea fiecarui tip.
Java realizeaza acest lucru, fiecare tip avand o dimensiune fixa.
Deoarece cele mai multe masini ruleaza pe 32 de biti, dimensiunile pentru tipurile
primitive de date au fost gandite sa fie optimizate pentru 32 de biti. Astfel,
o data Java de tip int ocupa 32 de biti ( fata de16 sau 32 de biti, in C/C++
), iar o data de tip long va ocupa 64 de biti ( fata de 32 sau 64 de biti, in
C/C++ ).
O alta diferenta este ca toate tipurile primare Java sunt cu semn. Astfel, declaratiile
unsigned din C nu sunt permise in Java.
1.2. Conversii
Si in Java si in C++ se poate face conversie intre un tip de data si un altul.
Dar in Java nu exista conversii implicite.
Sa luam urmatoarea secventa de program scrisa in C: long LongNb = 32768; int IntNb;
IntNb = LongNb;
Compilatorul C/C++ va face o conversie implicita (cast) din long in int. Pe
o platforma de 16 biti ( unde long are o lungime de 32 de biti si int are o
lungime de 16 biti ), in urma conversiei, variabila IntNb va avea valoarea 0.
Deci va avea loc o pierdere de precizie, fara ca programatorul sa fie avizat.
Java inlatura riscul unor potentiale erori in programare relative la conversii
prin faptul ca nu realizeaza conversii automate. Astfel programatorul este nevoit
sa faca o conversie explicita ( de exemplu IntNb = (int)LongNb;).
1.3. Operatori
Setul de operatori din Java este aproape identic cu cel din C/C++. Acestia sunt:
! (negatie), && (si conditional), || (sau conditional), ?: (conditie).
O diferenta este ca in Java acestia opereaza cu valori booleene. Astfel secventa
C: int x = 4; int y = 5; if (x && y) A
//secventa de cod
S va fi ilegala in Java, pentru ca, asa cum spuneam mai sus, nu se face conversie
automata. Conditia va trebui deci scrisa explicit: if (x!=0 && y!=0).
O alta diferenta privind operatorii, si care are o importanta deosebita, este
ca in Java operatorii nu pot fi supraincarcati, asa cum pot fi in C++. Folosirea
acestei trasaturi in C++ a dus la crearea multor erori. De aceea dezvoltatorii
Java au hotarat sa nu pastreze aceasta caracteristica.
2. Pointeri
Pointerii reprezinta in C++ un element care confera programatorului multa flexibilitate.
Cu toate acestea, folosirea pointerilor este o importanta sursa de erori.
Java nu permite programatorului sa foloseasca pointeri de nici un fel. Cum se
face atunci transmiterea variabilelor?
In C++ programatorul are libertatea sa transmita variabilele cum considera ca
este mai bine folosind operatorii &, * si ->. In Java insa nu exista
acesti operatori, dar exista urmatoarea regula: tipurile de date primare sunt
transmise prin valoare (prin copierea efectiva), iar obiectele si masivele sunt
transmise prin referinta (prin copierea adresei).
Sa luam urmatorul exemplu: vrem sa cream o functie care sa returneze media unui
student: void mediaStudent (int noteai, double media)
A int suma=0; for(int i=0;i<10;i++) suma+=noteaii; media=suma/10;
S
Nu putem returna rezultatul ca parametru (si anume in variabila media), pentru
ca acesta este transmis prin valoare si modificarea sa in interiorul functiei
nu are efect asupra variabilei media. In C acest parametru trebuia transmis
prin adresa (int *media sau int &media). Prin urmare trebuie cautata o solutie
de transmitere a parametrului prin adresa. Acest lucru se poate face prin crearea
unei clase care sa contina variabila media. Un obiect al acestei noi clase va
fi transmis ca parametru prin adresa: public class definesteStudent
A double media;
S iar functia de mai sus va avea forma: void mediaStudent (int noteai, definesteStudent stud)
A int suma=0; for(int i=0;i<10;i++) suma+=noteaii; stud.media=suma/10;
S
Bineinteles ca returnarea mediei se putea face in cazul de mai sus si ca retur
al functiei: double mediaStudent (int noteai) A...S dar in cazul in care se doreste returnarea mai multor valori primare, crearea
unei clase care sa le contina ramane singura solutie valabila.
2.1. Copierea obiectelor
Deoarece fiecare obiect este de fapt o referinta, asignarea unui obiect altuia
nu copiaza decat adresa catre care acesta refera.
Iata un exemplu:
Button butonOK = new Button("OK");
Button butonCancel = new Button("Cancel"); butonOK = butonCancel;
Obiectul butonOK va fi o referinta catre obiectul referit de butonCancel, iar
obiectul initial care era alocat in butonOK se pierde.
Pentru a copia efectiv datele dintr-un obiect in altul se foloseste functia
clone(), disponibila in clasele care implementeaza interfata Cloneable (cele
mai multe dintre clasele standard): butonOK=butonCancel.clone();
Acelasi lucru este valabil si pentru masive. Pentru a copia efectiv valorile
unui vector (de exemplu) in alt vector, fie trebuie copiata fiecare valoare
in parte, fie trebuie folosita metoda System.arraycopy().
2.2. Verificarea egalitatii obiectelor
O alta implicare directa a faptului ca obiectele sunt transmise prin referinta
este faptul ca operatorul == verifica daca doua variabile refera catre acelasi
obiect, si nu daca cele doua variabile contin aceleasi valori. Un numar de clase
defineste metoda equals() in acest scop.
Exemplu:
String sir1="abc";
String sir2="123"; if(sir1.equals(sir2))
A...S
2.3. null
Valoarea implicita pentru variabilele de tip referinta (obiecte si masive) este
null. Acesta este un cuvant rezervat care inseamna ca aceasta variabila nu refera
nici un obiect sau masiv. In Java null este un cuvant rezervat, spre deosebire
de NULL din C, care este doar o constanta predefinita cu 0.
Distinctia dintre transmiterea prin valoare si transmiterea prin referinta in
Java este foarte importanta. Urmatoarele elemente sunt foarte importante pentru
intelegerea acestei diferentieri:
- toate obiectele si masivele (arrays) sunt transmise prin referinta;
- toate tipurile primare de date sunt transmise prin valoare;
- operatorii = si == considera referinte la obiecte. Functiile clone() si equals()
trebuiesc folosite pentru copierea efectiva sau testarea obiectelor respective;
- referirea si dereferirea obiectelor si masivelor sunt realizate automat de
Java;
- o referinta nu poate fi convertita intr-un tip primar de data si nici invers;
- nu exista pointer in Java;
- null este o valoare speciala care indica absenta unei referinte.
Prin eliminarea pointerilor, limbajul Java a fost mult simplificat. Un beneficiu
al eliminarii lor este si asigurarea unei securitati mai bune a programelor
( o caracteristica definitorie a limbajului Java ). Astfel, prin eliminarea
abilitatii programatorului de a folosi adrese direct din memoria sistemului,
limbajul previne intr-o oarecare masura posibilitatea de a putea fi folosit
deliberat in scopuri periculoase de gen virusi, buffer overflow, stack overflow.
3. Gestionarea automata a memoriei
Aceasta este una dintre trasaturile care face limbajul Java renumit pentru usurinta
programarii. Desi operatorul new aloca memorie pentru un obiect, nu exista un
operator corespondent care sa dezaloce memoria alocata anterior prin new. Colectorul
de gunoaie elibereaza un spatiu de memorie imediat ce nu mai exista o referinta
catre acesta.
Exemplu: sa presupunem ca am folosit o culoare pentru desenarea unui background:
Color background = new Color(250,0,0);
In memorie se aloca un spatiu pentru acest obiect, care are adresa background.
Mai tarziu vrem sa schimbam culoarea, si procedam astfel: background = new Color(0,250,0);
Acum un nou obiect este alocat, iar adresa acestuia este trecuta in variabila
background. Prin urmare obiectul alocat anterior nu va mai fi referit, deci
memoria va fi dezalocata automat.
Aceasta facilitate scuteste programatorul sa tina cont de toate obiectele alocate,
sporindu-i rapiditatea programarii si eliminand din erori.
4. Clase
Modelul orientat pe obiecte din Java a fost inspirat din limbajul C++. Dar desi
clasele in C++ sunt importante, in Java sunt obligatorii si sunt "centrul
lucrurilor". In Java nu exista variabile sau functii de sine-statatoare.
Totul trebuie incapsulat intr-una sau mai multe clase. In plus, exista o intreaga
ierarhie de clase, care are ca "stramos" comun clasa Object.
Regasim in clasele Java modificatorii private, protected, si public. Ei au aceeasi
semnificatie ca si in C++. In plus, Java mai are un al patrulea nivel de acces,
care este folosit implicit. Daca nu este specificat nici un modificator, atunci
membrul respectiv este accesibil in cadrul pachetului in care clasa este definita,
dar nu si in alta parte.
Codul Java este vizibil la nivelul pachetului, iar un pachet contine definitiile
si implementarile de cod a uneia sau mai multor clase.
4.1. Functiile Membre
In Java fiecare metoda are corpul in acelasi loc unde are si definitia. De aceea
Java nu are nevoie de cuvintul cheie inline din C++. Toate metodele sunt scrise
ca si functiile inline din C++.
4.2. Valori implicite ale variabilelor
Un alt element care aduce o imbunatatire in Java fata de C++ este abilitatea
de a seta o valoare implicita pentru o variabila membra la momentul declararii
ei. De exemplu: class Persoana
A protected int varsta = 20;
S
In C++, aceasta atribuire se putea face numai in constructor. Posibilitatea
atribuirii unei valori implicite in Java are avantajul ca daca exista mai multi
constructori care trebuie sa aloce aceeasi valoare unei variabile, acestia sunt
simplificati pentru ca nu mai este necesara scrierea lor.
4.3. Constructori si destructori
Fiecare clasa Java poate include unul sau mai multi constructori. Ca si in C++,
constructorul are acelasi nume ca si clasa. In Java constructorii nu returneaza
nici o valoare si sunt declarati in acelasi mod ca si celelalte metode.
In C++ era nevoie de destructori pentru a elibera memoria alocata de un obiect.
Deoarece Java include "colectorul de gunoaie" pentru eliberarea automata
a memoriei care nu mai este referita, existenta destructorilor nu mai este necesara.
De aceea, destructori din C++ nu exista, fiecare clasa Java poate include in
schimb metoda finalize, care realizeaza eliberarea obliectului. Functia este
definita in clasa Object, deci este mostenita de toate clasele.
4.4. Mostenire
Asa cum stiti, mostenirea in Java este indicata prin folosirea cuvantului cheie
extends. Ca si C++, Java include cuvantul this care poate fi folosit de un obiect
pentru a se referi pe sine insusi. In plus, Java include si cuvantul cheie super
pe care un obiect sau o clasa il poate folosi pentru a referi un o metoda din
clasa parinte. De exemplu: class Persoana
A
String nume;
String prenume;
Persoana(String n, String p)
A nume = n; prenume = p;
S
S class Student extends Persoana
A
String facultate;
Persoana(String n, String p, String f)
A super(n,p); facultate = f;
S
S
Despre modalitatea de mostenire in Java am mai discutat si in articolele trecute,
asa ca nu o sa mai insist acum asupra acestui subiect. Pe scurt: o clasa poate
mosteni o singura alta clasa, insa poate implementa mai multe interfete. O interfata
este o clasa care are numai metode abstracte, deci corpul lor trebuie definit
in clasele care o mostenesc.
5. Lipsa preprocesorului
C si C++ includ directivele #define, #include, si #ifdef. Java nu include nici
un fel de preprocesor.
5.1. Definirea constantelor
Orice variabila declarata final este constanta. Valoarea sa trebuie specificata
de la initializare si ea nu poate fi schimbata ulterior. Echivalentul directivei
#define din C este o variabila declarata static final.
De exemplu variabila PI din clasa java.lang.Math este definita astfel: public static final double PI = 3.14159...
5.2. Macrouri
Java nu are un echivalent pentru macrourile din C, dar tehnologia compilatoarelor
a avansat destul de mult incat sa nu mai fie nevoie de ele.
5.3. Includerea fisierelor
Dupa cum stiti, Java are directiva import, care este aproximativ similara cu
directiva #include din C. Directiva import spune compilatorului ca fisierul
curent foloseste clasele specificate sau clasele din pachetele specificate,
si permite programatorului sa foloseasca nume scurte (de exemplu Math.PI in
loc de java.lang.Math.PI).
5.4. Compilarea conditionata
Java nu are directivele #ifdef si #if pentru a realiza compilarea conditionata.
Teoretic, compilarea conditionata nici nu ar trebui sa fie necesara in Java,
pentru ca de obicei aceasta se foloseste la schimbarea platformei. Practic insa,
compilarea conditionata este folositoare si in Java, de exemplu pentru a crea
interfete putin diferite in functie de platforma, sau pentru a include cod pentru
debug.
Compilatorul Java realizeaza implicit o compilare conditionata, in sensul ca
nu va compila un cod care in mod evident nu va fi executat. (de exemplu if(false)).
Compilarea conditionata functioneaza si cu constante (cu variabile declarate
static final). Acestea se folosesc in general pentru debug. Daca o clasa defineste
o astfel de constanta astfel: private static final boolean DEBUG = false; atunci compilatorul nu va compila cod de genul if(DEBUG). Pentru activarea optiunii
de debug, este necesara doar schimbarea valorii constantei si recompilarea codului.
6. Alte diferente
In afara de cele mentionate, mai exista un numar de alte diferente, pe care
le voi enumera pe scurt in continuare:
6.1. Variabile multidimensionale
Ca si C/C++, Java foloseste parantezele patrate pentru a declara un masiv. Sunt
insa doua diferente:
- in Java parantezele pot fi plasate fie inaintea, fie dupa numele variabilei;
- dimensiunea masivului nu trebuie specificata intre paranteze la momentul declararii
variabilei. Acest lucru nu este necesar si nici permis pentru ca Java cere ca
toate masivele sa fie alocate folosind operatorul new: int vectorai; vector = new inta100i; sau int vectorai = new inta100i;
6.2. Comentarii
In afara de comentariile existente si in C/C++, si anume: // si /*...*/, Java
introduce un nou tip de comentariu: /**...*/ Un astfel de comentariu poate fi
extras din codul sursa si folosit pentru a crea documentatie pentru clasa respectiva
cu utilitarul javadoc. Acest mod de comentare a codului este folosit pentru
toate clasele standard din Java.
6.3. Argumente in linia de comanda
Unui program C sau C++ i se pot transmite argumente in linia de comanda cu ajutorul
parametrilor argc si argv, unde argc reprezinta numarul de parametri transmisi,
iar argv este un sir cu parametrii respectivi. Intotdeauna va fi cel putin un
parametru transmis, deoarece primul parametru este numele programului: main ( int argc, char *argvai )
Intr-o aplicatie Java ( intr-un applet nu putem vorbi despre functia main),
argumentele din linia de comanda sunt trecuti intr-un sir de obiecte de tip
String: public static void main(String argsai);
Fiecare componenta a sirului args este un parametru transmis. Diferenta fata
de C/C++ este ca in Java numele programului nu este transmis ca parametru.
6.4. goto, break si continue
Cuvantul cheie goto nu este folosit in Java. El este pe lista cuvintelor rezervate,
asa ca poate la un moment dat o sa se revina asupra lui. Exista insa doi substituenti
pentru goto: break si continue pot fi folosite cu etichete. Break si continue
au si valoarea cunoscuta din C, dar au in plus si facilitatea de "goto": eticheta: for(int i=0;i<3;i++)
A for(int j=0;j<3;j++)
A if(xaii<10) break eticheta; else if(xaii>100) continue eticheta;
S
S
6.5. synchronized
Fiind un sistem multithreading, Java trebuie sa previna ca mai multe fire de
executie sa modifice simultan acelasi obiect. Sectiunile de cod care nu trebuie
executate simultan sunt denumite "sectiuni critice". Java furnizeaza
cuvantul cheie synchronized pentru a proteja aceste sectiuni critice.
6.6. package si import
Java furnizeaza de asemenea cuvantul cheie package pentru a specifica pachetul
din care clasa respectiva face parte. Clauza import are acelasi rol cu #include
din C.
6.7. Elemente care lipsesc in Java
Sunt o suma de alte elemente care exista in C++ si nu exista in Java. Printre
acestea enumar: templates, functii friend, parametri impliciti, struct, union.
In cele mai multe cazuri insa, nici nu este nevoie de ele, sau ele pot fi inlocuite
prin altceva. Eliminarea lor este justificata de faptul ca simplifica mult crearea
programelor Java.
Ca o concluzie, Java a fost conceput sa ajute programatorul cat mai mult prin
usurinta programarii, lasand greul in seama Java. Intr-adevar, fata de C++,
Java este mult mai usor, mai ales ca au fost dezvoltate clase pentru aproape
toate tipurile de aplicatii. Raman insa tipuri de programe care nu pot fi facute
in Java, programarea lor in C/C++ fiind mult mai eficienta. Aceasta datorita
libertatii accesului la resurse, vitezei de executie mult mai mari (stiti ca
Java este jumatate interpretat) etc.
Prin urmare avantaje si dezavantaje raman de ambele parti, numai tipul aplicatiei
si programatorul poate decide ce limbaj sa aleaga