Serialização de Objectos em Java

Neste artigo iremos falar sobre serialização de objectos em Java. Mas afinal o que é a serialização de objectos? A serialização de objectos é o processo de conversão de um objecto a uma sequência de bytes, assim como o processo que permite passar a sequência de bytes para um objecto utilizável e válido.

A serialização em Java é bastante simples e a API disponível pela SUN para este efeito é muito directa e intuitiva. No decorrer do artigo iremos ver exemplos práticos de serialização binária, e serialização de XML embora esta não faça parte dos padrões de serialização do Java.
Vamos começar por criar uma classe simples que irá depois ser usada para serialização.

1 import java.io.Serializable;
2
3 public class Exemplo1 implements Serializable{
4
5     private int numero;
6     private String nome;
7
8    public Exemplo1(int numero, String nome) {
9         this.numero = numero;
10        this.nome = nome;
11    }
12
13    public String getNome() {
14        return nome;
15    }
16
17    public int getNumero() {
18        return numero;
19    }
20
21    public String toString() {
22        return new String("Numero = "+this.numero+" | Nome = "+this.nome);
23    }
24 }

[Exemplo1.java]

Como podemos ver é uma classe bastante simples e nada fora do normal com duas variáveis “numero” e “nome” (linhas 5 e 6) um construtor (linhas 8 a 11) e os métodos GET (linhas 13 a 19). Para além disso tem também um Override do método toString que retorna uma String com os valores das variáveis. A única parte digamos fora do normal é mesmo na linha 3 a implementação do interface Serializable importado na linha 1 da nossa classe. Este interface vai permitir que os Objectos gerados por esta classe possam ser serializados e vice versa.

Temos agora uma classe de teste que irá serializar os objectos criados da classe Exemplo1.

1 import java.io.File;
2 import java.io.FileInputStream;
3 import java.io.FileOutputStream;
4 import java.io.ObjectInputStream;
5 import java.io.ObjectOutputStream;
6
7 public class Teste1 {
8
9     public static void main(String args []){
10        Exemplo1 e1 = new Exemplo1(001,"White");
11        Exemplo1 e2 = new Exemplo1(002,"Magician");
12
13        System.out.println(e1.toString());
14        System.out.println(e2.toString());
15
16        ObjectOutputStream out;
17        ObjectInputStream in;
18
19        try{
20            out = new ObjectOutputStream(new FileOutputStream(System.getProperty("user.dir")+File.separator+"Exemplo1.bin"));
21            out.writeObject(e1);
22            out.writeObject(e2);
23            out.flush();
24            out.close();
25        }
26        catch(Exception e){
27            e.printStackTrace();
28        }
29
30        Exemplo1 e3;
31        Exemplo1 e4;
32
33        try {
34            in = new ObjectInputStream(new FileInputStream(System.getProperty("user.dir")+File.separator+"Exemplo1.bin"));
35            e3 = (Exemplo1) in.readObject();
36            e4 = (Exemplo1) in.readObject();
37
38            in.close();
39
40            System.out.println(e3.toString());
41            System.out.println(e4.toString());
42        }
43        catch (Exception e){
44            e.printStackTrace();
45        }
46    }
47 }

[Teste1.java]

Esta classe é constituída apenas pelo método main visto que tem por objectivo apenas demonstrar a serialização de objectos para um ficheiro binário. Começamos por criar alguns objectos neste caso dois (linhas 10 e 11) e o conteúdo desses Objectos será impresso no ecrã (linhas 13 e 14) com o auxilio do método toString implementado na classe Exemplo1. Na linha 20 inicializamos um ObjectOutputStream que irá criar um ficheiro binário de nome Exemplo1.bin na directoria actual. Este stream irá permitirá serializar os objectos no ficheiro. Agora basta ordenar que os objectos sejam serializados, para isso usamos o método writeObject(Object object) que irá guardar o objecto dado como argumento no ficheiro de destino sob a forma binária (linhas 21 e 22).
Agora vamos fazer o processo inverso, passar os objectos serializados no ficheiro binário para objectos Java válidos. Este processo é tão simples como o seu inverso. Para isso iremos começar por criar dois novos objectos da classe Exemplo1 sem os inicializar (linhas 30 e 31), em seguida inicializamos um ObjectInputStream (linha 34) que irá ler o ficheiro Exemplo1.bin criado anteriormente, este stream contém o método readObject() que lê um objecto serializado num ficheiro. Como podemos ver nas linhas 35 e 36 os objectos retornados pelo método readObject() são guardados nas variáveis e3 e e4 criadas anteriormente, podemos ver também que nestas mesmas linhas é feito um cast para (Exemplo1) o cast deve ser sempre feito porque embora os objectos serializados sejam do tipo Exemplo1 o método readObject() retorna o tipo genérico Object que depois dever ser convertido para o tipo original desse objecto.
Por fim vamos imprimir os objectos e3 e e4 tal como fizemos anteriormente para o e1 e e2 e se todo o processo correr normalmente o valor de e1 será igual a e3 e o mesmo acontece com e2 e e4, o output. Neste caso será algo como o que podemos ver em seguida.

Numero = 1 | Nome = White
Numero = 2 | Nome = Magician
Numero = 1 | Nome = White
Numero = 2 | Nome = Magician

Vamos agora ver uma outra forma de serialização, embora use o mesmo mecanismo que o exemplo anterior neste exemplo os objectos serão serializados para um ByteArray, está técnica pode ser bastante útil para envio de grandes quantidades de objectos pela rede.
Para isso vamos usar novamente a classe Exemplo1 e uma nova classe de teste semelhante á classe Teste1 usada anteriormente.

1 import java.io.ByteArrayInputStream;
2 import java.io.ByteArrayOutputStream;
3 import java.io.ObjectInputStream;
4 import java.io.ObjectOutputStream;
5
6 public class Teste2 {
7    
8    public static void main(String args []){
9        Exemplo1 e1 = new Exemplo1(001,"White");
10        Exemplo1 e2 = new Exemplo1(002,"Magician");
11        
12        System.out.println(e1.toString());
13        System.out.println(e2.toString());
14        
15        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
16        ObjectOutputStream out;
17        ObjectInputStream in;
18        
19        try{
20            out = new ObjectOutputStream(buffer);
21            out.writeObject(e1);
22            out.writeObject(e2);
23            out.flush();
24            out.close();
25        }
26        catch(Exception e){
27            e.printStackTrace();
28        }
29        
30        Exemplo1 e3;
31        Exemplo1 e4;
32        
33        try {
34            in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()));
35            e3 = (Exemplo1) in.readObject();
36            e4 = (Exemplo1) in.readObject();
37            
38            in.close();
39            
40            System.out.println(e3.toString());
41            System.out.println(e4.toString());
42            
43        }
44        catch (Exception e){
45            e.printStackTrace();
46        }
47    }
48 }

[Teste2.java]

Há semelhança do exemplo anterior nesta classe são criados dois objectos da classe Exemplo1 e em seguida os seu valores são impressos.
As diferenças principais começam agora, na linha 15 criamos um ByteArrayOutputStream que será usado como buffer para guardar os objectos serializados, na linha 20 e á semelhança do exemplo anterior inicializamos o ObjectOutputStream mas ao contrário do primeiro exemplo em que damos como argumento um FileOutputStream usado para escrever no ficheiro neste caso dados como argumento o ByteArray criado, assim cada objecto serializado será guardado no ByteArray e não num ficheiro.
Nas linhas 21 e 22 são serializados dois objectos Exemplo1 para o ByteArray que será usado mais tarde.
ByteArrayOutputStream que criamos pode ser usado para varias coisas recorrendo ao método toByteArray() que retorna uma array de bytes com todos os dados do nosso ByteArrayOutputStream, as utilidades deste array são inúmeras mas neste casa vamos apenas usa-lo para construir um ObjectInputStream (linha 34), e assim iremos conseguir recuperar os nossos objectos serializados (linhas 35 e 36).
Por fim vamos imprimir valores dos nossos novos objectos e iremos ver que os valores coincidem tal como no exemplo anterior.

Numero = 1 | Nome = White
Numero = 2 | Nome = Magician
Numero = 1 | Nome = White
Numero = 2 | Nome = Magician

Para terminar este artigo sobre serialização de objectos em Java iremos fazer o processo usando XML ao invés do ficheiro binário como vimos anteriormente. Comecemos por criar uma classe Exemplo2.java.

1 public class Exemplo2 {
2    
3     private int numero;
4     private String nome;
5    
6     public Exemplo2 (){
7        
8     }
9    
10   public Exemplo2(int numero, String nome){
11        this.numero = numero;
12        this.nome = nome;
13   }
14
15   public void setNome(String nome) {
16        this.nome = nome;
17   }
18
19   public void setNumero(int numero) {
20        this.numero = numero;
21   }
22
23   public String getNome() {
24        return nome;
25   }
26
27   public int getNumero() {
28        return numero;
29   }
30    
31   public String toString() {
32        return new String("Numero = "+this.numero+" | Nome = "+this.nome);
33   }
34    
35 }

[Exemplo2.java]

Para começar vamos ver algumas diferenças de implementação na classe Exemplo2 para a classe Exemplo1 neste novo caso a classe não implementa a interface Serializable como acontecia nos exemplos anteriores, para além disso este processo requer alguns cuidados a classe deve conter os métodos set e get para todas as variáveis do objecto que queremos serializar em contras-te ao exemplos anteriores que apenas implementávamos os métodos que queríamos. Deve também ser implementado o construtor vazio (linhas 6 a 8 ). Atenção que a serialização para XML apenas consegue serializar dados com visibilidade public logo os métodos get e set bem como os construtor deve ser public caso contrario o valores não serão serializados.
Vejamos agora a classe que irá proceder á serialização.

1 import java.beans.XMLDecoder;
2 import java.beans.XMLEncoder;
3 import java.io.BufferedInputStream;
4 import java.io.BufferedOutputStream;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8
9 public class SerialXML {
10    
11    public static void main(String args []){
12        Exemplo2 e1 = new Exemplo2(001,"White");
13        Exemplo2 e2 = new Exemplo2(002,"Magician");
14
15       System.out.println(e1.toString());
16        System.out.println(e2.toString());
17        
18        try{
19            XMLEncoder encoder = new XMLEncoder( new BufferedOutputStream(
                   new FileOutputStream(System.getProperty("user.dir")+File.separator+"Exemplo2.xml")));
20            encoder.writeObject(e1);
21            encoder.writeObject(e2);
22            encoder.flush();
23            encoder.close();
24        }
25        catch(Exception e){
26            e.printStackTrace();
27        }
28        
29        Exemplo2 e3;
30        Exemplo2 e4;
31        
32        try{
33            XMLDecoder decoder = new XMLDecoder(new BufferedInputStream(
                   new FileInputStream(System.getProperty("user.dir")+File.separator+"Exemplo2.xml")));
34            e3 = (Exemplo2)decoder.readObject();
35            e4 = (Exemplo2)decoder.readObject();
36            decoder.close();
37            
38            System.out.println(e3.toString());
39            System.out.println(e4.toString());
40        }
41        catch(Exception e){
42            e.printStackTrace();
43        }
44    }
45 }

[SerialXML.java]

