Serializarea obiectelor: Diferență între versiuni

De la WikiLabs
Jump to navigationJump to search
Linia 56: Linia 56:


== Citirea obiectelor ==
== Citirea obiectelor ==
Citirea obiectelor serializate se face utilizând un obiect de tip '''ObjectInputStream''' care se folosește de un '''InputStream''' generic. După cum spuneam mai sus, un obiect serializat este de fapt un șir de octeți ce reprezintă valorile fiecărui câmp (ne-static și ne-transient) al clasei. Acesta nu conține și informații despre clasă, prin urmare, ca un obiect să poată fi deserializat, aplicația care face deserializarea trebuie să aiba la dispoziție clasa a cărei instanță este obiectul deserializat. Dacă această clasă nu este disponibilă, atunci metoda ''readObject()'' va arunca o excepție de tip [http://docs.oracle.com/javase/7/docs/api/java/lang/ClassNotFoundException.html java.lang.ClassNotFoundException].
Verificarea clasei este făcută la runtime (în timpul execuției programului, la momentul apelării metodei ''readObject()'') și implică compararea unui câmp ''static final long serialVersionUID'' declarat în clasă, cu valoarea conținută de obiectul serializat. Prin urmare, nu este suficient ca două clase sa fie identice din punct de vedere al sursei Java, ca să fie compatibile la serializare, trebuie să aibă aceeași valoare a câmpului ''serialVersionUID''. Dacă nu este specificat de către programator, valoarea acestui câmp este generată aleator de compilator la compilarea clasei și va diferi pentru același cod compilat pe două mașini diferite:
<syntaxhighlight lang="java">
public class Question implements Serializable{
    static final long serialVersionUID = 0x0000CAFEBABE0000;
    public String questionBody;
    public String[] answers;
    public int correctAnswerIndex;
}
</syntaxhighlight>

Versiunea de la data 23 august 2012 12:04

În afară de fluxurile de I/O despre care s-a discutat în detaliu în capitolul de fluxuri, limbajul Java pune la dispoziția programatorilor o noțiune puternică de serializare a obiectelor. Știm deja că o clasă este o structură ce încapsulează date și funcționalitate. Serializarea este procedeul prin care datele încapsulate în instanța unei clase sunt trimise și primite cu ajutorul unui flux de I/O. Clasele care realizează acest lucru sunt java.io.ObjectOutputStream și java.io.ObjectInputStream iar metodele cele mai utilizate sunt writeObject(Object) și readObject().

Regulă: Pentru ca o clasă să poate fi serializată (sau mai bine zis, pentru ca obiectele instanțe ale acelei clase să poată fi serializate), clasa trebuie să implementeze interfața java.io.Serializable:


public class Question implements Serializable{

    public String questionBody;
    public String[] answers;
    public int correctAnswerIndex;

}

Scrierea obiectelor

Un ObjectOutputStream este, ca și clasa FilterOutputStream, un "wrapper", adică o înfășurătoare care are nevoie de un alt OutputStream pentru a funcționa. Rolul clasei ObjectOutputStream este de a transforma un obiect într-un șir de octeți care se scriu mai departe pe stream-ul la nivel de octet:

public class ObjectWriter{

public static void main(String[] _args){
    try{
        OutputStream _byteStream = getSomeOutputStream();
        ObjectOutputStream _objectStream = new ObjectOutputStream(_byteStream);
        Question _q1 = new Question();
        _objectStream.writeObject(_q1);
        _objectStream.close();
    }catch(IOException _ioe){
        System.out.println("Unable to write object: " + _ioe.getMessage());
    }
}

public static OutputStream getSomeOutputStream(){
    //...
}

}

Câmpurile statice nu sunt serializate (serializarea se referă la obiect și nu la clasă). Dacă obiectul serializat conține referințe la alte obiecte, acestea sunt și ele serializate automat urmând aceleaşi reguli.

În plus, pentru a proteja un câmp la serializare, se poate folosi modificatorul transient. Câmpurile transient nu se serializează:

public class Question implements Serializable{

    public String questionBody;
    public String[] answers;
    public transient int correctAnswerIndex;

}

Citirea obiectelor

Citirea obiectelor serializate se face utilizând un obiect de tip ObjectInputStream care se folosește de un InputStream generic. După cum spuneam mai sus, un obiect serializat este de fapt un șir de octeți ce reprezintă valorile fiecărui câmp (ne-static și ne-transient) al clasei. Acesta nu conține și informații despre clasă, prin urmare, ca un obiect să poată fi deserializat, aplicația care face deserializarea trebuie să aiba la dispoziție clasa a cărei instanță este obiectul deserializat. Dacă această clasă nu este disponibilă, atunci metoda readObject() va arunca o excepție de tip java.lang.ClassNotFoundException.

Verificarea clasei este făcută la runtime (în timpul execuției programului, la momentul apelării metodei readObject()) și implică compararea unui câmp static final long serialVersionUID declarat în clasă, cu valoarea conținută de obiectul serializat. Prin urmare, nu este suficient ca două clase sa fie identice din punct de vedere al sursei Java, ca să fie compatibile la serializare, trebuie să aibă aceeași valoare a câmpului serialVersionUID. Dacă nu este specificat de către programator, valoarea acestui câmp este generată aleator de compilator la compilarea clasei și va diferi pentru același cod compilat pe două mașini diferite:

public class Question implements Serializable{

    static final long serialVersionUID = 0x0000CAFEBABE0000;

    public String questionBody;
    public String[] answers;
    public int correctAnswerIndex;

}