You are on page 1of 6

Serializarea obiectelor

Facilitile legate de serializare/deserializare apar n pachetul java.io.

Necesitatea serializrii i deserializrii obiectelor


n multe situaii apare necesitatea ca unul sau mai multe obiecte s poat fi memorate pentru a fi folosite i dup ncheierea aplicaiei care le-a creat. Pentru aceasta starea lor trebuie salvat ntr-un mod care s permit refacerea lor ulterioar. Java ofer mecanismele de serializare i deserializare, care realizeaz tocmai salvarea, respectiv refacerea obiectelor. Una dintre aplicaiile evidente ale acestor mecanisme const n serializarea obiectelor pe un sistem, urmat de transmiterea lor la distan i deserializarea lor pe o nou main gazd pentru a fi folosite n continuare. Serializarea const n memorarea, ntr-un anumit format, a strii obiectelor ntr-un tablou de octei sau ntr-un fiier. Acestea au un caracter serial: pot accepta numai secvene de octei. De aici deriv denumirile de serializare/deserializare. Mai este evident c vor fi folosite operaii de intrare/ieire la nivel de octet. Facilitatea ca obiectele s poat "supravieui" i dup teminarea executrii programului care le-a creat se mai numete persistena datelor. n cele ce urmeaz ne vom ndrepta atenia n special asupra fiierelor ca fluxuri pentru salvarea/restaurarea obiectelor. n exemplele care urmeaz vor presupune c serializarea se va face n cadrul unui director, iar deserializarea se va realiza din alt director. Ele vor fi numite ad-hoc director surs i director destinaie i nu sunt neaprat distincte.

Un prim exemplu
Exemplul 1. Directorul surs conine clasa Angajat:
import java.io.*; public class Angajat implements Serializable { String nume; int varsta; int salariu; static String firma; public Angajat(String n, int v, int s) { nume=n; varsta=v; salariu=s; } public void print() { System.out.println("Firma:\t\t" + firma + "\n" + "Nume:\t\t" + nume + "\n" + "Varsta:\t\t" + varsta + "\n" + "Salariul:\t" + salariu + "\n"); } }

