Sunteți pe pagina 1din 8

Programarea Orientata Spre Obiecte, Limbajul Java, 2019-2020 CEITI

Tema4. Clase pentru intrări și ieșiri. Serializarea și deserializarea obiectelor..


Ce reprezintă fluxurile de intrare și fluxurile de ieșire?
În Java operațiile de intrare / ieșire au la bază termenul de stream (flux) introdus pentru prima dată
de Dennis Ritchie care în 1984 implementează primul sistem I/O pe baza de stream în cadrul S.O.
Unix. Ideea de stream are la bază crearea unui canal de comunicaţie între două entităţi. Canalul
permite trecerea unui flux de date într-o singură direcţie. Deoarece există două direcţii de
comunicare, există două tipuri mari de stream-uri: de intrare (input stream) și de ieșire(output
stream). Un flux care citește date se numește de intrare, iar cel care scrie date se numește de ieșire.
Java oferă mai multe clase și interfețe pentru lucrul cu fluxurile de intrare /ieșire grupate în pachetul
în pachetul java.io.
Majoritatea metodelor claselor I/O pot genera excepții care trebuie tratate în 2 moduri :
declararea excepției în metodă folosind bloc try-catch
public static void main(String [] args) public static void main(String [] args) {
throws IOException { try {
//operatiile intrare /iesire //operatiile intrare /iesire
} } catch (IOException ex){ ... }
}

Care este ierarhia claselor pachetului java.io?


Anexa
Care este diferența dintre fluxurile de octeți și fluxurile de caractere?
Fluxurile de date atât cele de intrare cât și cele de ieșire, sunt de două tipuri fluxuri de
octeți şi fluxuri de caractere. Pentru a putea face o diferență între aceste două tipuri de fluxuri vom
aminti care sunt fișierele text și fișierele binare.
Un fişier care poate fi procesat folosind un editor de text, ca de exemplu Notepad, este un fişier text.
Toate celelalte fişiere sunt numite binare. Acestea nu pot fi editate cu un editor de text şi sunt create
pentru a fi citite de programe anumite.
De exemplu, fișierul sursă *.java este stocat într-un fişier text care poate fi citit cu orice editor de
text, iar fișierul *.class este stocat într-un fişier binar care este citit de JVM.
Calculatoarele nu fac diferență între cele două tipuri de fişiere. Toate fișierele sunt stocate în format
binar. Caracterele I/O sunt construite în baza datelor fluxurilor I/O binare care au fost codificate
automat. JVM codifică /decodifică caracterele Unicode atunci cînd le scrie/citeşte în/din fişiere. De
aceea fluxurile I/O binare sunt mai eficiente decît fluxurile I/O bazate pe caractere.

opreavictoria86@gmail.com © MUSTEAȚĂ Victoria


1
Programarea Orientata Spre Obiecte, Limbajul Java, 2019-2020 CEITI
De exemplu, dorim să scriem în fişier textul 199 folosind fluxurile bazate pe caractere. Caracterele
sunt scrise fiecare pe rînd. În Unicode caracterul 1 este 0x0031, acesta va fi convertit într-un cod ce
depinde de schema de codificare pentru fişiere a SO (0x semnifică un număr hexazecimal). În Satele
Unite, schema default de codificare pentru fişiere text în Windows este ASCII. Codul ASCII al
caracterului 1 este 49 (0x31 în hexazecimal), 9 este 57(0x39 în hexazecimal). Deci pentru a scrie 199
se vor folosi 3 bytes: 0x31,0x39 şi 0x39. Figura (a) de mai sus.

Fluxurile I/O binare (la nivel de octeți) nu necesită conversii de cod. Dacă vom scrie o valoare
numerică folosind fluxuri binare valoare exactă din memorie va fi transferată în fişier.
De exemplu, valoare în byte a 199 în memorie este 0XC7, exact așa va apărea în fișierul binar. Figura
(b) de mai sus.

În general , trebuie să folosim fluxuri I/O bazate pe caractere atunci cînd lucram cu fişiere create de
editoare text şi fluxuri I/O binare pentru lucrul cu fișierele binare create de JVM.
Fișierele binare sunt independente de schema de codificare a SO ceea ce oferă portabilitate.
Programele java pe orice calculator pot citi fișierele binare create de java.
De aceea clasele java sunt fişiere binare. Fișierele java *.class pot rula pe JVM pe orice calculator.

Cum transmitem/citim date din fișier cu ajutorul fluxurilor de caractere?


Vom opera cu ajutorul claselor FileReader pentru a citi date din fișier și FileWriter pentru a
transmite date în fișier:
Exemplul1. Se vor citi datele din fișierul in.txt și se vor scrie în fișierul out.txt cu ajutorul fluxurilor
de caractere. Citirea din fișier se va realiza folosind clasa Scanner discutată la lecțiile precedente:
import java.io.*;
import java.util.Scanner;
class Test_File_Working{
public static void main ( String [] args ){
Scanner in;
FileWriter out;
try {
in = new Scanner(new FileReader("in.txt"));
out = new FileWriter("out.txt");
while (in.hasNext())
out.write(Integer.toString(in.nextInt())+" ");
in.close();
out.close();
} catch (FileNotFoundException e) {
System.out.println("Oooops fisierul de intrare nu exista!");
} catch (IOException e) {
System.out.println("Nu se poate realiza transferul datelor in fisier!");
}}
}

Exemplul2. Citiți de la tastatură o secvență de numere. Introduceți într-un fișier doar numerele divizibile la 5.
import java.util.*;
import java.io.*;
public class Individual_1 {
opreavictoria86@gmail.com © MUSTEAȚĂ Victoria
2
Programarea Orientata Spre Obiecte, Limbajul Java, 2019-2020 CEITI
public static void main (String [] args) throws IOException{
Writer out = new FileWriter("out.txt");
Scanner in = new Scanner(System.in);
System.out.println("Cite numere ? ");
int n = in.nextInt();
for(int i=1; i<=n; i++){
int x = in.nextInt();
if (x%5==0) out.write(Integer.toString(x)+" ");
} out.close(); in.close();}}

Exemplul3. Într-un fișier se află un șir de numere care


reprezintă elementele unui vector. Elaborați un program
Java care va citi elementele vectorului din fișier, va afișa
vectorul citit urmat de suma elementelor vectorului.
import java.util.*;
import java.io.*;
public class Individual_2 {
public static void main(String [] args) throws IOException{
Scanner in = new Scanner(new FileReader("out.txt"));
ArrayList<Integer> vector = new ArrayList<Integer>();
while (in.hasNext()){
vector.add(in.nextInt());
}
System.out.println("Am citit vectorul: ");
System.out.println(vector);
int s=0;
for(Integer i:vector) {s+=i;}
System.out.println("Suma elementelor este "+ s);
in.close(); }}
Ce este serializarea obiectelor?
În mod normal orice obiect creat în Java este distrus atunci cînd finisează programul care l-a creat,
sau atunci cînd nu mai este referință la obiect acesta este distrus automat de procesul de colectare
(Garbage Collector). Sunt multe situații în care datele cu care lucrează un program trebuie să aibă o
durată de viață mai mare decît a programului care le-a creat – trebuie sa supraviețuiască în afara
spațiului de adrese a unei JVM.
Pentru aceasta limbajul Java pune la dispoziția programatorilor noțiunea de serializare a obiectelor.
Definiție: Serializarea este procesul de transformare a obiectelor într-un şir de octeţi, din care să
poată fi refăcut ulterior obiectul original. Procesul invers, de citire a unui obiect serializat pentru a-i
reface starea originală, se numește deserializare.

În ce constă procesul de serializare?

opreavictoria86@gmail.com © MUSTEAȚĂ Victoria


3
Programarea Orientata Spre Obiecte, Limbajul Java, 2019-2020 CEITI
Pentru a putea serializa starea unui obiect este necesar ca clasa acestuia să implementeze interfața
Serializable folosind clasele de serializare şi fluxurile de ieşire la nivel de octeţi vom converti
într-o secvenţă de octeţi starea obiectului şi o vom salva fie într-un fişier, fie într-o bază de date sau
în memorie.

În ce constă procesul de deserializare?

Presupunem că avem deja un obiect serializat anterior, salvat într-un fişier sau BD sau memorie,
pentru a deserializa obiectul vom folosi clasele de deserializare şi fluxurile de intrare la nivel de octeţi
care reconstruiesc obiectul original.

Care sunt condițiile de serializare/deserializare a obiectelor?


