terça-feira, 23 de outubro de 2012

Serialização Parte II


Continuando o post anterior.... Vamos ver agora dois aspectos diferentes da serialização. Primeiro vamos generalizar a classe que tinhamos feito no post anterior.

Vamos ver a classe de serialização:

Vamos alterar agora a nossa chamada para o exemplo. Notem que a única diferença real é que agora temos que fazer o cast da linha 35 abaixo:


Rodamos a aplicação. Ok... Ela continua rodando!


Agora vamos ver outra opção. E se nós não quisermos que uma propriedade pública seja gravada (sei lá por que!). Para isto vamos derivar (o termo correto seria extender) uma classe nova a partir da ClasseSerializada que utilizamos no exemplo anterior. Vamos acrescentar um novo inteiro que chamaremos, com bastante inventividade, de inteiro2 e vamos fazer com que inteiro não seja serializado.

Vou dividir a apresentação da classe em duas partes só para ficar mais fácil a explicação.


Primeiro vamos notar como foi criada a classe
public class ClasseSerializada2 extends ClasseSerializada
quer dizer que estamos herdando na ClasseSerializada2 tudo que já foi feito na classe mãe. Usamos extends para dizer ao Java que estamos fazendo a classe filha.

Nas linhas 20 a 26 estamos criando a nova propriedade e encapsulando-a.

Vocês se lembram que tínhamos um método print para poder dar saída na console dos valores das propriedades? Como acrescentamos uma nova temos que fazer uma alteração para incluir a nova propriedade na impressão. Poderíamos criar um novo método imprimindo todas as propriedades, mas vamos aproveitar a facilidade que a herança nos permite.

Temos que fazer duas coisas para o código funcionar:
  • veja na linha 31 que colocamos uma anotação de Override, que indica ao java que este método substitui o da classe mãe
  • na linha 33 damos saída na nova propriedade
  • na linha 34 utilizamos um super.print(). Este super indica a classe mãe, assim estamos chamando dentro deste método da classe filha, o método original da classe mãe.
Vamos agora a segunda parte da classe. O procedimento de serialização verifica as propriedades que tenham uma propriedade transiente verdadeira, assim para que ela seja escondida basta trocar este valor para falso.

Como fazemos isto? Utilizamos uma classe java.beans.Introspector. Esta classe permite verificar as propriedades, eventos e métodos suportados por um bean. No nosso caso procuramos pela PropertyDescriptor cujo nome seja igual ao da propriedade que queremos ocultar (vejam as linhas 46 a 50).
Tendo localizado basta fazer o valor ser falso.

Você vai falar que não é isto que estou fazendo. É verdade!
No meu exemplo, eu na verdade estou trocando o status da propriedade. Se está true vira false e vice versa.

Como eu já sei que esta propriedade é criada por default como TRUE, ao chamar togglePropriedade("inteiro") o efeito que consigo é desativar transient da propriedade inteiro.

Dava para usar um operador ternário substituindo as linhas 51 a 55.

Vamos a teste. A classe é a mesma do exemplo passado ... TesteSerializacao. Somente foi adaptada para chamar a ClasseSerializada2.


Veja que interessante. A saída dos inteiros é invertida, primeiro aparece inteiro2 e depois inteiro. Está claro o porque?


Depois que a classe de deserializada o inteiro volta como zero. Quer dizer que ele não foi lido ou gravado? Vamos ver a saída XML para entender:
Onde foi parar <void property="inteiro"> ? Não existe no arquivo XML ... quer dizer que nossa alteração funcionou. Esta propriedade não foi serializada.

;-) Gostaram do visual do Notepad++?

domingo, 21 de outubro de 2012

Já ouviu falar em serialização ?

De forma genérica a serialização é uma técnica usada para persistir objetos em um programa, em outras palavras, consiste em:
  • gravar os dados dos objetos em um banco de dados e/ou arquivos,
  • fazer a transmissão remota de objetos via rede.
Serializar é colocar os valores que o objeto está utilizando (suas propriedades) de uma forma que fique em série (sequencial). Fazendo isto estamos tornando o objeto Serializable, e, tornando um objeto Serializable, estamos atribuindo essa qualidade a ele, e dando privilégios para que o mesmo possa ser gravado em disco ou enviado por rede.

Na serialização podem ser gravados os atributos públicos e privados para um stream. O processo inverso é chamado - deserialização, que consiste em restaurar os atributos de um objeto gravado em um stream.

Vamos considerar a seguinte classe:
/*
* Esta classe será utilizada para exemplo de serialização XML
* Notem que ela tem o formato de um bean
*
* Temos algumas propriedades: inteiro, string, boolean, array
*
*/
package serializacao;
/**
*
* @author Henrique
*/
public class ClasseSerializada1 {
  /*
   * Propriedade inteiro
   */
  int i;
  public int getInteiro() { return i; }
  public void setInteiro(int i) { this.i = i; }

  /*
   * Propriedade minhaString
   */
  String s;
  public String getMinhaString() { return s; }
  public void setMinhaString(String s) { this.s = s;}

  /*
   * Propriedade Array de inteiro
   */
  int[] ia = new int[0];
  public int[] getArrayInteiro() { return ia; }
  public void setArrayInteiro(int[] iarray) {this.ia = iarray; }

  /*
   * Imprime as propriedades (usado no teste para vermos o que está no objeto)
   */
  public void print() {
    System.out.println(getInteiro());
     System.out.println(getMinhaString());
     int[] a = getArrayInteiro();
     System.out.print("[");
     for(int j = 0; j < a.length; j++) {
       System.out.print(a[j]+" ");
     }
     System.out.println("]");
  }
}

Notem que existem 3 propriedades públicas (encapsuladas com getters e setters).
Vamos criar uma outra classe para fazer a gravação e leitura. Notem que o processo é bastante parecido com a leitura e gravação de arquivos e por isto utilizamos Buffered___Stream e File___Stream.

O procedimento é bastante simples




  • Fazemos (na escrita) o encadeamento de XMLEncoder(BufferedOutputStream(FileOutputStream))
  • Usamos o XMLEncoder.writeObject para escrever o objeto no arquivo
Pronto!!!
Para ler é só fazer o inverso:

  • chamar XMLDecoder(BufferedInputStream(FileInputStream))
  • Usar XMLDecoder.readObject
    • Prestar atenção que temos que fazer um cast da classe correta

/*
* classe para teste de funcionamento
*/
package serializacao;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
*
* @author Henrique
*/
public class Serializacao1 {
  /*
   * A classe XMLEncoder serializa um objeto parecido com java.io.ObjectOutput
   *  --> é por isto que transferimos as streams para gerar o arquivo de
   * O XMLEncoder somente persiste as propriedades públicas das classes
   * Para cada propriedade pública, XMLEncoder chama o método getter
   * (getPropriedade) correspondente e persiste o valor retornado
   *
   * Desta forma qualquer propriedade privada (que não possuir getter e setter)
   * não será persistida ==> veremos isto em outro post
  */
  public void serializar(String nomeArquivo, ClasseSerializada1 obj) {
   try {
      // Serializa o object no arquivo XML
      XMLEncoder encoder;
      encoder = new XMLEncoder(
      new BufferedOutputStream(
        new FileOutputStream(nomeArquivo)));
      encoder.writeObject(obj);
      encoder.close();
   } catch (FileNotFoundException e) {
     // imprime erro na console
     System.out.println(e.getMessage());
   }
  }

