This is an old revision of the document!
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:
Operația inversă se numește deserializare și presupune refacerea obiectului original din această reprezentare.
Serializarea este folosită în special pentru:
Pentru ca un obiect să poată fi serializat de JVM folosind mecanismul nativ, clasa sa trebuie să implementeze interfața: java.io.Serializable.
public class User implements Serializable { private String name; private int age; }
Această interfață:
ObjectOutputStream și ObjectInputStream.
Dacă un obiect nu este serializabil, JVM aruncă NotSerializableException.
1. Clasa model
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 + ")"; } }
2. Serializarea unui obiect
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."); } }
3. Deserializarea unui obiect
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); } }
Nu toate câmpurile unui obiect ar trebui serializate. Exemple tipice:
Pentru a exclude un câmp de la serializare folosim cuvântul cheie transient.
public class Account implements Serializable { private String username; private transient String password; }
password ca fiind transient, acesta nu este salvat, iar după deserializare devine null.
Câmpul serialVersionUID identifică versiunea unei clase serializate.
private static final long serialVersionUID = 1L;
Acest identificator este folosit de JVM pentru a verifica dacă o clasă este compatibilă cu obiectul serializat, astfel putem garanta compatibilitatea operațiilor.
Jackson este o bibliotecă externă foarte populară care permite:
Spre deosebire de serializarea nativă:
Serializable,De ce este Jackson preferat în aplicațiile moderne?
Este standardul de facto pentru:
1. Dependința Jackson
Într-un proiect real (Maven), Jackson se adaugă astfel:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.16.1</version> </dependency>
2. Clasa model
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 + "}"; } }
3. Serializare: obiect → JSON
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); } }
password nu apare deoarece are adnotarea @JsonIgnore.
4. Deserializare: JSON → obiect
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); } }
years → age datorită @JsonPropertypassword este ignorat
5. Serializare în fișier și citire din fișier
Scriere în fișier
mapper.writeValue(new File("user.json"), user);
Citire din fișier
User user = mapper.readValue(new File("user.json"), User.class);
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:
Cu alte cuvinte, adnotările spun ce este codul, nu ce face.
Adnotările au apărut pentru a elimina:
Astăzi, ele sunt fundamentale în:
@AnnotationName public class Example { }
Adnotările pot avea parametri:
@JsonProperty("user_name") private String name;
sau mai mulți:
@MyAnnotation(value = "test", enabled = true)
Prin @Target putem controla unde este permisă adnotarea:
TYPE – clase, interfețeFIELD – câmpuriMETHOD – metodePARAMETER – parametriCONSTRUCTORLOCAL_VARIABLEExemplu:
@Target(ElementType.FIELD)
@Retention definește cât timp există adnotarea:
@Retention(RetentionPolicy.RUNTIME)
Tipuri:
SOURCE – doar la compilare (ex: @Override)CLASS – în bytecode, dar nu la runtimeRUNTIME – disponibilă prin reflection (cea mai importantă)
@Override public String toString() { ... }
@Deprecated public void oldMethod() { }
@SuppressWarnings("unchecked")
Jackson folosește adnotări pentru a controla serializarea:
@JsonIgnore private String password;
@JsonProperty("years") private int age;
Aceste adnotări:
ObjectMapper1. Definirea adnotării
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Important { String value() default ""; }
@interface
2. Utilizarea adnotării
public class Person { @Important("primary identifier") private String name; private int age; }
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()); } } } }
Java definește trei momente distincte în care adnotările pot fi citite sau folosite:
1. La compilare (compile-time)
Adnotările cu:
@Retention(RetentionPolicy.SOURCE)
sunt:
Exemplu:
@Override
Rol:
2. După compilare, înainte de rulare (build-time / processing)
Unele adnotări sunt procesate prin Annotation Processors (APT – Annotation Processing Tool).
Acestea:
Exemple reale:
@Getter, @Setter)
#define), ci un mecanism controlat de compilator.
3. La runtime (execuție)
Adnotările cu:
@Retention(RetentionPolicy.RUNTIME)
sunt:
Exemple:
@JsonProperty)@Test)@Autowired)Acesta este cel mai important caz pentru Java modern.
Reflection este mecanismul prin care un program Java poate:
Cu alte cuvinte, Reflection permite unui program să se auto-analizeze și să se auto-manipuleze în timpul execuției.
Reflection a fost introdus pentru a permite:
Fără Reflection:
Reflection nu înlocuiește programarea clasică. Este:
De aceea este folosită:
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:
Class<String> c1 = String.class; Class<?> c2 = "hello".getClass(); Class<?> c3 = Class.forName("java.lang.String");
Toate cele trei referințe indică aceeași clasă la runtime.
Presupunem următoarea clasă:
public class Person { private String name; private int age; public Person(String name, int age) { } public String getName() { return name; } }
Câmpuri
Field[] fields = Person.class.getDeclaredFields(); for (Field f : fields) { System.out.println(f.getName()); }
Metode
Method[] methods = Person.class.getDeclaredMethods(); for (Method m : methods) { System.out.println(m.getName()); }
Constructori
Constructor<?>[] constructors = Person.class.getDeclaredConstructors(); for (Constructor<?> c : constructors) { System.out.println(c); }
Reflection permite accesarea membrilor private, ocolind encapsularea.
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);
Method m = Person.class.getDeclaredMethod("getName"); String result = (String) m.invoke(p); System.out.println(result);
Reflection permite:
Constructor<Person> ctor = Person.class.getConstructor(String.class, int.class); Person p = ctor.newInstance("Ion", 25);
Acesta este mecanismul folosit de:
Reflection este mecanismul prin care adnotările RUNTIME sunt citite.
Exemplu:
@Retention(RetentionPolicy.RUNTIME) @interface Info { String value(); }
@Retention(RetentionPolicy.RUNTIME) @interface Info { String value(); }
Citire la runtime:
Info info = Person.class.getAnnotation(Info.class); System.out.println(info.value());
Reflection este folosit pentru:
Exemple:
Reflection: