Differences

This shows you the differences between two versions of the page.

Link to this comparison view

poo-ca-cd:laboratoare:programare-avansata-java [2026/01/11 18:51]
florian_luis.micu created
poo-ca-cd:laboratoare:programare-avansata-java [2026/01/12 01:22] (current)
florian_luis.micu [[Optional] Software Development Methodologies]
Line 4: Line 4:
   * Data publicării:​ 12.01.2026   * Data publicării:​ 12.01.2026
   * Data ultimei modificări:​ 12.01.2026   * Data ultimei modificări:​ 12.01.2026
 +
 +=====Obiective=====
 +
 +Scopul acestui laborator este introducerea studenților în concepte mai avansate care permit crearea framework-urilor și a aplicațiilor enterprise.
 +
 +Aspectele urmărite sunt:
 +  * înțelegerea conceptelor de testare.
 +  * utilizarea corectă a mecanismului de reflection.
 +  * exersarea serializărilor și deserealizărilor.
 +  * implementarea și utilizarea corectă a adnotărilor.
 +  * exersarea folosirii aserților.
 +  * familiarizarea cu dependențe populare cum ar fi JUnit, Mockito, Jackson.
 +
 +Aspectele **bonus** urmărite sunt:
 +  * înțelegerea mecanismelor de logging.
 +  * aprofundarea metodologiilor de dezvoltare software.
 +  * Diferența dintre getClass() și instanceof.
 +  * Organizarea memoriei pentru moștenire în Java.
 +
 +<note warning>
 +  * În acest laborator există mai multe secțiuni marcate **[Optional]**. Aceste secțiuni cuprind informații **bonus** care vă pot fi prezentate în **timpul laboratorului** sau pe care le puteți aprofunda **în afara** acestuia, ele nefiind necesare pentru laboratoarele viitoare sau pentru teme.
 +  * De asemenea, veți întâlni câteva secțiuni marcate **[Nice to know]**. Vă recomandăm ca acestea să aibă **prioritate** în parcurgerea secțiunilor de tip **[Optional]**,​ deoarece vă pot oferi informații bonus care să fie și foarte probabil utile pentru **teme** sau **laboratoare viitoare**.
 +</​note>​
 +
 +=====🔄 Serializare și Deserializare=====
 +
 +====Ce este serializarea?​====
 +
 +**Serializarea** reprezintă procesul prin care un obiect Java este transformat într-o **reprezentare transferabilă**,​ de regulă o secvență de bytes sau un format text, astfel încât să poată fi:
 +  * salvat pe disc
 +  * transmis prin rețea
 +  * stocat temporar (cache)
 +  * restaurat ulterior în memorie
 +
 +Operația inversă se numește **deserializare** și presupune refacerea obiectului original din această reprezentare.
 +
 +====De ce avem nevoie de serializare?​====
 +
 +Serializarea este folosită în special pentru:
 +  * salvarea stării unei aplicații
 +  * persistarea temporară a obiectelor
 +  * transfer de date între procese sau servicii
 +  * sisteme de cache distribuit
 +  * mecanisme de rollback / snapshot
 +
 +<note important>​
 +Serializarea **nu înlocuiește o bază de date**. Este un mecanism de transport sau stocare temporară, nu unul de persistență relațională.
 +</​note>​
 +
 +====Serializarea nativă Java====
 +
 +===Interfața Serializable===
 +
 +Pentru ca un obiect să poată fi serializat de JVM folosind mecanismul nativ, clasa sa trebuie să implementeze interfața: ''​java.io.Serializable''​.
 +
 +<code java>
 +public class User implements Serializable {
 +    private String name;
 +    private int age;
 +}
 +</​code>​
 +
 +Această interfață:​
 +  * este un **marker interface** (deci nu definește metode),
 +  * este verificată de JVM la **runtime**,​
 +  * permite folosirea claselor ''​ObjectOutputStream''​ și ''​ObjectInputStream''​.
 +
 +Dacă un obiect nu este serializabil,​ JVM aruncă ''​NotSerializableException''​.
 +
 +====Exemplu complet: serializare și deserializare====
 +
 +**1. Clasa model**
 +
 +<code java>
 +import java.io.Serializable;​
 +
 +public class Person implements Serializable {
 +    private String name;
 +    private int age;
 +
 +    public Person(String name, int age) {
 +        this.name = name;
 +        this.age = age;
 +    }
 +
 +    @Override
 +    public String toString() {
 +        return name + " (" + age + "​)";​
 +    }
 +}
 +</​code>​
 +
 +**2. Serializarea unui obiect**
 +
 +<code java>
 +import java.io.FileOutputStream;​
 +import java.io.ObjectOutputStream;​
 +
 +public class SerializeDemo {
 +    public static void main(String[] args) throws Exception {
 +        Person p = new Person("​Ana",​ 30);
 +
 +        ObjectOutputStream out =
 +            new ObjectOutputStream(new FileOutputStream("​person.ser"​));​
 +
 +        out.writeObject(p);​
 +        out.close();​
 +
 +        System.out.println("​Object serialized."​);​
 +    }
 +}
 +</​code>​
 +
 +**3. Deserializarea unui obiect**
 +
 +<code java>
 +import java.io.FileInputStream;​
 +import java.io.ObjectInputStream;​
 +
 +public class DeserializeDemo {
 +    public static void main(String[] args) throws Exception {
 +        ObjectInputStream in =
 +            new ObjectInputStream(new FileInputStream("​person.ser"​));​
 +
 +        Person p = (Person) in.readObject();​
 +        in.close();
 +
 +        System.out.println("​Deserialized:​ " + p);
 +    }
 +}
 +</​code>​
 +
 +<spoiler Output>
 +<code bash>
 +Object serialized.
 +Deserialized:​ Ana (30)
 +</​code>​
 +</​spoiler>​
 +
 +====Câmpuri transient====
 +
 +Nu toate câmpurile unui obiect ar trebui serializate. Exemple tipice:
 +  * parole
 +  * token-uri
 +  * conexiuni
 +  * cache-uri
 +
 +Pentru a exclude un câmp de la serializare folosim cuvântul cheie ''​transient''​.
 +
 +<code java>
 +public class Account implements Serializable {
 +    private String username;
 +    private transient String password;
 +}
 +</​code>​
 +
 +<note important>​
 +Deoarece am declarat ''​password''​ ca fiind ''​transient'',​ acesta nu este salvat, iar după deserializare devine ''​null''​.
 +</​note>​
 +
 +====Versionarea serializării====
 +
 +Câmpul ''​serialVersionUID''​ identifică **versiunea unei clase serializate**.
 +
 +<code java>
 +private static final long serialVersionUID = 1L;
 +</​code>​
 +
 +Acest identificator este folosit de JVM pentru a verifica dacă o clasă este compatibilă cu obiectul serializat, astfel putem garanta compatibilitatea operațiilor.
 +
 +<note tip>
 +Dacă nu este declarat explicit:
 +  * JVM generează unul automat.
 +  * orice mică modificare va genera un nou UID.
 +</​note>​
 +
 +====Serializare modernă: Jackson (JSON)====
 +
 +===Ce este Jackson?===
 +
 +**Jackson** este o bibliotecă externă foarte populară care permite:
 +  * serializarea obiectelor Java în **JSON**,
 +  * deserializarea JSON-ului în obiecte Java.
 +
 +Spre deosebire de serializarea nativă:
 +  * nu folosește format binar,
 +  * nu necesită ''​Serializable'',​
 +  * este independentă de JVM.
 +
 +===De ce este Jackson preferat în aplicațiile moderne?===
 +
 +  * JSON este lizibil și ușor de debugat,
 +  * este interoperabil cu alte limbaje,
 +  * permite control fin prin adnotări,
 +  * este mult mai sigur decât serializarea nativă.
 +
 +Este standardul de facto pentru:
 +  * API-uri REST,
 +  * microservicii,​
 +  * testare și integrare.
 +
 +===Exemplu cu Jackson===
 +
 +**1. Dependința Jackson**
 +
 +Într-un proiect real (Maven), Jackson se adaugă astfel:
 +
 +<code xml pom.xml>
 +<​dependency>​
 +    <​groupId>​com.fasterxml.jackson.core</​groupId>​
 +    <​artifactId>​jackson-databind</​artifactId>​
 +    <​version>​2.16.1</​version>​
 +</​dependency>​
 +</​code>​
 +
 +<note tip>
 +Multe proiecte au deja această dependență când se folosește Spring Boot pentru crearea unui API.
 +</​note>​
 +
 +**2. Clasa model**
 +
 +<code java>
 +import com.fasterxml.jackson.annotation.JsonIgnore;​
 +import com.fasterxml.jackson.annotation.JsonProperty;​
 +
 +public class User {
 +
 +    private String name;
 +    private int age;
 +
 +    @JsonIgnore
 +    private String password;
 +
 +    public User() {
 +        // constructor fără parametri NECESAR pentru deserializare
 +    }
 +
 +    public User(String name, int age, String password) {
 +        this.name = name;
 +        this.age = age;
 +        this.password = password;
 +    }
 +
 +    public String getName() {
 +        return name;
 +    }
 +
 +    public int getAge() {
 +        return age;
 +    }
 +
 +    @JsonProperty("​years"​)
 +    public void setAge(int age) {
 +        this.age = age;
 +    }
 +
 +    public String getPassword() {
 +        return password;
 +    }
 +
 +    @Override
 +    public String toString() {
 +        return "​User{name='"​ + name + "',​ age=" + age + "​}";​
 +    }
 +}
 +</​code>​
 +
 +<note important>​
 +  * constructorul fără parametri este obligatoriu pentru Jackson
 +  * @JsonIgnore exclude câmpul din JSON
 +  * @JsonProperty permite maparea numelor diferite din JSON
 +</​note>​
 +
 +**3. Serializare:​ obiect → JSON**
 +
 +<code java>
 +import com.fasterxml.jackson.databind.ObjectMapper;​
 +
 +public class JacksonSerializeDemo {
 +
 +    public static void main(String[] args) throws Exception {
 +
 +        ObjectMapper mapper = new ObjectMapper();​
 +
 +        User user = new User("​Ana",​ 30, "​secret123"​);​
 +
 +        String json = mapper.writeValueAsString(user);​
 +
 +        System.out.println("​JSON rezultat:"​);​
 +        System.out.println(json);​
 +    }
 +}
 +</​code>​
 +
 +<spoiler output>
 +<code json>
 +{"​name":"​Ana","​age":​30}
 +</​code>​
 +</​spoiler>​
 +
 +<note tip>
 +Câmpul ''​password''​ nu apare deoarece are adnotarea ''​@JsonIgnore''​.
 +</​note>​
 +
 +**4. Deserializare:​ JSON → obiect**
 +
 +<code java>
 +import com.fasterxml.jackson.databind.ObjectMapper;​
 +
 +public class JacksonDeserializeDemo {
 +
 +    public static void main(String[] args) throws Exception {
 +
 +        ObjectMapper mapper = new ObjectMapper();​
 +
 +        String json = """​
 +        {
 +          "​name":​ "​Ion",​
 +          "​years":​ 25,
 +          "​password":​ "​shouldBeIgnored"​
 +        }
 +        """;​
 +
 +        User user = mapper.readValue(json,​ User.class);​
 +
 +        System.out.println("​Obiect deserializat:"​);​
 +        System.out.println(user);​
 +    }
 +}
 +</​code>​
 +
 +<spoiler output>
 +<code bash>
 +User{name='​Ion',​ age=25}
 +</​code>​
 +</​spoiler>​
 +
 +<note tip>
 +  * ''​years''​ → ''​age''​ datorită ''​@JsonProperty''​
 +  * ''​password''​ este ignorat
 +  * Jackson a folosit constructorul fără parametri + setter-ele
 +</​note>​
 +
 +**5. Serializare în fișier și citire din fișier**
 +
 +Scriere în fișier
 +<code java>
 +mapper.writeValue(new File("​user.json"​),​ user);
 +</​code>​
 +
 +Citire din fișier
 +<code java>
 +User user = mapper.readValue(new File("​user.json"​),​ User.class);​
 +</​code>​
 +
 +=====🏷️ Adnotări în Java=====
 +
 +====Ce sunt adnotările?​====
 +
 +**Adnotările** sunt **metadate** atașate elementelor din codul Java (clase, metode, câmpuri, parametri).
 +
 +Ele **nu modifică direct logica programului**,​ ci oferă **informații suplimentare** care pot fi interpretate de:
 +  * compilator
 +  * JVM
 +  * framework-uri
 +  * tool-uri externe (JUnit, Jackson, Spring etc.)
 +
 +Cu alte cuvinte, adnotările spun **ce este codul**, nu **ce face**.
 +
 +====De ce folosim adnotări?​====
 +
 +Adnotările au apărut pentru a elimina:
 +  * configurații externe (XML)
 +  * cod repetitiv (boilerplate)
 +  * convenții fragile bazate pe nume
 +
 +Astăzi, ele sunt fundamentale în:
 +  * serializare/​deserializare (Jackson)
 +  * testare (JUnit)
 +  * dependency injection (Spring)
 +  * validare
 +  * ORM (Hibernate)
 +
 +====Sintaxa unei adnotări====
 +
 +<code java>
 +@AnnotationName
 +public class Example { }
 +</​code>​
 +
 +Adnotările pot avea **parametri**:​
 +
 +<code java>
 +@JsonProperty("​user_name"​)
 +private String name;
 +</​code>​
 +
 +sau mai mulți:
 +<code java>
 +@MyAnnotation(value = "​test",​ enabled = true)
 +</​code>​
 +
 +====Unde pot fi aplicate adnotările?​====
 +
 +Prin @Target putem controla **unde este permisă adnotarea**:​
 +  * ''​TYPE''​ – clase, interfețe
 +  * ''​FIELD''​ – câmpuri
 +  * ''​METHOD''​ – metode
 +  * ''​PARAMETER''​ – parametri
 +  * ''​CONSTRUCTOR''​
 +  * ''​LOCAL_VARIABLE''​
 +
 +Exemplu:
 +<code java>
 +@Target(ElementType.FIELD)
 +</​code>​
 +
 +====Când sunt disponibile adnotările?​ (Retention)====
 +
 +''​@Retention''​ definește **cât timp există adnotarea**:​
 +<code java>
 +@Retention(RetentionPolicy.RUNTIME)
 +</​code>​
 +
 +Tipuri:
 +  * ''​SOURCE''​ – doar la compilare (ex: ''​@Override''​)
 +  * ''​CLASS''​ – în bytecode, dar nu la runtime
 +  * ''​RUNTIME''​ – disponibilă prin reflection (cea mai importantă)
 +
 +<note tip>
 +Jackson, JUnit și Spring folosesc **RUNTIME**.
 +</​note>​
 +
 +====Adnotări built-in importante====
 +
 +===@Override===
 +
 +<code java>
 +@Override
 +public String toString() { ... }
 +</​code>​
 +
 +  * verifică dacă metoda chiar suprascrie una din părinte
 +  * previne erori de semnătură
 +
 +===@Deprecated===
 +
 +<code java>
 +@Deprecated
 +public void oldMethod() { }
 +</​code>​
 +
 +  * marchează cod care nu mai trebuie folosit
 +  * IDE-ul afișează warning
 +
 +===@SuppressWarnings===
 +
 +<code java>
 +@SuppressWarnings("​unchecked"​)
 +</​code>​
 +
 +  * dezactivează warning-uri ale compilatorului
 +  * trebuie folosit cu **atenție**
 +
 +====Adnotări și Jackson====
 +
 +Jackson folosește adnotări pentru a controla serializarea:​
 +<code java>
 +@JsonIgnore
 +private String password;
 +</​code>​
 +
 +<code java>
 +@JsonProperty("​years"​)
 +private int age;
 +</​code>​
 +
 +Aceste adnotări:
 +  * sunt citite prin **reflection**
 +  * influențează comportamentul ''​ObjectMapper''​
 +
 +====Crearea unei adnotări custom====
 +
 +**1. Definirea adnotării**
 +
 +<code java>
 +import java.lang.annotation.*;​
 +
 +@Retention(RetentionPolicy.RUNTIME)
 +@Target(ElementType.FIELD)
 +public @interface Important {
 +    String value() default "";​
 +}
 +</​code>​
 +
 +<note important>​
 +  * adnotările folosesc ''​@interface''​
 +  * metodele NU au corp
 +  * pot avea valori default
 +</​note>​
 +
 +**2. Utilizarea adnotării**
 +
 +<code java>
 +public class Person {
 +
 +    @Important("​primary identifier"​)
 +    private String name;
 +
 +    private int age;
 +}
 +</​code>​
 +
 +====Citirea adnotărilor cu Reflection====
 +
 +<code java>
 +import java.lang.reflect.Field;​
 +
 +public class AnnotationDemo {
 +
 +    public static void main(String[] args) throws Exception {
 +
 +        Class<​Person>​ clazz = Person.class;​
 +
 +        for (Field field : clazz.getDeclaredFields()) {
 +            if (field.isAnnotationPresent(Important.class)) {
 +                Important imp = field.getAnnotation(Important.class);​
 +                System.out.println(field.getName() + " -> " + imp.value());​
 +            }
 +        }
 +    }
 +}
 +</​code>​
 +
 +<spoiler output>
 +<code bash>
 +name -> primary identifier
 +</​code>​
 +</​spoiler>​
 +
 +====[Optional] Când sunt procesate adnotările?​====
 +
 +===Cele trei momente de procesare===
 +
 +Java definește **trei momente distincte** în care adnotările pot fi citite sau folosite:
 +
 +**1. La compilare (compile-time)**
 +
 +Adnotările cu:
 +<code java>
 +@Retention(RetentionPolicy.SOURCE)
 +</​code>​
 +
 +sunt:
 +  * disponibile doar în timpul compilării
 +  * eliminate complet din bytecode
 +  * folosite de compilator sau tool-uri
 +
 +Exemplu:
 +<code java>
 +@Override
 +</​code>​
 +
 +Rol:
 +  * verificare de corectitudine
 +  * warning-uri / erori de compilare
 +
 +<note important>​
 +Nu există acces la aceste adnotări la runtime.
 +</​note>​
 +
 +**2. După compilare, înainte de rulare (build-time / processing)**
 +
 +Unele adnotări sunt procesate prin **Annotation Processors** (APT – Annotation Processing Tool).
 +
 +Acestea:
 +  * rulează după compilare
 +  * pot genera cod
 +  * pot valida structura codului
 +
 +Exemple reale:
 +  * Lombok (''​@Getter'',​ ''​@Setter''​)
 +  * MapStruct
 +  * AutoValue
 +
 +<note tip>
 +  * Conceptual există un **pas de preprocesare**,​ dar **nu la nivelul JVM**, ci la nivelul **build-ului**.
 +  * Acesta NU este un preprocesor ca în C (''#​define''​),​ ci un mecanism controlat de compilator.
 +</​note>​
 +
 +3. La runtime (execuție)
 +
 +Adnotările cu:
 +<code java>
 +@Retention(RetentionPolicy.RUNTIME)
 +</​code>​
 +
 +sunt:
 +  * păstrate în bytecode
 +  * disponibile prin **Reflection**
 +  * interpretate de framework-uri
 +
 +Exemple:
 +  * Jackson (''​@JsonProperty''​)
 +  * JUnit (''​@Test''​)
 +  * Spring (''​@Autowired''​)
 +
 +Acesta este **cel mai important caz** pentru Java modern.
 +
 +
 +=====🪞 Reflection în Java=====
 +
 +====Ce este Reflection?​====
 +
 +Reflection este mecanismul prin care un program Java poate:
 +  * inspecta **structura claselor** la runtime
 +  * accesa **câmpuri, metode, constructori**
 +  * crea obiecte dinamic
 +  * invoca metode fără a le cunoaște la compilare
 +
 +Cu alte cuvinte, Reflection permite unui program să se **auto-analizeze** și să se **auto-manipuleze** în timpul execuției.
 +
 +====De ce există Reflection?​====
 +
 +Reflection a fost introdus pentru a permite:
 +  * framework-uri generice (Spring, Hibernate, Jackson, JUnit)
 +  * extensibilitate fără modificarea codului
 +  * comportament configurabil prin metadate (adnotări)
 +
 +Fără Reflection:
 +  * multe framework-uri moderne nu ar fi posibile
 +  * codul ar fi rigid și greu de extins
 +
 +====Reflection ≠ programare normală====
 +
 +Reflection **nu înlocuiește** programarea clasică. Este:
 +  * mai lentă
 +  * mai greu de citit
 +  * mai periculoasă
 +
 +De aceea este folosită:
 +  * **rar**
 +  * **controlat**
 +  * **în infrastructură**,​ nu în logica de business
 +
 +====Obiectul Class – punctul de intrare====
 +
 +Orice operație de Reflection pornește de la un obiect de tip ''​Class<?>''​.
 +
 +Există trei modalități principale de a obține un ''​Class'':​
 +
 +<code java>
 +Class<​String>​ c1 = String.class;​
 +Class<?>​ c2 = "​hello"​.getClass();​
 +Class<?>​ c3 = Class.forName("​java.lang.String"​);​
 +</​code>​
 +
 +Toate cele trei referințe indică **aceeași clasă la runtime**.
 +
 +====Inspectarea structurii unei clase====
 +
 +Presupunem următoarea clasă:
 +<code java>
 +public class Person {
 +    private String name;
 +    private int age;
 +
 +    public Person(String name, int age) { }
 +
 +    public String getName() { return name; }
 +}
 +</​code>​
 +
 +**Câmpuri**
 +<code java>
 +Field[] fields = Person.class.getDeclaredFields();​
 +
 +for (Field f : fields) {
 +    System.out.println(f.getName());​
 +}
 +</​code>​
 +
 +<spoiler output>
 +<code bash>
 +name
 +age
 +</​code>​
 +</​spoiler>​
 +
 +**Metode**
 +<code java>
 +Method[] methods = Person.class.getDeclaredMethods();​
 +
 +for (Method m : methods) {
 +    System.out.println(m.getName());​
 +}
 +</​code>​
 +
 +**Constructori**
 +<code java>
 +Constructor<?>​[] constructors = Person.class.getDeclaredConstructors();​
 +
 +for (Constructor<?>​ c : constructors) {
 +    System.out.println(c);​
 +}
 +</​code>​
 +
 +====Accesarea membrilor private====
 +
 +Reflection permite accesarea membrilor ''​private'',​ **ocolind encapsularea**.
 +
 +<code java>
 +Person p = new Person("​Ana",​ 30);
 +
 +Field field = Person.class.getDeclaredField("​name"​);​
 +field.setAccessible(true);​
 +
 +String value = (String) field.get(p);​
 +System.out.println(value);​
 +</​code>​
 +
 +<note warning>​Acest lucru rupe principiile OOP și trebuie folosit **doar în infrastructură sau framework-uri**. </​note>​
 +
 +====Invocarea metodelor prin Reflection====
 +
 +<code java>
 +Method m = Person.class.getDeclaredMethod("​getName"​);​
 +String result = (String) m.invoke(p);​
 +
 +System.out.println(result);​
 +</​code>​
 +
 +Reflection permite:
 +  * apelarea metodelor necunoscute la compilare
 +  * decuplarea codului
 +
 +====Crearea obiectelor dinamic====
 +
 +<code java>
 +Constructor<​Person>​ ctor =
 +    Person.class.getConstructor(String.class,​ int.class);
 +
 +Person p = ctor.newInstance("​Ion",​ 25);
 +</​code>​
 +
 +Acesta este mecanismul folosit de:
 +  * Jackson
 +  * Hibernate
 +  * Spring
 +
 +====Reflection și Adnotări====
 +
 +Reflection este mecanismul prin care **adnotările RUNTIME sunt citite**.
 +
 +Exemplu:
 +<code java>
 +@Retention(RetentionPolicy.RUNTIME)
 +@interface Info {
 +    String value();
 +}
 +</​code>​
 +
 +<code java>
 +@Retention(RetentionPolicy.RUNTIME)
 +@interface Info {
 +    String value();
 +}
 +</​code>​
 +
 +Citire la runtime:
 +<code java>
 +Info info = Person.class.getAnnotation(Info.class);​
 +System.out.println(info.value());​
 +</​code>​
 +
 +====Reflection în framework-uri reale====
 +
 +Reflection este folosit pentru:
 +  * scanarea claselor
 +  * identificarea adnotărilor
 +  * apelarea metodelor automat
 +
 +Exemple:
 +  * JUnit → caută metode cu @Test
 +  * Jackson → caută câmpuri și constructori
 +  * Spring → injectează dependențe
 +
 +====Costuri și riscuri====
 +
 +Reflection:
 +  * este mai lentă decât apelurile directe
 +  * elimină verificările de tip la compilare
 +  * poate produce erori doar la runtime
 +
 +<note tip>
 +Reflection este o unealtă de **framework**,​ nu de business logic. În practică, o vom folosi **doar la testarea codului nostru**.
 +</​note>​
 +
 +
 +=====🔍 Assertions=====
 +
 +====Ce sunt assertions?​====
 +
 +**Assertions** sunt afirmații folosite pentru a verifica **ipoteze interne** ale programului în timpul execuției. Ele exprimă condiții care **ar trebui să fie mereu adevărate** dacă programul funcționează corect.
 +
 +Aserțiile **nu sunt mecanisme de tratare a erorilor** și **nu înlocuiesc excepțiile**.
 +
 +====Scopul assertions====
 +
 +Assertions sunt folosite pentru:
 +  * detectarea bug-urilor logice
 +  * validarea presupunerilor interne
 +  * documentarea implicită a codului
 +
 +====Sintaxa de bază====
 +
 +<code java>
 +assert condition;
 +</​code>​
 +
 +Sau cu mesaj:
 +
 +<code java>
 +assert condition : "Mesaj de eroare";​
 +</​code>​
 +
 +<note important>​
 +Dacă ''​condition''​ este ''​false'',​ JVM aruncă un ''​AssertionError''​.
 +</​note>​
 +
 +====Exemplu simplu====
 +
 +<code java>
 +public int divide(int a, int b) {
 +    assert b != 0 : "b must not be zero";
 +    return a / b;
 +}
 +</​code>​
 +
 +Dacă ''​b''​ este ''​0'',​ programul va eșua **doar dacă assertions sunt activate**.
 +
 +====Activarea assertions====
 +
 +Assertions sunt **dezactivate implicit**.
 +
 +Se activează la rulare:
 +<code java>
 +java -ea Main
 +</​code>​
 +
 +<note important>​ Dacă assertions nu sunt activate, **codul assert nu se execută deloc**. </​note>​
 +
 +====Assertions vs Excepții====
 +
 +^ Assertions ​                      ^ Excepții ​               ^
 +| pentru bug-uri interne ​          | pentru erori de runtime |
 +| pot fi dezactivate ​              | sunt mereu active ​      |
 +| nu se folosesc pentru input user | validează input extern ​ |
 +
 +
 +=====🔬 Unit Testing=====
 +
 +====Ce este Unit Testing?​====
 +
 +**Unit testing** reprezintă procesul de testare a **celor mai mici unități de cod** (de regulă metode sau clase) în **izolare** față de restul aplicației.
 +
 +Scopul este verificarea faptului că:
 +  * o metodă produce rezultatul corect
 +  * pentru aceleași intrări, comportamentul este determinist
 +  * modificările ulterioare nu introduc regresii
 +
 +Unit testing-ul se concentrează pe **corectitudinea logicii**, nu pe integrarea componentelor.
 +
 +====Ce este o „unitate”?​====
 +
 +În Java, o unitate este de obicei:
 +  * o metodă publică
 +  * o clasă cu responsabilitate clară
 +  * o funcție pură (fără dependențe externe)
 +
 +Exemplu de unitate bună:
 +<code java>
 +int add(int a, int b)
 +</​code>​
 +
 +Exemplu de unitate slabă:
 +<code java>
 +void saveUserAndSendEmailAndLog()
 +</​code>​
 +
 +====Caracteristicile unui test unitar bun====
 +
 +Un test unitar corect este:
 +  * rapid (milisecunde)
 +  * determinist (același rezultat de fiecare dată)
 +  * independent (nu depinde de alte teste)
 +  * izolat (fără DB, rețea, fișiere)
 +
 +<note important>​ Dacă testul depinde de o bază de date sau de o cerere HTTP către orice resursă online, atunci **nu mai este unit test**. </​note>​
 +
 +====Framework standard: JUnit====
 +
 +În Java, standardul de facto pentru unit testing este JUnit.
 +
 +Un test JUnit este:
 +  * o metodă
 +  * marcată cu @Test
 +  * care validează un comportament folosind assertions
 +
 +====Structura unui test JUnit====
 +
 +<code java>
 +class CalculatorTest {
 +
 +    @Test
 +    void additionWorks() {
 +        Calculator c = new Calculator();​
 +        int result = c.add(2, 3);
 +
 +        assertEquals(5,​ result);
 +    }
 +}
 +</​code>​
 +
 +Elemente cheie:
 +  * ''​@Test''​ marchează metoda ca test
 +  * ''​assertEquals''​ verifică rezultatul
 +  * dacă assert-ul eșuează → testul eșuează
 +
 +====Assertions în JUnit====
 +
 +JUnit oferă propriul set de assertions:
 +<code java>
 +assertEquals(expected,​ actual);
 +assertTrue(condition);​
 +assertFalse(condition);​
 +assertNotNull(object);​
 +assertThrows(Exception.class,​ () -> code);
 +</​code>​
 +
 +Exemplu:
 +<code java>
 +assertThrows(IllegalArgumentException.class,​
 +             () -> calculator.divide(10,​ 0));
 +</​code>​
 +
 +====Pattern-ul AAA (Arrange – Act – Assert)====
 +
 +Majoritatea testelor respectă structura:
 +<code java>
 +@Test
 +void exampleTest() {
 +    // Arrange
 +    Calculator c = new Calculator();​
 +
 +    // Act
 +    int result = c.add(2, 3);
 +
 +    // Assert
 +    assertEquals(5,​ result);
 +}
 +</​code>​
 +
 +Această structură:
 +  * clarifică intenția testului
 +  * îmbunătățește lizibilitatea
 +  * ușurează mentenanța
 +
 +====Setări comune: @BeforeEach / @AfterEach====
 +
 +Pentru inițializare și cleanup:
 +<code java>
 +@BeforeEach
 +void setup() {
 +    calculator = new Calculator();​
 +}
 +
 +@AfterEach
 +void clear() {
 +    calculator.reset();​
 +}
 +</​code>​
 +
 +Aceste metode:
 +  * rulează **înainte / după fiecare test**
 +  * evită duplicarea codului
 +  * păstrează testele independente
 +
 +====Unit testing vs Assertions====
 +
 +^ Unit Testing ​    ^ Assertions ​        ^
 +| testare automată | verificări interne |
 +| rulează mereu    | pot fi dezactivate |
 +| parte din CI     | doar pentru debug  |
 +| validează API    | validează ipoteze ​ |
 +
 +<note warning>
 +Interacțiunile cu:
 +  * baze de date
 +  * rețea
 +  * filesystem
 +  * servicii externe
 +
 +Acestea aparțin:
 +  * integration testing
 +  * system testing
 +</​note>​
 +
 +=====🧪 Integration Testing & Mocking=====
 +
 +====Ce este Integration Testing?​====
 +
 +**Integration testing** verifică dacă **mai multe componente ale aplicației funcționează corect împreună**,​ nu doar în izolare.
 +
 +Spre deosebire de unit testing:
 +  * nu testăm o metodă izolată
 +  * testăm colaborarea dintre clase, module sau layere
 +
 +Exemple:
 +  * service + repository
 +  * controller + service
 +  * service + client HTTP
 +
 +====Unit Tests vs Integration Tests====
 +
 +^ Unit Testing ​       ^ Integration Testing ​       ^
 +| testează o unitate ​ | testează colaborarea ​      |
 +| izolat ​             | dependențe reale           |
 +| foarte rapid        | mai lent                   |
 +| fără infrastructură | poate necesita DB / config |
 +
 +<note important>​ Integration testing **nu înseamnă** „testăm tot sistemul”. </​note>​
 +
 +====Niveluri de integrare====
 +
 +În practică există mai multe grade:
 +  * integrare **parțială** (mock pentru unele dependențe)
 +  * integrare **reală** (DB real, filesystem, config)
 +
 +Laboratoarele se concentrează de obicei pe **integrare parțială**,​ pentru control și viteză.
 +
 +====De ce avem nevoie de Mocking?​====
 +
 +Mocking permite **simularea comportamentului unei dependențe**,​ fără a o executa efectiv.
 +
 +Folosim mocking când:
 +  * dependența este lentă
 +  * dependența este externă
 +  * vrem să controlăm exact comportamentul
 +
 +====Mockito – framework de mocking====
 +
 +**Mockito** este standardul de facto în Java pentru mocking.
 +
 +Cu Mockito putem:
 +  * crea obiecte mock
 +  * defini comportamentul lor
 +  * verifica interacțiunile
 +
 +====Exemplu simplu de mocking====
 +
 +<code java>
 +UserRepository repo = mock(UserRepository.class);​
 +
 +when(repo.findById(1L))
 +    .thenReturn(new User("​Ana"​));​
 +</​code>​
 +
 +Ce se întâmplă:​
 +  * ''​repo''​ NU este implementare reală
 +  * metoda ''​findById''​ este simulată
 +  * nu există DB implicat
 +
 +====Integrare parțială cu Mockito====
 +
 +<code java>
 +class UserServiceTest {
 +
 +    UserRepository repo = mock(UserRepository.class);​
 +    UserService service = new UserService(repo);​
 +
 +    @Test
 +    void returnsUserName() {
 +        when(repo.findById(1L))
 +            .thenReturn(new User("​Ana"​));​
 +
 +        String name = service.getUserName(1L);​
 +
 +        assertEquals("​Ana",​ name);
 +    }
 +}
 +</​code>​
 +
 +Aici testăm:
 +  * logica din ''​UserService''​
 +  * integrarea cu repository-ul (simulat)
 +
 +====Verificarea interacțiunilor====
 +
 +Mockito permite verificarea apelurilor:
 +<code java>
 +verify(repo).findById(1L);​
 +</​code>​
 +
 +Acest lucru este util când:
 +  * ordinea apelurilor contează
 +  * vrem să verificăm efecte indirecte
 +
 +====Observații importante====
 +
 +  * Nu se face mock pentru **clasa testată**
 +  * Se fac mock doar pentru **dependințe**
 +  * Mock-urile nu trebuie suprafolosite
 +  * Testele cu prea multe mock-uri devin fragile
 +
 +====[Nice to know] Folosirea lui var în teste====
 +
 +În Java, ''​var''​ permite **inferarea tipului la compilare**,​ reducând zgomotul sintactic, fără a pierde siguranța tipurilor.
 +
 +
 +<note important>​ ''​var''​ **nu este tip dinamic**. Tipul este determinat **o singură dată**, la compilare. </​note>​
 +
 +===Exemplu cu Mockito + var===
 +
 +Fără ''​var'':​
 +<code java>
 +UserRepository repository = mock(UserRepository.class);​
 +UserService service = new UserService(repository);​
 +</​code>​
 +
 +Cu var:
 +<code java>
 +var repository = mock(UserRepository.class);​
 +var service = new UserService(repository);​
 +</​code>​
 +
 +Codul este:
 +  * mai scurt
 +  * la fel de sigur
 +  * mai lizibil în contextul testelor
 +
 +<note important>​
 +Deși ''​var''​ poate fi util în scrierea rapidă a codului, trebuie să avem grijă să nu îl folosim în următoarele situații:
 +  * câmpuri
 +  * parametri
 +  * API public
 +  * când tipul nu e clar din context
 +</​note>​
 +
 +=====[Nice to know] 🪵 Logging în Java =====
 +
 +==== Ce este logging-ul? ====
 +
 +Logging-ul reprezintă mecanismul prin care o aplicație înregistrează informații despre:
 +  * execuția codului
 +  * evenimente importante
 +  * erori sau situații neașteptate
 +
 +Spre deosebire de ''​System.out.println'',​ logging-ul:
 +  * poate fi configurat (nivel, format, destinație)
 +  * poate fi dezactivat fără a modifica codul
 +  * este standard în aplicații profesionale
 +
 +====De ce NU folosim System.out.println?​====
 +
 +Instrucțiunea ''​System.out.println'':​
 +  * nu are niveluri (info, warning, error)
 +  * nu poate fi filtrat pe baza nivelelor
 +  * nu este potrivit pentru producție
 +
 +====Logging standard în Java (java.util.logging)====
 +
 +Java oferă un mecanism de logging în JDK, fără biblioteci externe ''​java.util.logging''​.
 +
 +Exemplu minimal:
 +<code java>
 +import java.util.logging.Logger;​
 +
 +public class Example {
 +    private static final Logger logger =
 +        Logger.getLogger(Example.class.getName());​
 +
 +    void run() {
 +        logger.info("​Application started"​);​
 +    }
 +}
 +</​code>​
 +
 +Niveluri de logging (cele mai uzuale)
 +
 +  * ''​SEVERE''​ / ''​ERROR''​ – erori critice
 +  * ''​WARNING''​ – situații problematice
 +  * ''​INFO''​ – informații generale
 +  * ''​FINE''​ / ''​DEBUG''​ – detalii de execuție
 +
 +<note important>​ Logging-ul NU trebuie să schimbe comportamentul aplicației,​ **doar să-l observe**. </​note>​
 +
 +====Logging folosind Log4j====
 +
 +Pe lângă mecanismul de logging din JDK (java.util.logging),​ în practică este foarte des întâlnită biblioteca Apache Log4j.
 +
 +Log4j este:
 +  * o bibliotecă externă de logging (nu face parte din JDK)
 +  * mai flexibilă și mai configurabilă decât logging-ul standard din Java
 +  * folosită frecvent în aplicații enterprise și framework-uri
 +
 +Un exemplu de inițializare:​
 +<code java>
 +import org.apache.logging.log4j.LogManager;​
 +import org.apache.logging.log4j.Logger;​
 +
 +public class Example {
 +    private static final Logger logger =
 +        LogManager.getLogger(Example.class);​
 +
 +    void run() {
 +        logger.info("​Application started"​);​
 +    }
 +}
 +</​code>​
 +
 +<note warning> Log4j a fost implicat în vulnerabilitatea **Log4Shell (2021)**, una dintre cele mai grave din istoria Java. Acest lucru NU înseamnă că logging-ul este periculos, ci că **bibliotecile trebuie actualizate și verificate pentru vulnerabilități**. În prezent, există utilitare care fac acest lucru automat cum ar fi [[https://​snyk.io/​|Snyk]]</​note>​
 +
 +<note tip>
 +  * ''​java.util.logging''​ → suficient pentru laborator și aplicații simple
 +  * Log4j → standard de facto în aplicații mari
 +  * ''​System.out.println''​ → nu este logging
 +</​note>​
 +
 +=====[Optional] 🗺️ Software Development Methodologies=====
 +
 +În urma paradigmei OOP, au fost concepute mai multe metodologii care pot augmenta performanța sau anumite abilități ale unui proiect conform necesităților.
 +
 +De exemplu, **Data Oriented Design** (DOD) este o paradigmă care ajută programarea folosind obiecte prin folosirea **cache-ului**. Practic, clasele sunt refactorizate astfel încât să țină cont de mărimea cache-ului din procesor pentru a scădea drastic timpii de acces. Această paradigmă începe să fie destul de folosită în mediile în care viteza este importantă,​ cum ar fi programarea jocurilor video, drept urmare chiar și engine-ul Unity a fost rescris astfel încât să folosească DOD (găsiți articolul [[https://​unity.com/​dots|aici]].
 +
 +<note tip>
 +Vă recomandăm să parcurgeți secvențe din acest videoclip de la [[https://​www.youtube.com/​watch?​v=yy8jQgmhbAU|CppCon]].
 +</​note>​
 +
 +O altă metodologie utilă este **Test Driven Development** (TDD) în care accentul este pus pe dezvoltarea testelor înainte de a dezvolta codul propriu-zis. Practic, după etapa de ideație, putem trece la crearea testelor care vor defini comportamentul corect al programului nostru, apoi vom dezvolta codul necesar pentru a putea trece cu succes peste testele scrise de noi. Puteți citi mai multe despre această abordare [[https://​agilealliance.org/​glossary/​tdd/​|aici]].
 +
 +De asemenea, o altă metodologie folosită este **Domain Driven Design** (DDD) care este o abordare de proiectare software în care structura aplicației este ghidată de domeniul de business, iar logica esențială este modelată prin concepte clare (entități,​ valori, agregate) definite împreună cu experți din domeniu, folosind un limbaj comun (ubiquitous language) pentru a menține codul aliniat cu realitatea problemei. Această abordare este detaliată în articolul scris de Redis [[https://​redis.io/​glossary/​domain-driven-design-ddd/​|aici]].
 +=====Exerciții=====
 +
 +<note tip>
 +Pentru exercițiile de mai jos va trebui să vă creați propriile proiecte Maven. Vă recomandăm să luați dependențele necesare de pe [[https://​mvnrepository.com/​repos/​central|Maven Repository]],​ concret JUnit și Mockito.
 +
 +De asemenea, vă recomandăm să parcurgeți documentația Mockito și JUnit sau să folosiți ghiduri de pe site-uri de specialitate cum ar fi [[https://​www.baeldung.com/​mockito-series|Baeldung Mockito series]].
 +</​note>​
 +
 +<note important>​
 +Acest laborator nu va fi încărcat pe Devmind Code, deoarece va fi verificat manual de către laborantul vostru.
 +</​note>​
 +====Task 1 – Unit Testing cu coverage ≥ 80% (6p)====
 +
 +Se dă următoarea clasă de producție, care modelează un serviciu simplu de procesare a comenzilor. Codul conține:
 +  * ramificații (if)
 +  * excepții
 +  * assertions
 +  * logică dependentă de input
 +
 +<code java>
 +public class OrderService {
 +
 +    public double calculateTotal(double price, int quantity) {
 +        assert price >= 0 : "Price must be non-negative";​
 +        assert quantity > 0 : "​Quantity must be positive";​
 +
 +        return price * quantity;
 +    }
 +
 +    public double applyDiscount(double total, double discountPercent) {
 +        if (discountPercent < 0 || discountPercent > 50) {
 +            throw new IllegalArgumentException("​Invalid discount"​);​
 +        }
 +
 +        return total - (total * discountPercent / 100);
 +    }
 +
 +    public String categorizeOrder(double total) {
 +        if (total < 100) {
 +            return "​SMALL";​
 +        } else if (total < 500) {
 +            return "​MEDIUM";​
 +        } else {
 +            return "​LARGE";​
 +        }
 +    }
 +
 +    public boolean isFreeShipping(double total) {
 +        return total >= 200;
 +    }
 +}
 +</​code>​
 +
 +Scrieți teste unitare folosind JUnit 5 astfel încât:
 +  * coverage-ul clasei să fie ≥ 80%
 +  * toate ramurile importante să fie testate
 +  * testele să fie clare, independente și deterministe
 +
 +<note tip>
 +În industrie, testarea este importantă,​ iar o metrică consacrată în acest sens este ca 80% din cod să fie acoperit de teste. Pentru a verifica cât cod este acoperit de testele voastre puteți folosi [[https://​www.jetbrains.com/​help/​idea/​code-coverage.html|ghidul oficial IntelliJ]] sau să întrebați laborantul.
 +</​note>​
 +
 +<note important>​
 +Pentru a testa cât mai mult cod, trebuie să generați teste valide și invalide pentru codul dat, astfel încât să testați toate ramurile logice.
 +</​note>​
 +
 +====Task 2 - Integration Test + Mocking (4p)====
 +
 +Se dau următoarele clase:
 +<code java User.java>​
 +public class User {
 +    private final String name;
 +
 +    public User(String name) {
 +        this.name = name;
 +    }
 +
 +    public String getName() {
 +        return name;
 +    }
 +}
 +</​code>​
 +
 +<code java UserRepository.java>​
 +public interface UserRepository {
 +    User findById(long id);
 +}
 +</​code>​
 +
 +<code java UserService.java>​
 +public class UserService {
 +
 +    private final UserRepository repository;
 +
 +    public UserService(UserRepository repository) {
 +        this.repository = repository;
 +    }
 +
 +    public String getUserName(long id) {
 +        User user = repository.findById(id);​
 +        return user.getName();​
 +    }
 +}
 +</​code>​
 +
 +  - Scrieți un test pentru UserService folosind Mockito.
 +  - Mock-uiți UserRepository astfel încât:
 +    * pentru id = 1, să returneze un User("​Ana"​)
 +  - Verificați că metoda getUserName(1) returnează "​Ana"​.
 +  ​
 +Puteți folosi ''​var''​ pentru:
 +  * repository
 +  * service
 +  * rezultatul final
 +
 +<note tip>​Denumirea și scopul claselor de mai sus respectă un standard folosit de framework-ul Spring.</​note>​
 +
 +===== Resurse și link-uri utile =====
 +  * [[https://​site.mockito.org/​ | Mockito]] ​
 +  * [[https://​www.atlassian.com/​continuous-delivery/​software-testing/​types-of-software-testing | Different types of software testing]]
 +  * [[https://​www.baeldung.com/​java-logging-intro | Introduction to Java logging - Baeldung]]
 +  * [[https://​softwareengineering.stackexchange.com/​questions/​123956/​why-should-i-use-reflection | Why should I use reflection and when? - StackOverflow]]
 +
  
poo-ca-cd/laboratoare/programare-avansata-java.1768150273.txt.gz · Last modified: 2026/01/11 18:51 by florian_luis.micu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0