C-Java, Gestionarea automata a memoriei, Clase, Functii membre, Mostenirea in Java, Constante, Fisiere



Java vizavi de C/C++

 

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

1.1. Tipurile de date primare 32966hzq96uej2v

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

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) {
    //secventa de cod
}
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 note[], double media)
{
  int suma=0;
  for(int i=0;i<10;i++)
    suma+=note[i];
  media=suma/10;
}
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
{
  double media;
}
iar functia de mai sus va avea forma:
void mediaStudent (int note[], definesteStudent stud)
{
  int suma=0;
  for(int i=0;i<10;i++)
    suma+=note[i];
  stud.media=suma/10;  
}
Bineinteles ca returnarea mediei se putea face in cazul de mai sus si ca retur al functiei:
double mediaStudent (int note[]) {...}
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))
  {...}

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
{
  protected int varsta = 20;
}
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
{
  String nume;
  String prenume;
  Persoana(String n, String p)
  {
    nume = n;
    prenume = p;
  }
}

class Student extends Persoana
{
  String facultate;
  Persoana(String n, String p, String f)
  {
    super(n,p);
    facultate = f;
  }
}

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 vector[];
vector = new int[100];
sau
int vector[] = new int[100];

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 *argv[] )
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 args[]);
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++)
{
  for(int j=0;j<3;j++)
  {
    if(x[i]<10)
      break eticheta;
    else if(x[i]>100)
      continue eticheta;
  }
}

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