Para este processo precisamos das classes java.beans.XMLEncoder para gravar os objectos e a classe java.beans.XMLDecoder para o recuperar do arquivo XML (linhas 1 e 2).
Tal como nos exemplos anteriores esta classe apenas contém o métodos main e começa por criar dois objectos da classe Exemplo2 e em seguida os seus valores são impressos.
Vamos agora passar ao processo de serialização, para isso começamos por criar um objecto XMLEncoder (linha 19) que vai permitir serializar os objectos em um ficheiro XML, para isso e á semelhança do exemplos anteriores iremos usar o método writeObject (linhas 20 e 21). Depois destes passos já devemos uma ficheiro Exemplo2.xml com o seguinte conteúdo.

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.6.0" class="java.beans.XMLDecoder">
 <object class="Exemplo2">
  <void property="nome">
   <string>White</string>
  </void>
  <void property="numero">
   <int>1</int>
  </void>
 </object>
 <object class="Exemplo2">
  <void property="nome">
   <string>Magician</string>
  </void>
  <void property="numero">
   <int>2</int>
  </void>
 </object>
</java>

Como se pode ver o ficheiro contém os objectos serializados a apresentação dos dados é bastante bem estruturada e simples de ler e até mesmo editar, alias esta é uma das vantagens da serialização face à serialização binaria.
Temos agora o processo inverso passar de XML para objectos, para isso vamos criar um objecto XMLDecoder (linha 33) e vamos usar o método readObject disponibilizado por este objecto (linhas 34 e 35).
Por fim e tal como nos exemplos anteriores teremos o seguinte output.

Numero = 1 | Nome = White
Numero = 2 | Nome = Magician
Numero = 1 | Nome = White
Numero = 2 | Nome = Magician

Para finalizar a serialização de objectos basta apenas acrescentar que na serialização binária é possível definir determinadas variáveis ou constantes de forma a que no momento da serialização estes não sejam serializados. Para isso basta usar a palavra reservada transient, a sua utilização é bastante simples basta colocar a palavra transient antes do tipo e nome da variável ou seja algo semelhante a private transient String password; assim a variável password não será serializada.

Podem ainda consultar as APIs utilizadas neste artigo em:
http://java.sun.com/javase/6/docs/api/java/io/Serializable.html
http://java.sun.com/javase/6/docs/api/java/io/ObjectOutputStream.html
http://java.sun.com/javase/6/docs/api/java/io/ObjectInputStream.html
http://java.sun.com/javase/6/docs/api/java/beans/XMLEncoder.html
http://java.sun.com/javase/6/docs/api/java/beans/XMLDecoder.html

Este artigo foi originalmente escrito por Fábio Correira (magician) para a 10ª Edição da Revista PROGRAMAR

Anúncios

6 thoughts on “Serialização de Objectos em Java

  1. É muito simples na parte em que fazemos

    new ObjectOutputStream(new FileOutputStream(System.getProperty(“user.dir”)+File.separator+“Exemplo1.bin”));

    e

    new ObjectInputStream(new FileInputStream(System.getProperty(“user.dir”)+File.separator+“Exemplo1.bin”));

    ao invés de usar o FileInputStream usamos o Socket.getInputStream e Socket.getOutputStream para usar as streams dos sockets ao invés da stream para ficheiro.

    Deduzo que sabes trabalhar com Sockets e não também tenho ai um artigo sobre isso lol 😀

  2. Cara muito bom mesmo seu artigo.
    Tem como vc me mostrar um exeplo com UDP??
    Meu grande problema é mandar tipos de dados diferentes pela rede usando UDP. Do jeito que vc mostrou tem como??
    Muito obriado, antecipadamente.

  3. Gostei do artigo, mas diz-me, es como fazias, para receber os dados com um Loop em vez de fazres os dois read seguidos?

    Muito obriagada.

    • Podes fazer um loop de reads, dessa forma ele vai recebendo objectos e esperando pelos proximos. Caso não queiras que ele bloqueia podes usar o método avaiable para ver se ainda tem mais dados para receber.

      • Eu tentei esse metodo.. mas ele n estava a funcionar la muito bem! Mas obrigado pela dica!

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão /  Alterar )

Google photo

Está a comentar usando a sua conta Google Terminar Sessão /  Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão /  Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão /  Alterar )

Connecting to %s