This shows you the differences between two versions of the page.
poo-ca-cd:laboratoare:design-avansat-de-clase [2025/10/13 21:05] florian_luis.micu [Membrii statici] |
poo-ca-cd:laboratoare:design-avansat-de-clase [2025/10/13 22:40] (current) florian_luis.micu [Laboratorul 2: Obiecte în Java] |
||
---|---|---|---|
Line 3: | Line 3: | ||
* Autori: [[miculuis1@gmail.com | Florian-Luis Micu ]], [[sorinabuf@gmail.com | Sorina-Anamaria Buf ]], [[stefancocioran@gmail.com | Ștefan Cocioran ]] | * Autori: [[miculuis1@gmail.com | Florian-Luis Micu ]], [[sorinabuf@gmail.com | Sorina-Anamaria Buf ]], [[stefancocioran@gmail.com | Ștefan Cocioran ]] | ||
* Data publicării: 13.10.2025 | * Data publicării: 13.10.2025 | ||
- | * Data ultimei modificări: 13.10.2025 | + | * Data ultimei modificări: 14.10.2025 |
+ | * reordonarea secțiunilor, mutarea anumitor concepte în [Nice to know] și refrazări. | ||
===== Obiective ===== | ===== Obiective ===== | ||
Line 25: | Line 26: | ||
* Destructori. | * Destructori. | ||
* Scenariid de utilizare pentru static. | * Scenariid de utilizare pentru static. | ||
+ | * Utilizarea corectă a array-urilor care conțin obiecte. | ||
Line 81: | Line 83: | ||
<note important> | <note important> | ||
- | Vom explica în acest laborator și în laboratoarele următoare mai multe proprietăți ale tipului ''String''. | + | * Orice text inserat între **ghilimele** este considerat un **String literal**. |
+ | * Vom explica în acest laborator și în laboratoarele următoare mai multe proprietăți ale tipului ''String''. | ||
</note> | </note> | ||
=====🧱 Clase și obiecte===== | =====🧱 Clase și obiecte===== | ||
Line 140: | Line 143: | ||
===Instanțierea unei clase=== | ===Instanțierea unei clase=== | ||
- | Prin instanțierea unei clase se înțelege crearea unui obiect care corespunde unui șablon definit. | + | Pentru a lucra cu obiectele din heap, avem nevoie de adresa la care sunt stocate. |
+ | În limbaje precum C, această adresă este memorată într-un **pointer**, oferind control direct asupra memoriei. | ||
+ | |||
+ | Java **nu permite pointeri direcți**, pentru a evita erorile de acces și scurgerile de memorie. | ||
+ | |||
+ | În schimb, Java folosește **referințe**, care funcționează asemănător, dar sunt gestionate automat de JVM prin **Garbage Collector** ceea ce înseamnă că programatorul **nu** se mai ocupă de gestionarea memoriei manual. | ||
+ | |||
+ | <code java> | ||
+ | Date date; // referință de tip "Date" | ||
+ | </code> | ||
- | Procesul de inițializare implică: declarare, instanțiere și atribuire. | + | Prin instanțierea unei clase se înțelege crearea unui obiect pe baza unei clase, adică alocarea unei zone de memorie pentru o instanță a clasei. |
- | După ce am definit clasa ''Apple'', putem crea obiecte ''Apple'', folosind următoarea regulă: | + | Procesul de inițializare implică: declarare, instanțiere și atribuire, conform următoarei sintaxe: |
''Class'' ''numeVar'' = ''new'' ''Class()''. | ''Class'' ''numeVar'' = ''new'' ''Class()''. | ||
Line 150: | Line 162: | ||
Membrul stâng până la egal conține **referința** de tip ''Class'', iar membrul drept conține **instanța** de tip ''Class''. | Membrul stâng până la egal conține **referința** de tip ''Class'', iar membrul drept conține **instanța** de tip ''Class''. | ||
- | Keyword-ul ''new'' este folosit pentru **crearea instanței** sau **crearea obiectului**, conform următorului exemplu: | + | Keyword-ul ''new'' este folosit pentru **alocarea memoriei** (instanțierea clasei), conform următorului exemplu: |
<code java> | <code java> | ||
Line 158: | Line 170: | ||
Apple a2 = new Apple(); // declarare și instanțiere pe aceeași linie | Apple a2 = new Apple(); // declarare și instanțiere pe aceeași linie | ||
</code> | </code> | ||
+ | |||
+ | <note warning> | ||
+ | Dacă referința nu a fost încă asociată unui obiect, valoarea sa este ''null'', iar accesarea membrilor va produce o eroare de tip ''NullPointerException''. | ||
+ | </note> | ||
<note important> | <note important> | ||
Line 163: | Line 179: | ||
* Rețineți că un **obiect** este de fapt o **clasă instanțiată**. | * Rețineți că un **obiect** este de fapt o **clasă instanțiată**. | ||
* Vom explica în secțiunile următoare de ce este nevoie să punem paranteze în membrul drept după numele clasei. | * Vom explica în secțiunile următoare de ce este nevoie să punem paranteze în membrul drept după numele clasei. | ||
- | </note> | ||
- | |||
- | <note warning> | ||
- | Un obiect care nu este instanțiat are valoarea ''null'', deoarece nu a fost alocată memorie pentru acesta. Deci considerăm că un obiect poate să fie **inițializat** sau ''null'': | ||
- | |||
- | <code java> | ||
- | Apple a; | ||
- | System.out.println(a); // se va afișa "null" | ||
- | </code> | ||
</note> | </note> | ||
Line 185: | Line 192: | ||
</code> | </code> | ||
| | ||
- | Aceasta este varianta **preferată** pentru instanțierea string-urilor. De remarcat că și varianta următoare este corectă, dar **ineficientă**, din motive ce vor fi explicate în următoarele laboratoare. | + | Aceasta este varianta **preferată** pentru instanțierea String-urilor. De remarcat că și varianta următoare este corectă, dar **ineficientă**, din motive ce vor fi explicate în următoarele laboratoare. |
<code java> | <code java> | ||
Line 193: | Line 200: | ||
====Accesarea variabilelor și metodelor unui obiect==== | ====Accesarea variabilelor și metodelor unui obiect==== | ||
- | Odată creat obiectul, putem accesa variabilele și metodele sale folosind **dot notation** ''.'' după numele variabilei: | + | Odată creat obiectul, putem accesa membrii săi (variabile și metodele) folosind **dot notation** (''.'') după numele variabilei: |
<code java PrintAppleDetails.java> | <code java PrintAppleDetails.java> | ||
Line 227: | Line 234: | ||
<note important> | <note important> | ||
- | * În Java, câmpurile **neinițializate** primesc valoarea implicită 0 sau echivalentul acestei valori pentru tipul de date respectiv (ex. ''bool'' -> ''false'', ''float'' -> ''0.f''). Din acest motiv, variabila membru ''mass'' are valoarea ''0''. | + | În Java, câmpurile **neinițializate** primesc valoarea implicită 0 sau echivalentul acestei valori pentru tipul de date respectiv (ex. ''bool'' -> ''false'', ''float'' -> ''0.f'', ''Apple'' -> ''null''). Din acest motiv, variabila membru ''mass'' are valoarea ''0''. Țineți cont că acestă inițializare default se întâmplă **doar pentru câmpuri** și **nu pentru variabilele locale** dintr-o metodă. |
- | * Metoda "main" poate fi introdusă în **orice** clasă, puteți avea chiar mai multe metode "main" în mai multe clase, dar doar o **singură** metodă "main" poate fi pornită la un anumit moment de timp. | + | </note> |
+ | |||
+ | <note tip> | ||
+ | Metoda "main" poate fi introdusă în **orice** clasă, puteți avea chiar mai multe metode "main" în mai multe clase, dar doar o **singură** metodă "main" poate fi pornită la un anumit moment de timp. | ||
</note> | </note> | ||
Line 247: | Line 257: | ||
===Variabile locale=== | ===Variabile locale=== | ||
- | O variabilă locală este: | + | O variabilă locală: |
- | * creată la apelul metodei; | + | * este creată la apelul metodei; |
- | * ștearsă automat când metoda se termină; | + | * este ștearsă automat când metoda se termină; |
* trebuie inițializată înainte de a fi folosită. | * trebuie inițializată înainte de a fi folosită. | ||
Line 261: | Line 271: | ||
<note important> | <note important> | ||
- | Spre deosebire de variabilele instanței, variabilele locale **nu primesc valori implicite**. Dacă se încearcă folosirea unei variabile locale fără inițializare, se va afișa o **eroare de compilare**. | + | Spre deosebire de variabilele instanței (câmpuri), variabilele locale **nu primesc valori implicite**. Dacă se încearcă folosirea unei variabile locale fără inițializare, se va afișa o **eroare de compilare**. |
</note> | </note> | ||
===Inițializarea variabilelor locale=== | ===Inițializarea variabilelor locale=== | ||
- | Java **nu permite** folosirea unei variabile locale fără inițializare sigură. | + | Java **nu permite** folosirea unei variabile locale fără **inițializare sigură**. |
Un exemplu de cod greșit este următorul: | Un exemplu de cod greșit este următorul: | ||
Line 286: | Line 296: | ||
<code java> | <code java> | ||
class Apple { | class Apple { | ||
- | int x, y; | + | int x = 2; |
+ | int y = 3; | ||
+ | // Presupunem că argumentele pasate sunt: x = 10, y = 11 | ||
void moveTo(int x, int y) { | void moveTo(int x, int y) { | ||
- | System.out.println("Moving apple to " + x + ", " + y); | + | System.out.println("Moving apple to (" + x + ", " + y + ")"); |
} | } | ||
} | } | ||
</code> | </code> | ||
- | În acest exemplu, ''x'' și ''y'' din metodă sunt **parametrii**, nu variabilele ale instanței. | + | <spoiler Output> |
- | Pentru a accesa variabilele care aparțin instaței, trebuie să folosim keyword-ul ''this'', prezentat mai jos. | + | <code bash> |
+ | Moving apple to (10, 11) | ||
+ | </code> | ||
+ | </spoiler> | ||
+ | |||
+ | În acest exemplu, ''x'' și ''y'' din metodă sunt **parametrii** metodei, nu variabilele ale instanței. | ||
+ | |||
+ | Pentru a accesa variabilele care aparțin instanței, trebuie să folosim keyword-ul ''this'', prezentat mai jos. | ||
===Supraîncărcarea metodelor=== | ===Supraîncărcarea metodelor=== | ||
Line 335: | Line 354: | ||
Un alt exemplu cu care sunteți deja familiari este chiar metoda ''print()'' în care puteți introduce mai multe tipuri de argumente. | Un alt exemplu cu care sunteți deja familiari este chiar metoda ''print()'' în care puteți introduce mai multe tipuri de argumente. | ||
- | |||
- | <code java> | ||
- | class PrintStream { | ||
- | void print(char[] arg) { ... } | ||
- | void print(int arg) { ... } | ||
- | void print(double arg) { ... } | ||
- | // etc. | ||
- | } | ||
- | </code> | ||
<code java> | <code java> | ||
Line 351: | Line 361: | ||
</code> | </code> | ||
- | ==Cum alege compilatorul metoda corectă?== | + | <spoiler [Nice to know] Detalii despre algoritmul din spatele supraîncărcării> |
Compilatorul Java folosește următorul algoritm pentru supraîncărcare: | Compilatorul Java folosește următorul algoritm pentru supraîncărcare: | ||
- Caută o potrivire exactă a tipurilor de argumente. | - Caută o potrivire exactă a tipurilor de argumente. | ||
Line 362: | Line 371: | ||
* Vom detalia moștenirea în laboratorul următor. | * Vom detalia moștenirea în laboratorul următor. | ||
</note> | </note> | ||
+ | </spoiler> | ||
====Crearea obiectelor==== | ====Crearea obiectelor==== | ||
Line 387: | Line 397: | ||
int day = 1; | int day = 1; | ||
+ | // Constructor explicit adăugat de programator | ||
SpecialDate(int day) { | SpecialDate(int day) { | ||
this.day = day; | this.day = day; | ||
Line 405: | Line 416: | ||
SpecialDate date2 = new SpecialDate(30); | SpecialDate date2 = new SpecialDate(30); | ||
</code> | </code> | ||
+ | </note> | ||
+ | |||
+ | <note tip> | ||
+ | Constructorii **nu au tip de return** pentru că returnează tipul clasei automat pentru instanțiere. | ||
</note> | </note> | ||
===Supraincărcarea constructorilor=== | ===Supraincărcarea constructorilor=== | ||
- | Java permite existența mai multor constructori în aceeași clasă, cu semnături diferite. Alegerea constructorului potrivit se face la compilare, conform regulilor de selecție a metodelor supraincărcate. | + | Java permite existența **mai multor constructori** în aceeași clasă, cu **semnături diferite**. Alegerea constructorului potrivit se face la compilare, conform regulilor de selecție a metodelor supraincărcate. |
<code java> | <code java> | ||
Line 426: | Line 441: | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | <note tip> | ||
+ | * Puteți crea oricâți constructori doriți într-o clasă. | ||
+ | * Din punct de vedere al coding style-ului vă recomandăm să ordonați constructorii după numărul de parametrii. | ||
+ | </note> | ||
===Copy constructor=== | ===Copy constructor=== | ||
- | În Java, există conceptul de **copy constructor**, acesta reprezentând un constructor care ia ca parametru un obiect de același tip cu clasa în care se află constructorul respectiv. Cu ajutorul acestui constructor, putem să copiem obiecte, prin copierea membru cu membru în constructor. | + | În Java, există conceptul de **copy constructor**, acesta reprezentând un constructor care ia ca parametru un obiect de același tip cu clasa în care se află constructorul respectiv. Cu ajutorul acestui constructor, putem să **copiem obiecte**, prin copierea membru cu membru în constructor. |
<code java> | <code java> | ||
Line 443: | Line 463: | ||
// copy constructor | // copy constructor | ||
public Student(Student student) { | public Student(Student student) { | ||
- | // name este camp privat, noi il putem accesa direct (student.name) | + | /* |
- | // deoarece ne aflam in interiorul clasei | + | "name" este câmp privat, noi îl putem accesa direct (student.name) |
+ | deoarece ne aflam in interiorul clasei | ||
+ | */ | ||
this.name = student.name; | this.name = student.name; | ||
this.averageGrade = student.averageGrade; | this.averageGrade = student.averageGrade; | ||
Line 487: | Line 509: | ||
===Garbage Collection=== | ===Garbage Collection=== | ||
- | Mecanismul de garbage collection are rolul de a **elibera memoria** ocupată de obiectele care nu mai sunt accesibile. Un obiect devine neaccesibil atunci când nu mai există **nicio referință activă** către el. | + | Mecanismul de garbage collection are rolul de a **elibera memoria** ocupată de obiectele care nu mai sunt accesibile. Un obiect devine inaccesibil atunci când nu mai există **nicio referință activă** către el. |
<code java> | <code java> | ||
Line 556: | Line 578: | ||
''this'' se folosește pentru: | ''this'' se folosește pentru: | ||
- | 1. **Dezambiguizare**, prin accesarea la variabile care aparțin instanței ce sunt ascunse de parametri: | + | 1. **Dezambiguizare**, prin accesarea variabilelor care aparțin instanței ce sunt ascunse de parametri: |
<code java> | <code java> | ||
Line 616: | Line 638: | ||
</note> | </note> | ||
- | 3. **Apelul către un alt constructor din aceeași clasă:** | + | 3. Apelul către un **alt constructor** din aceeași clasă: |
- | Keyword-ul ''this'' are un al treilea scenariu în care poate fi folosit, concret apelul către un alt constructor din aceeași clasă se face cu ''this(...)'' și trebuie să fie **prima instrucțiune** din constructorul curent.<code java> | + | Apelul către un alt constructor din aceeași clasă se face cu ''this(...)'' și trebuie să fie **prima instrucțiune** din constructorul curent.<code java> |
class Car { | class Car { | ||
String model; | String model; | ||
Line 632: | Line 654: | ||
System.out.println("Is this the batmobile?"); | System.out.println("Is this the batmobile?"); | ||
} | } | ||
- | this(model, 4); // eroare, apelul "this" nu se află pe prima linie din constructor | + | this("Batmobile", doors); // eroare, apelul "this" nu se află pe prima linie din constructor |
+ | } | ||
+ | |||
+ | Car(String model) { | ||
+ | this(model, 4); // corect | ||
} | } | ||
} | } | ||
Line 638: | Line 664: | ||
<note tip> | <note tip> | ||
- | Java 25 permite ca apelul ''this()'' să nu se afle pe prima linie din constructor. | + | * Java 25 permite ca apelul ''this()'' să nu se afle pe prima linie din constructor. |
+ | * Recomandăm refolosirea constructorilor pentru a evita **cod duplicat**. | ||
</note> | </note> | ||
Line 732: | Line 759: | ||
<code java> | <code java> | ||
class PropertiesExample { | class PropertiesExample { | ||
- | String myString; | + | private String myString; |
- | String getMyString() { | + | public String getMyString() { |
return myString; | return myString; | ||
} | } | ||
- | void setMyString(String myString) { | + | public void setMyString(String myString) { |
this.myString = myString; | this.myString = myString; | ||
} | } | ||
Line 749: | Line 776: | ||
PropertiesExample pe = new PropertiesExample(); | PropertiesExample pe = new PropertiesExample(); | ||
- | pe.setMyString("This is my string!"); | + | pe.myString = "This is bad"; // nu funcționează pentru că este "private" |
+ | |||
+ | pe.setMyString("This is my string!"); // funcționează | ||
System.out.println(pe.getMyString()); | System.out.println(pe.getMyString()); | ||
Line 905: | Line 934: | ||
==== Alocarea memoriei în Heap ==== | ==== Alocarea memoriei în Heap ==== | ||
- | Obiectele în Java sunt stocate în **heap**, o zonă de memorie dedicată alocărilor dinamice. | + | Obiectele în Java sunt stocate în **Heap**, o zonă de memorie dedicată alocărilor dinamice. Pentru a crea un obiect, folosim operatorul ''new''. |
- | Pentru a crea un obiect, folosim operatorul ''new'', care: | + | |
- | + | ||
- | * alocă spațiu în memoria heap; | + | |
- | * apelează constructorul clasei; | + | |
- | * returnează o **referință** către noul obiect, care este stocată pe stack. | + | |
<code java Student.java> | <code java Student.java> | ||
Line 939: | Line 963: | ||
În exemplul de mai sus: | În exemplul de mai sus: | ||
- | * referința ''st'' este pe **stack**; | + | * referința ''st'' este pe **Stack**; |
- | * obiectul ''Student'' este alocat pe **heap**; | + | * obiectul ''Student'' este alocat pe **Heap**; |
* adresa obiectului din heap este copiată în referința ''st''. | * adresa obiectului din heap este copiată în referința ''st''. | ||
{{:poo-ca-cd:laboratoare:obiecte-in-java:stack_and_heap_vars.png?nolink&750|}} | {{:poo-ca-cd:laboratoare:obiecte-in-java:stack_and_heap_vars.png?nolink&750|}} | ||
- | Când metoda în care a fost creat obiectul se termină, referința ''st'' dispare, dar obiectul rămâne în heap până când **Garbage Collector-ul** decide că nu mai este utilizat. | + | Când metoda în care a fost creat obiectul se termină, referința ''st'' dispare, dar obiectul rămâne în Heap până când **Garbage Collector-ul** decide că nu mai este utilizat (când numărul de referințe către acea zonă din Heap ajunge la 0). |
- | <note tip> | ||
- | Heap-ul este gestionat **automat** de JVM prin Garbage Collector, programatorul nu trebuie să elibereze manual memoria. | ||
- | </note> | ||
- | ==== Referințe și pointeri ==== | ||
- | |||
- | Pentru a lucra cu obiectele din heap, avem nevoie de adresa la care sunt stocate. | ||
- | În limbaje precum C, această adresă este memorată într-un **pointer**, oferind control direct asupra memoriei. | ||
- | |||
- | Java **nu permite pointeri direcți**, pentru a evita erorile de acces și scurgerile de memorie. | ||
- | În schimb, folosește **referințe**, care funcționează asemănător, dar sunt gestionate automat de JVM. | ||
- | |||
- | <code java> | ||
- | Date date; // referință de tip "Date" | ||
- | </code> | ||
- | |||
- | Dacă referința nu a fost încă asociată unui obiect, valoarea sa este ''null'', iar accesarea membrilor va produce o eroare de tip ''NullPointerException''. | ||
Line 1030: | Line 1038: | ||
</spoiler> | </spoiler> | ||
- | După cum se poate observa, ''a1'' și ''a2'' vor funcționa ca entități independente una de cealaltă, astfel că modificarea câmpului ''mass'' din ''a1'' nu va avea nici un efect implicit și automat în ''a2''. Există totuși situații când dorim să creăm câmpuri care să fie partajate și să nu fie memorate separat pentru fiecare instanță. | + | După cum se poate observa, ''a1'' și ''a2'' vor funcționa ca entități independente una de cealaltă, astfel că modificarea câmpului ''mass'' din ''a1'' nu va avea nici un efect implicit și automat în ''a2''. |
Membrii statici **nu aparțin unei instanțe** anume, ci **clasei** în sine. Aceștia sunt împărtășiți de-a lungul tuturor obiectelor create din acea clasă și pot să fie accesate **fără** să se creeze o instanță, având o locație specială în memorie (diferită de Heap și Stack). | Membrii statici **nu aparțin unei instanțe** anume, ci **clasei** în sine. Aceștia sunt împărtășiți de-a lungul tuturor obiectelor create din acea clasă și pot să fie accesate **fără** să se creeze o instanță, având o locație specială în memorie (diferită de Heap și Stack). | ||
Line 1038: | Line 1046: | ||
Când declarați o variabilă sau o metodă înăuntrul unei clase: | Când declarați o variabilă sau o metodă înăuntrul unei clase: | ||
* **Membrii instanței**: aparțin doar obiectului instanțiat. (ex. ''a1.mass'' și ''a2.mass'' pot avea valori diferite). | * **Membrii instanței**: aparțin doar obiectului instanțiat. (ex. ''a1.mass'' și ''a2.mass'' pot avea valori diferite). | ||
- | * **Membrii statici**: aparțin clasei și sunt împărtășiți de către toate obiectele (ex. toate merele pot avea aceeași constantă gravitațională). | + | * **Membrii statici**: aparțin clasei și sunt împărtășiți de către toate obiectele (ex. toate merele pot avea aceeași constantă gravitațională ''gravAcc''). |
^Tip^Apartenență^Accesat prin^Cum este valoarea reținută în memorie^ | ^Tip^Apartenență^Accesat prin^Cum este valoarea reținută în memorie^ | ||
Line 1079: | Line 1087: | ||
</spoiler> | </spoiler> | ||
- | <note tip> | ||
- | Folosiți variabile statice pentru a avea **configurații împărtășite** sau **constante la nivel de clase**, nu pentru a stoca date per obiect. | ||
- | </note> | ||
====Accesarea membrilor statici==== | ====Accesarea membrilor statici==== | ||
Line 1110: | Line 1115: | ||
public static void main(String[] args) { | public static void main(String[] args) { | ||
float g = Apple.gravAcc; | float g = Apple.gravAcc; | ||
+ | | ||
+ | // Corect, dar nepreferat | ||
+ | Apple a1 = new Apple(); | ||
+ | float g = a1.gravAcc; | ||
} | } | ||
} | } |