  /*
   * Na deserialização, um objeto criado é inicializado com os valores
   * persistidos no arquivo XML (as propriedades públicas)
   *
   * le o arquivo serializado para dentro do objeto ClasseSerializada
   * retorno o objeto lido do arquivo XML
   */
  public ClasseSerializada1 deserializar(String nomeArquivo) {
    ClasseSerializada1 obj = null;
    try {
      XMLDecoder decoder = new XMLDecoder(
      new BufferedInputStream(
         new FileInputStream(nomeArquivo)));
      obj = (ClasseSerializada1) decoder.readObject();
      decoder.close();
    } catch (FileNotFoundException e) {
      // imprime erro na console
      System.out.println(e.getMessage());
    }
    return obj; // retorna o objeto lido do arquivo XML
  }

}
 



Agora uma classe simples de teste.
/*
* classe para teste de funcionamento

  */ 
 package serializacao;
  /**
      * 

    * @author Henrique
    */
 public class TesteSerializacao { 

  /**   * @param args the command line arguments
   */ 
  public static void main(String[] args) { 

    static String nomeArquivoXML = "classeserializada.xml";
 

    Serializacao1 s1 = new Serializacao1();
    ClasseSerializada o = new ClasseSerializada();
    // define as propriedades
    o.setInteiro(1);
    o.setMinhaString("Esta string será serializada");
    o.setArrayInteiro(new int[]{1, 2, 3, 4});
    // imprime para conferirmos os resultados
    System.out.println("*************** Objeto criado");
    o.print();
    // grava (serializa) o objeto o
    s1.serializar(nomeArquivoXML, o);
    // faz alterações nos campos para podermos ver que
    // a deserialização funciona
    o.setInteiro(3);
    o.setMinhaString("string alterada");
    o.setArrayInteiro(new int[]{1, 2});
    System.out.println("*************** Objeto alterado");
    o.print(); // imprime o objeto alterado para vermos
    // busca os dados a partir do arquivo XML
    o = deserializar(nomeArquivoXML);
    System.out.println("********** Objeto deserializado");
    o.print(); // gera a mesma saída do primeiro o.print() deste método
  }
}


O arquivo XML gerado está mostrado abaixo. Notem que a classe serializada está identificada (pacote + classe) e as propriedades estão todas listadas e identificadas.

<?xml version="1.0" encoding="UTF-8"?>
<java class="java.beans.XMLDecoder" version="1.7.0_07">
<object class="serializacao.ClasseSerializada">
<void property="arrayInteiro">
  <array class="int" length="4">
    <void index="0"> <int>1</int> </void>
    <void index="1"> <int>2</int> </void>
    <void index="2"> <int>3</int> </void>
    <void index="3"> <int>4</int> </void>
  </array>
</void>
<void property="inteiro">
  <int>1</int>
</void>
<void property="minhaString">
  <string>Esta string será serializada</string>
</void>
</object>
</java>

Vemos no próximo posto como impedir uma propriedade pública de ser serializada e como serializar uma propriedade privada.

Podemos tornar a classe Serializacao1 mais genérica, recebendo e devolvendo objetos da classe  Object e fazendo o cast fora.


public class Serializacao {
  public void serializar(String nomeArquivo, Object obj) {
      ... // fica tudo igual

   }

  public Object deserializar(String nomeArquivo) {
    Object obj = null;
    try {
      XMLDecoder decoder = new XMLDecoder(
      new BufferedInputStream(
         new FileInputStream(nomeArquivo)));
      obj = decoder.readObject();
      decoder.close();
    } catch (FileNotFoundException e) {
      // imprime erro na console
      System.out.println(e.getMessage());
    }
    return obj; // retorna o objeto lido do arquivo XML
  }
}


No próximo post vamos usar este formato, ok?

sábado, 20 de outubro de 2012

Layout Manager e Netbeans

Tive uma pergunta em um post anterior solicitando que eu colocasse como utilizar os gerenciadores de layout via Netbeans. Fiz um pequeno vídeo mostrando a criação da mesma classe (não tive preocupação com colocar os mesmos nomes, mas está com os mesmos tipos de objetos).

Espero que gostem....