1. Clasa trebuie să implementeze interfața java.io.Serializable, aceasta nu conţine nici o
metodă, nici atribute. Se folosește ca un indicator care anunță că clasa poate fi serializată, sau
este serializată într-un fişier, bază de date, sau trimisă pe rețea. Așa tip de interfețe poartă
numele de interfețe de marcare.
2. Toate cîmpurile în clasă trebuie să fie serializabile. Dacă un atribut nu este serializabil acesta
trebuie fie marcat transient. (transient = ignorat de procesul de serializare).
3. Toate tipurile de date primitive pot fi serializate.
4. Clasele care realizează serializarea/deserializarea sunt ObjectInputStream
(http://java.sun.com/j2se/1.5.0/docs/api/java/io/ObjectInputStream.html) şi
ObjectOutputStream(http://java.sun.com/j2se/1.5.0/docs/api/java/io/ObjectOutputStream.h
tml), metodele cele mai utilizate fiind writeObject(Object obj) şi readObject().
5. Toate clasele ce moștenesc o clasă serializată vor implementa implicit interfața Serializable,
adică nu este necesar de a declara că o clasă implementează interfață Serializable dacă una
dintre clasele moștenite au făcut deja acest lucru.
6. În situația în care dorim să declarăm o clasă serializabilă dar superclasa sa nu este serializabilă,
atunci trebuie să avem în vedere următoarele lucruri:
 Superclasa trebuie să aibă obligatoriu un constructor accesibil fără argumente, acesta fiind
utilizat pentru inițializarea variabilelor moștenite în procesul de restaurare al unui obiect.
Variabilele proprii vor fi inițializate cu valorile de pe fluxul de intrare. În lipsa unui astfel de
constructor accesibil pentru superclasă, va fi generată o excepție la execuție.
 Variabilele accesibile ale superclasei nu vor fi serializate, fiind responsabilitatea clasei curente
de a asigura un mecanism propriu pentru salvarea/ restaurarea lor.

Metode de a realiza serializarea/deserializarea


1. Utilizarea interfeței Serializable.
2. Utilizarea interfeței Externalizable.

opreavictoria86@gmail.com © MUSTEAȚĂ Victoria


4
Programarea Orientata Spre Obiecte, Limbajul Java, 2019-2020 CEITI

Exemplu de serializare folosind interfața Serializable

import java.io.*;
class Angajat implements Serializable {
private String nume;
private int ani;
public String getNume(){ return nume;}
public void setNume(String nume){this.nume=nume;}
public int getAni(){return ani;}
public void setAni(int ani){this.ani=ani;}
}
//serializare
class TestAngajat {
public static void main (String [] args){
Angajat persoana=new Angajat();
persoana.setNume("Tehnologii Moderne ");
persoana.setAni(4);
try {
FileOutputStream file=new FileOutputStream("out.ser");
ObjectOutputStream st= new ObjectOutputStream(file);
st.writeObject(persoana);
file.close();
}catch ( IOException ex){ ex.printStackTrace();}
}}
//deserializare
class TestDeserializareAngajat{
public static void main(String [] args){
try {
FileInputStream file=new FileInputStream("out.ser");
ObjectInputStream st= new ObjectInputStream(file);
Angajat persoana=(Angajat)st.readObject();
st.close();
file.close();
System.out.println("Numele angajatului:"+persoana.getNume());
System.out.println("Virsta angajatului :"+persoana.getAni());
}catch (IOException ex){ ex.printStackTrace();
} catch (ClassNotFoundException e) {e.printStackTrace();}}}

Dezavantajul mecanismului implicit de serializare este că algoritmul pe care se beazează, fiind creat
pentru cazul general, se poate comporta ineficient în anumite situaţii concrete: poate fi mult mai
lent decît este cazul sau reprezentarea binară generată pentru un obiect poate fi mult mai
voluminoasă decît ar trebui. În aceste situaţii, putem să înlocuim algoritmul implicit cu unul propriu,
particularizat pentru o clasă anume pentru ca clasa să poată avea mai mult control asupra serializării.
Acest lucru se realizează prin supradefinirea (într-o clasă serializabilă!) a metodelor
writeObject() şi readObject() avînd exact signatura :

private void writeObject(java.io.ObjectOutputStream stream) throws


IOException

private void readObject(java.io.ObjectInputStream stream)throws


IOException, ClassNotFoundException

opreavictoria86@gmail.com © MUSTEAȚĂ Victoria


5
Programarea Orientata Spre Obiecte, Limbajul Java, 2019-2020 CEITI
Metoda writeObject() controlează ce date sunt salvate, iar readObject() controlează modul
în care sunt restaurate obiectele, citind informaţiile salvate .
În corpul metodei writeObject() pentru a controla datele ce vor fi supuse serializării de vor
folosi metode ca stream.writeInt(…), stream.writeDouble(…), stream.UTF(…).
Notă

Analogic și în corpul metodei readObject() se vor folosi metode de tip


stream.readUTF(), stream.readInt(),ș.a
Interfaţa Serializable realizează serializarea implicită.

Exemplu de serializare folosind interfața Externalizable


Atunci cînd dorim un control mai amănunţit al serializării vom implementa interfaţa Externalizable
(extinde interfața Serializable) şi oferă un control complet al formatului extern al obiectului.
Definiţia acestei interfeţe este :
package java.io;
public interface Externalizable extends Serializable {
public void writeExternal(ObjectOutput out) throws IOException;
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException;
}
Deci clasele ce vor implementa această interfaţă trebuie obligatoriu să implementeze metodele
writeExternal() şi readExternal() în care se va face serializarea completă a obiectelor şi coordonarea
cu superclasa ei.
Interfaţa Externalizable este folosită în situaţii în care se doreşte îmbunătăţirea
performanţelor algoritmului standard, mai exact creşterea vitezei procesului de serializare.
import java.io .*;
class Persoana implements Externalizable {
int cod ; String nume ;
public Persoana(){}
public Persoana ( String nume , int cod ) {
this.nume = nume ; this.cod = cod;
}
public void writeExternal( ObjectOutput s)throws IOException {
s.writeUTF ( nume ); s.writeInt (cod );
}
public void readExternal ( ObjectInput s) throws ClassNotFoundException ,
IOException {
nume = s. readUTF (); cod = s. readInt ();
}}

class TestPersoanaSerializare {
public static void main (String [] args){
try {
Persoana om=new Persoana("Musteata Victoria",1001);

FileOutputStream file=new FileOutputStream("out1.ser");


ObjectOutputStream st= new ObjectOutputStream(file);
st.writeObject(om);
file.close();
}catch ( IOException ex){ ex.printStackTrace();
}}}
opreavictoria86@gmail.com © MUSTEAȚĂ Victoria
6
Programarea Orientata Spre Obiecte, Limbajul Java, 2019-2020 CEITI
class TestDeserializarePersoana{
public static void main(String [] args){
try {
FileInputStream file=new FileInputStream("out1.ser");
ObjectInputStream st= new ObjectInputStream(file);
Persoana om =(Persoana) st.readObject();
st.close();
file.close();
System.out.println("Numele angajatului:"+om.nume);
System.out.println("Virsta angajatului :"+om.cod);
}catch (IOException ex){ ex.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}}}

1. Clasele care nu implementează una din interfeţele menţionate nu vor putea fi


serializate sau deserializate.
2. Un câmp declarat transient nu va fi serializat restul cîmpurilor vor fi serializate.
Notă!

private String question;


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

De ce este utilă serializarea?


1. Asigură un mecanism simplu şi rapid de utilizat pentru salvarea şi restaurarea a datelor;
2. Asigură persistenţa obiectelor, adică obiectul poate exista şi între apelurile programelor care îl
folosesc, nu doar în momentul execuţiei programului care l-a creat. Acest lucru se realizează
prin serializarea obiectului şi scrierea lui pe disc înainte de terminarea unui program, apoi, la
relansarea programului, obiectul va fi citit de pe disc şi starea lui refacută.
3. Compensarea diferenţelor între sisteme de operare - transmiterea unor informaţii între
platforme de lucru diferite se realizează unitar, independent de formatul de reprezentare a
datelor.
4. Transmiterea datelor în reţea - Aplicaţiile ce rulează în reţea pot comunica între ele folosind
fluxuri pe care sunt trimise, respectiv recepţionate obiecte serializate.

La general serializarea se foloseşte atunci cînd avem nevoie ca datele încapsulate într-o instanţă
a unei clase să poată exista înafara timpului de execuţie a unei maşini virtuale.

opreavictoria86@gmail.com © MUSTEAȚĂ Victoria


7
Programarea Orientata Spre Obiecte, Limbajul Java, 2019-2020 CEITI
Anexa

Surse:
1. http://www.ms.sapientia.ro/~manyi/teaching/oop/oop_romanian/curs8/curs8.html
2. Java de la 0 la expert , pag.268
3. https://docs.oracle.com/javase/7/docs/api/java/io/Writer.html
4. https://docs.oracle.com/javase/7/docs/api/java/io/Reader.html
5. https://docs.oracle.com/javase/7/docs/api/java/io/PrintWriter.html
6. Y. Daniel Liang , Introduction to Java Programming - 9th Edition, 9th Edition, Capitol 19
7. http://www.arh.pub.ro/lab/poo/lab3.html
8. http://www.tutorialspoint.com/java/io/java_io_bufferedreader.htm
9. http://www.tutorialspoint.com/java/java_serialization.htm
10. Cristian Frăsinaru , Curs Practic de Java , pag . 177

opreavictoria86@gmail.com © MUSTEAȚĂ Victoria


8

S-ar putea să vă placă și