i clasa Serial1:
import java.io.*; public class Serial1 { public static void main(String[] sir) throws IOException { Angajat.firma = "SRL Serial"; Angajat Unu = new Angajat("Vasile", 25, 1485); Angajat Doi = new Angajat("Ion", 24, 420); FileOutputStream fos = new FileOutputStream("Serial"); ObjectOutputStream oos = new ObjectOutputStream(fos); // oos.writeUTF(Angajat.firma); oos.writeObject(Unu); oos.writeObject(Doi); oos.close(); fos.close(); } }

Facem deocamdat abstracie de coninutul comentariilor de sfrit de linie. Clasa ObjectOutputStream extinde clasa abstract OutputStream. Prin invocarea metodei writeObject se realizeaz serializarea (ntr-un mod standard) a obiectelor create i scrierea rezultatului serializrii n fluxul de ieire. Clasa Angajat implementeaz interfaa Serializable. Prin abuz de limbaj vom numi clas serializabil o clas ce implementeaz aceast interfa. S reinem c numai instanele unei clase serializabile pot fi serializate. Mai observm c un flux de serializare poate conine mai multe obiecte. Presupunem n continuare c n directorul destinaie apar (de exemplu au fost copiate) fiierele Angajat i Serial, precum i urmtoarea clas, al crei nume este tot Serial:
import java.io.*; public class Serial2 { public static void main(String args[]) throws IOException { FileInputStream fis = new FileInputStream("Serial"); ObjectInputStream ois = new ObjectInputStream(fis); // Angajat.firma = ois.readUTF(); Angajat Unu = (Angajat) ois.readObject(); Angajat Doi = (Angajat) ois.readObject(); Unu.print(); Doi.print(); ois.close(); fis.close(); } }

Clasa ObjectInputStream extinde clasa abstract InputStream. Prin invocarea metodei readObject se realizeaz deserializarea (ntr-un mod standard) a obiectelor din fluxul de intrare. Metoda readObject ntoarce un obiect de tipul Object, ceea ce face necesar conversia explicit la tipul Angajat. S observm c la ieire, n dreptul numelui firmei apare null. Aceasta se datoreaz faptului c n mecanismul de serializare nu sunt incluse i cmpurile statice.

Explicaia const n aceea c serializarea se refer la obiecte, iar cmpurile statice sunt variabile de clas. Dac comentariile de sfrit de linie din clasele de mai sus devin instruciuni efective, atunci i cmpul static firma va fi inclus n procesul de serializare. Drept urmare, la ieire va aprea i numele firmei. Un cmp declarat cu transient acest modificator nu va fi inclus n procesul de serializare. Aceasta corespunde de exemplu situaiei n care la deserializare el nu mai este folosit. Dac pentru un anumit obiect dorim totui s transmitem i acest cmp, va trebui s o facem explicit, la fel ca pentru cmpurile statice.

Ce se transmite la serializare?
Rspundem parial, menionnd c nu sunt transmise: - cmpurile statice i cele cu modificatorul transient; - corpurile metodelor: este transmis numai signatura lor. Exemplul 2. Serializm un obiect al urmtoarei clase:
import java.io.*; public class UnuC implements Serializable { int x,y; UnuC(int a, int b) { x = a; y = b; } void print() { IO.writeln("Suma =\t" + (x+y)); } }

Dac la deserializare n directorul destinaie n metoda print a clasei UnuC nlocuim (x+y) prin (x*y), iar obiectul de tipul UnuC obinut prin deserializare invoc aceast metod, la ieire va aprea produsul (i nu suma) celor dou cmpuri. ntrebare: Cte obiecte sunt serializate la serializarea unui obiect? Intervine noiunea de graf asociat unui obiect. Graful asociat unui obiect const din obiectul respectiv, dar i din obiectele referite direct sau indirect de el. La serializarea unui obiect este serializat ntregul graf asociat obiectului respectiv. Aceast facilitate este foarte util i este ilustrat n continuare.

Exemplul 3. Serializarea unei liste circulare. Directorul surs conine clasa element: import java.io.*; class element implements Serializable { char c; element leg; static element p,u;
element() { } element(char ch) { c = ch; } void creare() { char ch = IO.readch(); p = new element(ch); u = p; ch = IO.readch(); while (ch != '$') { u = u.adaug(ch); ch = IO.readch(); } u.leg = p; } element adaug(char ch) { element x = new element(ch); leg = x; return x; } String parcurg(element x) { //if (x==null) return ""; //else if (x.leg == p) return x.c + ""; else return x.c + parcurg(x.leg); } }

precum i clasa Lista1:


import java.io.*; public class Lista1 { public static void main(String[] sir) throws Exception { element Ob = new element(); Ob.creare(); Ob = element.p; System.out.println(Ob.parcurg(Ob)); FileOutputStream fos=new FileOutputStream("lista"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(Ob); oos.close(); fos.close(); } }

Metoda principal a clasei Lista1 construiete o list nlnuit circular format din caracterele citite de la intrare pn la ntlnirea caracterului '$'. Capul listei apare n cmpul static p al clasei elem. La serializarea lui p sunt de fapt serializate toate elemetele (obiectele) din list, adic ntreg graful asociat acestui obiect. ntr-adevr, dac n directorul destinaie includem clasa element i urmtoarea clas Lista2:

import java.io.*; public class Lista2 { public static void main(String[] sir) throws Exception { FileInputStream fis = new FileInputStream("lista"); ObjectInputStream ois = new ObjectInputStream(fis); element p = (element) ois.readObject(); element.p = p; System.out.println( p.parcurg(p) ); ois.close(); fis.close();

} }

atunci la executarea metodei principale vor fi listate toate elementele listei. Exemplul de mai sus arat c serializarea "nu se ncurc" dac un obiect din graful asociat se autorefer. S menionm c n acest caz mecanismul de serializare "memoreaz" o singur dat un astfel de obiect.

Interfee i clase folosite la serializare/deserializare


Interfaa Serializable
public interface Serializable

Interfaa nu are nici metode, nici cmpuri. Rolul su este de a autoriza serializarea i deserializarea instanelor sale. Clasele ce extind aceast interfa sunt automat serializabile. La serializarea grafului asociat unui obiect, dac un obiect din acest graf nu este serializabil, atunci va fi lansat excepia NotSerializableException. Clasa ObjectOutputStream
public class ObjectOutputStream extends OutputStream implements DataOutput,ObjectOutput { public ObjectOutputStream(OutputStream os) throws IOException public final void writeObject(Object Ob) implementarea metodelor write din DataOutput, flush i close din OutputStream }

Un flux de ieire ce instaniaz aceast clas permite scrierea de date primitive i de grafuri asociate unor obiecte ntr-un mod standard, care permite reconstituirea lor folosind metodele clasei ObjectInputStream. Clasa ObjectInputStream
public class ObjectInputStream extends InputStream implements DataInput, ObjectInput {

public ObjectInputStream(InputStream is) public final Object readObject() throws ClassNotFoundException implementarea metodelor read din DataInput, available i close din InputStream

La deserializare sunt alocate noi adrese, prevenindu-se astfel scrierea peste obiecte existente. Drept urmare, serializarea i deserializarea pot fi realizate n cadrul aceluiai program, fr "pericolul" ca obiectul serializat i cel obinut prin deserializare s aib aceeai adres. La invocarea metodelor writeObject i readObject pentru un obiect se verific dac obiectul ce trebuie serializat/deserializat este o instan a unei clase ce conine aceste metode. n caz afirmativ, se ntreprind aciunile de mai sus, iar n caz contrar se ntreprinde aciunea standard.

You might also like