This shows you the differences between two versions of the page.
|
poo:breviare:breviar-03 [2018/09/22 17:14] 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 ===== | ||
| - | |||
| - | * Responsabil: [[mihai.nan.cti@gmail.com|Mihai Nan]] | ||
| - | * Profesor titular: Carmen Odubășteanu | ||
| - | |||
| ==== Introducere ==== | ==== 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 172: | Line 168: | ||
| 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>**. | 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 (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ă:. | + | 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> | <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. | 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 192: | 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 244: | Line 351: | ||
| ==== 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 278: | 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ă). | ||