This shows you the differences between two versions of the page.
|
poo:breviare:breviar-03 [2018/09/22 10:36] mihai.nan |
poo:breviare:breviar-03 [2025/10/16 12:26] (current) stefanel.turcu [Extra: Tipuri speciale în Java: enum și record] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== Breviar ====== | ====== Breviar ====== | ||
| ===== Laborator 3 - Clase și Obiecte în Java ===== | ===== Laborator 3 - Clase și Obiecte în Java ===== | ||
| - | + | ==== Introducere ==== | |
| - | * Responsabil: [[mihai.nan.cti@gmail.com|Mihai Nan]] | + | |
| - | * Profesor titular: Carmen Odubășteanu | + | |
| - | + | ||
| - | === Introducere === | + | |
| Presupunem că dorim //să descriem//, uzitând un limbaj de programare, un **obiect** carte. În general, o carte poate fi caracterizată prin titlu, autor și editură. Cum am putea realiza această descriere formală? | Presupunem că dorim //să descriem//, uzitând un limbaj de programare, un **obiect** carte. În general, o carte poate fi caracterizată prin titlu, autor și editură. Cum am putea realiza această descriere formală? | ||
| Line 58: | Line 54: | ||
| <note tip>Se poate observa cu usurință, în cadrul exemplului de mai sus, că atât datele cât și **metodele** (funcțiile) care operează asupra acestora se găsesc în interiorul aceleiași entități, numită **clasa**. Evident, în codul din exemplu sunt folosite concepte care nu au fost încă explicate, dar cunoașterea și înțelegerea lor reprezintă scopul principal al acestui laborator.</note> | <note tip>Se poate observa cu usurință, în cadrul exemplului de mai sus, că atât datele cât și **metodele** (funcțiile) care operează asupra acestora se găsesc în interiorul aceleiași entități, numită **clasa**. Evident, în codul din exemplu sunt folosite concepte care nu au fost încă explicate, dar cunoașterea și înțelegerea lor reprezintă scopul principal al acestui laborator.</note> | ||
| - | === Clase și Obiecte === | + | ==== Clase și Obiecte ==== |
| - | == Ce este un obiect? Ce este o clasă? == | + | === Ce este un obiect? Ce este o clasă? === |
| * Atunci când un producător crează un produs, mai întâi acesta specifică toate caracteristicile produsului într-un document de specificații, iar pe baza acelui document se crează fizic produsul. De exemplu, calculatorul este un produs creat pe baza unui astfel de document de specificații. La fel stau lucrurile și într-un program orientat pe obiecte: mai întâi se crează **clasa** obiectului (documentul de specificații) care înglobează toate caracteristicile unui **obiect** (instanță a clasei), dupa care, pe baza acesteia, se crează (instanțiază) obiectul în memorie. | * Atunci când un producător crează un produs, mai întâi acesta specifică toate caracteristicile produsului într-un document de specificații, iar pe baza acelui document se crează fizic produsul. De exemplu, calculatorul este un produs creat pe baza unui astfel de document de specificații. La fel stau lucrurile și într-un program orientat pe obiecte: mai întâi se crează **clasa** obiectului (documentul de specificații) care înglobează toate caracteristicile unui **obiect** (instanță a clasei), dupa care, pe baza acesteia, se crează (instanțiază) obiectul în memorie. | ||
| Line 68: | Line 64: | ||
| </note> | </note> | ||
| - | == Definirea unei clase == | + | === Definirea unei clase === |
| * Din cele de mai sus deducem că o clasă descrie un obiect, în general, un nou tip de date. Într-o **clasă** găsim **date** și **metode** ce operează asupra datelor respective. | * Din cele de mai sus deducem că o clasă descrie un obiect, în general, un nou tip de date. Într-o **clasă** găsim **date** și **metode** ce operează asupra datelor respective. | ||
| * Pentru a defini o clasă, trebuie folosit cuvântul cheie ''class'' urmat de numele clasei. | * Pentru a defini o clasă, trebuie folosit cuvântul cheie ''class'' urmat de numele clasei. | ||
| Line 87: | Line 83: | ||
| </note> | </note> | ||
| - | == Crearea unui obiect == | + | === Crearea unui obiect === |
| * Spuneam mai sus că un obiect reprezintă o instanță a unei clasa. În Java, instanțierea sau crearea unui obiect se face dinamic, folosind cuvântul cheie ''new'' și are ca efect crearea efectivă a obiectului cu alocarea spațiului de memorie corespunzător. | * Spuneam mai sus că un obiect reprezintă o instanță a unei clasa. În Java, instanțierea sau crearea unui obiect se face dinamic, folosind cuvântul cheie ''new'' și are ca efect crearea efectivă a obiectului cu alocarea spațiului de memorie corespunzător. | ||
| * Așa cum fiecare calculator construit pe baza documentului de specificații are propriile componente, fiecare obiect de tip ''Carte'' are propriile sale atribute. | * Așa cum fiecare calculator construit pe baza documentului de specificații are propriile componente, fiecare obiect de tip ''Carte'' are propriile sale atribute. | ||
| Line 119: | Line 115: | ||
| Point p; | Point p; | ||
| Dimension d; | Dimension d; | ||
| - | | + | |
| public Rectangle(Point p, Dimension d) { | public Rectangle(Point p, Dimension d) { | ||
| this.p = p; | this.p = p; | ||
| Line 168: | Line 164: | ||
| * Atribuirea ''c3 = c2'' nu a făcut altceva decât să atașeze referinței ''c3'' obiectul având aceeași identitate precum cel referit de ''c2'', adică obiectul secund creat. | * Atribuirea ''c3 = c2'' nu a făcut altceva decât să atașeze referinței ''c3'' obiectul având aceeași identitate precum cel referit de ''c2'', adică obiectul secund creat. | ||
| + | === Trimiterea datelor în Java === | ||
| + | |||
| + | Spre deosebire de C++, în Java nu există o modalitate prin care să poată fi făcută o diferențiere explicită între trimiterea parametrilor **<color red>prin referință</color>** și trimiterea acestora **<color blue>prin valoare</color>**. | ||
| + | |||
| + | Conform specificației Java ([[https://docs.oracle.com/javase/specs/jls/se9/html/jls-4.html#jls-4.3|secțiunea 4.3]]), transmiterea tuturor datelor, atât a celor de tip obiect, cât și a celor primitive, este definită următoarea regulă:. | ||
| + | |||
| + | <note important>In Java argumentele sunt trimise doar **<color blue>prin valoare</color>** (pass-by-value).</note> | ||
| + | |||
| + | Chiar dacă la o primă vedere această regulă poate să pară simplă, este necesară o explicație suplimentară. În cazul valorilor primitive, valoarea este considerată pur și simplu data asociată (exemple ''1'', ''10.5'', ''true'') iar valoarea parametrilor este copiată de fiecare dată când ei sunt plasați în apeluri. | ||
| + | |||
| + | În ceea ce privește obiectele, în Java, se utilizează următoarea regulă, mai extinsă: | ||
| + | |||
| + | <note important>Valoarea asociată unui obiecte este, de fapt, un pointer, numit referință, la obiectul din memorie.</note> | ||
| + | |||
| + | Spre exemplu, dacă definim o expresie de forma ''Foo foo = new Foo();'', variabila ''foo'' nu deține obiectul ''Foo'' creat, ci, mai degrabă, o valoare a pointerului pentru obiectul ''Foo'' creat. Valoarea acestui pointer la obiect (ceea ce în specificația Java se numește **o referință de obiect** sau pur și simplu **referință**) este copiată de fiecare dată când obiectul este plasat ca argument al unui apel. | ||
| + | |||
| + | În Java, numai următoarele operații pot fi efectuate pe o referință de obiect: | ||
| + | * accesarea câmurilor; | ||
| + | * invocarea metodelor; | ||
| + | * operatorul pentru castare; | ||
| + | * operatorul pentru concatenarea string-urilor (atunci când primește ca parametru o referință la un obiect, o să realizeze o conversie a referinței la String, prin invocarea metodei ''toString'' pentru obiectul referențiat); | ||
| + | * operatorul ''instanceof''; | ||
| + | * operatorii de egalitate pentru referințe: ''=='' și ''!=''; | ||
| + | * operatorul condițional: ''? :''. | ||
| + | |||
| + | <note tip>În practică, acest lucru înseamnă că putem schimba câmpurile obiectului trimis ca parametru într-o metodă și să invocăm metodele acestuia, însă nu putem schimba obiectul spre care pointează referința. Deoarece referința este plasată prin valoare, pointerul original este copiat în stiva de apeluri atunci când metoda este invocată.</note> | ||
| + | |||
| + | {{ :poo:breviare:java-pass-by-value.png |}} | ||
| + | |||
| + | Pentru a înțelege mai bine conceptele prezentate în această secțiune, puteți consulta și analiza rezultatele pentru următoarele secvențe de cod. | ||
| + | |||
| + | <code java> | ||
| + | int someValue = 10; | ||
| + | int anotherValue = someValue; | ||
| + | someValue = 17; | ||
| + | System.out.println("Some value = " + someValue); | ||
| + | System.out.println("Another value = " + anotherValue); | ||
| + | </code> | ||
| + | |||
| + | <code java> | ||
| + | public class Test { | ||
| + | public void process(int value) { | ||
| + | System.out.println("Entered method (value = " + value + ")"); | ||
| + | value = 50; | ||
| + | System.out.println("Changed value within method (value = " + value + ")"); | ||
| + | System.out.println("Leaving method (value = " + value + ")"); | ||
| + | } | ||
| + | |||
| + | public static void main(String args[]) { | ||
| + | Test processor = new Test(); | ||
| + | int someValue = 7; | ||
| + | System.out.println("Before calling method (value = " + someValue + ")"); | ||
| + | processor.process(someValue); | ||
| + | System.out.println("After calling method (value = " + someValue + ")"); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | <code java> | ||
| + | class Ball {} | ||
| + | |||
| + | class Main { | ||
| + | public static void main(String args[]) { | ||
| + | Ball someBall = new Ball(); | ||
| + | System.out.println("Some ball before creating another ball = " + someBall); | ||
| + | Ball anotherBall = someBall; | ||
| + | someBall = new Ball(); | ||
| + | System.out.println("Some ball = " + someBall); | ||
| + | System.out.println("Another ball = " + anotherBall); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | <code java> | ||
| + | class Vehicle { | ||
| + | private String name; | ||
| + | public Vehicle(String name) { | ||
| + | this.name = name; | ||
| + | } | ||
| + | public void setName(String name) { | ||
| + | this.name = name; | ||
| + | } | ||
| + | public String getName() { | ||
| + | return name; | ||
| + | } | ||
| + | @Override | ||
| + | public String toString() { | ||
| + | return "Vehicle[name = " + name + "]"; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | class VehicleProcessor { | ||
| + | public void process(Vehicle vehicle) { | ||
| + | System.out.println("Entered method (vehicle = " + vehicle + ")"); | ||
| + | vehicle.setName("A changed name"); | ||
| + | System.out.println("Changed vehicle within method (vehicle = " + vehicle + ")"); | ||
| + | System.out.println("Leaving method (vehicle = " + vehicle + ")"); | ||
| + | } | ||
| + | public void processWithReferenceChange(Vehicle vehicle) { | ||
| + | System.out.println("Entered method (vehicle = " + vehicle + ")"); | ||
| + | vehicle = new Vehicle("A new name"); | ||
| + | System.out.println("New vehicle within method (vehicle = " + vehicle + ")"); | ||
| + | System.out.println("Leaving method (vehicle = " + vehicle + ")"); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | class Main { | ||
| + | public static void main(String args[]) { | ||
| + | VehicleProcessor processor = new VehicleProcessor(); | ||
| + | Vehicle vehicle = new Vehicle("Some name"); | ||
| + | System.out.println("Before calling method (vehicle = " + vehicle + ")"); | ||
| + | processor.process(vehicle); | ||
| + | System.out.println("After calling method (vehicle = " + vehicle + ")"); | ||
| + | processor.processWithReferenceChange(vehicle); | ||
| + | System.out.println("After calling reference-change method (vehicle = " + vehicle + ")"); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| === Componența unei clase === | === Componența unei clase === | ||
| Line 183: | Line 297: | ||
| Dacă programatorul nu prevede într-o clasa niciun constructor, atunci compilatorul va genera pentru clasa respectivă un constructor implicit fără niciun argument și al cărui corp de instrucțiuni este vid. | Dacă programatorul nu prevede într-o clasa niciun constructor, atunci compilatorul va genera pentru clasa respectivă un constructor implicit fără niciun argument și al cărui corp de instrucțiuni este vid. | ||
| </note> | </note> | ||
| + | |||
| + | {{ :poo:breviare:default-constructor1.png |}} | ||
| <note warning> | <note warning> | ||
| Line 225: | Line 341: | ||
| <note warning> | <note warning> | ||
| - | * Un membru static al unei clase caracterizează clasa în interiorul căreia este definit precum și toate obiectele clasei respective. | + | Un membru static al unei clase caracterizează clasa în interiorul căreia este definit precum și toate obiectele clasei respective. |
| - | * Un membru al unei clase (atribut sau metodă) este static dacă el este precedat de cuvântul cheie ''static''. | + | |
| - | * Din interiorul unei metode statice pot fi accesați doar alți membri statici ai clasei în care este definită metoda, accesarea membrilor nestatici ai clasei producând o eroare de compilare. | + | Un membru al unei clase (atribut sau metodă) este static dacă el este precedat de cuvântul cheie ''static''. |
| - | * Trebuie avut în vedere contextul static al metodei **main**. Dintr-un context static nu se pot apela funcții nestatice, în schimb, se pot crea obiecte ale oricărei clase. | + | |
| + | Din interiorul unei metode statice pot fi accesați doar alți membri statici ai clasei în care este definită metoda, accesarea membrilor nestatici ai clasei producând o eroare de compilare. | ||
| + | |||
| + | Trebuie avut în vedere contextul static al metodei **main**. Dintr-un context static nu se pot apela funcții nestatice, în schimb, se pot crea obiecte ale oricărei clase. | ||
| </note> | </note> | ||
| - | === Principii POO === | + | ==== Principii POO ==== |
| + | |||
| + | Programarea Orientată pe Obiecte (OOP) se bazează pe un set de principii fundamentale care permit organizarea logică și extensibilă a codului, facilitând reutilizarea, modularitatea și întreținerea aplicațiilor. | ||
| + | |||
| + | Principiile de bază ale OOP sunt următoarele: | ||
| + | * **Încapsularea** – ascunderea detaliilor interne ale unei clase și expunerea doar a unei interfețe publice (prin metode și proprietăți). Scopul este protejarea datelor și controlul modului în care acestea sunt accesate și modificate. | ||
| + | * **Abstractizarea** – procesul de simplificare a unei entități complexe prin definirea doar a caracteristicilor esențiale, eliminând detaliile irelevante. În Java, abstracția se realizează prin clase abstracte și interfețe. | ||
| + | * **Moștenirea** – mecanismul prin care o clasă poate prelua (moșteni) atributele și comportamentele unei alte clase. Aceasta permite reutilizarea codului și extinderea funcționalității. | ||
| + | <note> Moștenirea va fi studiată pe larg în laboratoarele și cursurile următoare, unde vom analiza concepte precum clase derivate, suprascrierea metodelor și ierarhiile de clase.</note> | ||
| + | * **Polimorfismul** – posibilitatea ca același comportament (de exemplu, o metodă) să se manifeste diferit în funcție de contextul obiectului care îl execută. Acesta este un concept-cheie care permite flexibilitate și extensibilitate în designul aplicațiilor. | ||
| + | |||
| + | {{:poo:breviare:poo-principii.png?700|}} | ||
| <note important> | <note important> | ||
| Mai multe funcții pot avea același nume în același domeniu de definiție, dacă se pot diferenția prin numărul sau tipul argumentelor de apel. | Mai multe funcții pot avea același nume în același domeniu de definiție, dacă se pot diferenția prin numărul sau tipul argumentelor de apel. | ||
| </note> | </note> | ||
| - | == Supraincarcarea == | + | === Supraîncarcarea === |
| - | În Java, se pot găsi două sau mai multe metode, în cadrul aceleiași clase, care să aibă același nume, atâta timp cât argumentele lor sunt diferite. În acest caz, se spune că metoda este supraîncărcată, iar procedeul se numește **supraîncarcarea metodelor**. | + | În Java, se pot găsi două sau mai multe metode, în cadrul aceleiași clase, care să aibă același nume, atâta timp cât argumentele lor sunt diferite (polimorfism). În acest caz, se spune că metoda este supraîncărcată, iar procedeul se numește **supraîncarcarea metodelor**. |
| Pentru o mai bună înțelegere a acestui principiu POO, se va oferi, în continuare, un exemplu pentru o metodă care determină maximul. | Pentru o mai bună înțelegere a acestui principiu POO, se va oferi, în continuare, un exemplu pentru o metodă care determină maximul. | ||
| Line 266: | Line 397: | ||
| Un alt exemplu elocvent, pentru acest prinicpiu POO, este operatorul ''+'' care execută operații diferite în cotexte diferite. | Un alt exemplu elocvent, pentru acest prinicpiu POO, este operatorul ''+'' care execută operații diferite în cotexte diferite. | ||
| + | |||
| + | ==== Extra: Tipuri speciale în Java: enum și record ==== | ||
| + | |||
| + | === Ce este un enum? === | ||
| + | |||
| + | Un **enum** (de la *enumeration*) este un tip special de clasă care definește un set fix de constante. | ||
| + | Este folosit atunci când o variabilă poate avea doar un număr limitat de valori posibile. | ||
| + | |||
| + | **Exemplu:** | ||
| + | |||
| + | <code java> | ||
| + | public enum ZiSaptamana { | ||
| + | LUNI, MARTI, MIERCURI, JOI, VINERI, SAMBATA, DUMINICA | ||
| + | } | ||
| + | |||
| + | public class Program { | ||
| + | public static void main(String[] args) { | ||
| + | ZiSaptamana zi = ZiSaptamana.VINERI; | ||
| + | |||
| + | // Poți folosi switch cu enum | ||
| + | switch (zi) { | ||
| + | case SAMBATA, DUMINICA -> System.out.println("Weekend!"); | ||
| + | default -> System.out.println("Zi de lucru."); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | **Observații:** | ||
| + | * Valorile //LUNI//, //MARTI//, etc. sunt constante (publice, statice și imutabile). | ||
| + | * Se folosesc frecvent pentru a face codul mai clar și mai sigur (în locul valorilor numerice sau string). | ||
| + | |||
| + | === Ce este un record? === | ||
| + | |||
| + | Un **record** este o formă modernă de clasă introdusă în Java 14 (stabilă din Java 16), | ||
| + | care servește pentru a defini **obiecte imutabile de tip „data class”**, adică structuri care doar rețin date. | ||
| + | |||
| + | **Exemplu simplu:** | ||
| + | |||
| + | <code java> | ||
| + | public record Punct(int x, int y) { } | ||
| + | |||
| + | public class Main { | ||
| + | public static void main(String[] args) { | ||
| + | Punct p1 = new Punct(3, 5); | ||
| + | System.out.println(p1); // => Punct[x=3, y=5] | ||
| + | System.out.println(p1.x()); // accesează câmpul x | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | **Ce oferă automat un record:** | ||
| + | * constructor pentru toți parametrii (`new Punct(int x, int y)`) | ||
| + | * metode //x()// și //y()// pentru acces la date (getters) | ||
| + | * metode //equals()//, //hashCode()// și //toString()// generate automat | ||
| + | * toate câmpurile sunt //private// și //final// → obiectul este imutabil | ||
| + | |||
| + | **Avantaj:** | ||
| + | **record** reduce semnificativ codul boilerplate comparativ cu o clasă clasică. | ||
| + | |||
| + | **Exemplu echivalent (clasă obișnuită):** | ||
| + | |||
| + | <code java> | ||
| + | public class PunctClasic { | ||
| + | private final int x; | ||
| + | private final int y; | ||
| + | |||
| + | public PunctClasic(int x, int y) { | ||
| + | this.x = x; | ||
| + | this.y = y; | ||
| + | } | ||
| + | |||
| + | public int getX() { return x; } | ||
| + | public int getY() { return y; } | ||
| + | |||
| + | @Override | ||
| + | public String toString() { | ||
| + | return "Punct[x=" + x + ", y=" + y + "]"; | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | **Când să folosești un record:** | ||
| + | * Când vrei un obiect simplu, imutabil, care doar conține date. | ||
| + | * Când nu ai nevoie de moștenire (un //record// nu poate extinde altă clasă). | ||