Atenção: por essa edição ser muito antiga não há arquivo PDF para download.
Os artigos dessa edição estão disponíveis somente através do formato HTML.

Clique aqui para ler todos os artigos desta edição 

Persistência em Java

Serialização utilizando o JDK

É difícil imaginar uma aplicação de software, seja de que porte for, que não tenha a necessidade de utilizar todos ou parte de seus dados entre as diversas execuções ou disponibilizá-los para que a mesma aplicação os utilize em outros computadores ou por outras aplicações. Estes dados podem ir de simples parâmetros para os componentes gráficos, como posicionamento e aparência, passando por dados que descrevem recursos externos, até dados que fazem parte do negócio para o qual a aplicação tenha sido projetada.

A forma como os dados se tornam disponíveis entre as diversas execuções de uma aplicação são variadas. De maneira geral, podem ir de simples arquivos com formato texto ou binário, passando por estruturas padronizadas em arquivos XML (eXtensible Markup Language) e chegando a estruturas organizadas em tabelas disponíveis em sistemas de gerenciamento de bancos de dados.

Este processo de armazenamento e recuperação de dados, independente do meio de armazenamento, é conhecido como persistência. Como dito inicialmente, a persistência pode ser realizada de diferentes maneiras, dependendo do mecanismo utilizado. Neste artigo será abordada a serialização e suas variações providas pela plataforma Java.

Características

A serialização Java possui as seguintes características, de acordo com sua especificação (ver Referências no final):

·         Disponibilizar um mecanismo simples e extensível;

·         Manter o tipo do objeto e suas propriedades em uma forma serializada;

·         Ser extensível no suporte a objetos remotos;

·         Ser extensível no suporte a persistência simples de objetos;

·         Requerer implementação em nível de classe somente para customização, e;

·         Permitir que o objeto defina seu formato externo.

 

Estas características permitem que as aplicações Java persistam os estados de seus objetos com nenhum ou pouco código, dependendo da situação e de quanto se necessite de customização. Neste contexto, Java disponibiliza duas interfaces, java.io.Serializable (vista a seguir) e java.io.Externalizable (vista mais adiante), as quais precisam ser implementadas por classes cujos objetos devem ser serializados.

A interface java.io.Serializable funciona como marcadora, ou seja, esta interface não possui métodos a serem implementados, apenas informa ao mecanismo de persistência que o objeto em questão é passível de serialização. Assim, uma classe a ser serializada deve:

·         Implementar a interface java.io.Serializable;

·         Identificar quais atributos serão serializados e utilizar o modificador transient ou, o qualificador static para indicar quais não serão, ou usar o atributo serialPersistentFields para definir explicitamente quais atributos devem ser serializados;

·         Ter acesso ao construtor sem argumentos de sua primeira superclasse não serializável.

 

E, opcionalmente, definir os métodos:

·         writeObject(), que controla quais dados serão persistidos ou que informação adicional será acrescentada ao fluxo (ler Nota 1);

·         readObject(), que lê os dados gravados pelo método writeObject() correspondente ou atualiza o estado do objeto após este ter sido restaurado, e;

·         writeReplace(), que identifica um objeto substituto que será gravado no fluxo.

 

Nota 1. Fluxo

Java utiliza o termo fluxo (do inglês stream) para representar a seqüência de bytes que é lida a partir de um dispositivo de entrada (input stream) ou escrita para um dispositivo de saída (output stream). Tais dispositivos podem ser um console (monitor e teclado), um arquivo em disco, um bloco de memória ou uma conexão de rede.

 

A Listagem 1 apresenta a classe Pessoa que implementa java.io.Serializable e cujos objetos terão seus estados (os valores de nome e nascimento) gravados pelo mecanismo padrão de persistência.

 

Listagem 1. Classe Pessoa a ser utilizada no processo de serialização.

01 public class PessoaSerializable1 implements java.io.Serializable {

      // atributos cujos valores serão serializados

02    private String nome;

03    private Date   nascimento;

  

04    public PessoaSerializable1(String nome, Date nascimento) {

05       this.nome       = nome;

06       this.nascimento = nascimento;

      } // public PessoaSerializable1(String, Date)

 

07    public String toString() {

08       return "Nome: " + nome + " " + " Nascimento: " +

                DateFormat.getDateInstance().format(nascimento);

      } // public String toString()

 

      // setters

09    public void setNome(String nome) { this.nome = nome; }

10    public void setNascimento(Date nascimento) { this.nascimento = nascimento; }

 

      // getters

11    public String getNome() { return nome; }

12    public Date   getNascimento() { return nascimento; }

   } // public class PessoaSerializable1 implements java.io.Serializable

Deserializando um objeto

Normalmente, o programador não se preocupa com a ordem na qual o estado de uma rede de objetos é serializada, mas é importante conhecer como o processo de deserialização é realizado. Para demonstrar este processo, tomaremos como exemplo a hierarquia de classes da Figura 1 e cujos códigos são apresentados mais adiante.

 

Figura 1. Hierarquia de classes utilizada para demonstrar o processo de deserialização.

 

Na Listagem 2 é apresentada a classe Pessoa que, por definição, estende a classe java.lang.Object que, por sua vez, não é serializável. A classe Pessoa serve como base para a classe Funcionario e para a classe Gerente. A classe Funcionario, apresentada na Listagem 3, estende a classe Pessoa de tal forma que todo funcionário tenha um identificador e um nome, ao mesmo tempo em que implementa a interface marcadora java.io.Serializable, o que permite que objetos Funcionario possam ser serializados. A classe Gerente é apresentada na Listagem 4. Esta classe, que estende Funcionario, declara que todo gerente é também um funcionário e, ainda, pelo princípio da herança, também permite que objetos Gerente sejam serializados.

Quando um objeto Gerente for recuperado, primeiro o mecanismo de serialização instancia um novo objeto e então a primeira classe serializável na hierarquia será encontrada (neste caso, Funcionario). O mecanismo de serialização invoca o construtor sem argumentos da superclasse da classe Funcionario – a última classe não serializável do objeto – que é Pessoa. Se o estado (ou parte dele) da classe Pessoa deve ser preservado, a classe Gerente é responsável por serializá-lo e restaurá-lo, conforme exemplificado pela Listagem 4 e melhor observado na seção Serialização Controlada, logo a seguir. Antes que o processo de deserialização ocorra é necessário que a classe serializada anteriormente seja encontrada e carregada pela Máquina Virtual Java. Se isto não ocorrer por algum motivo, o método readObject() lançará a exceção java.lang.ClassNotFoundException.

 

Listagem 2. Classe Pessoa.

01 public class Pessoa {

      // esta classe não implementa a interface java.io.Serializable, consequentemente os valores

      // de seus atributos nao serão serializados automaticamente.

02    protected long   oid;

03    protected String nome;

 

04    public Pessoa() { this(0,null); }

 

05    public Pessoa(long oid, String nome) { this.oid = oid; this.nome = nome; }

 

06    public String toString() { return "Pessoa: " + oid + " Nome: " + nome; }

 

      // setters

07    public void setNome(String nome) { this.nome = nome; }

 

      // getters

08    public long   getOid() { return oid; }

09    public String getNome() { return nome; }

   } // public class Pessoa

 

Listagem 3. Classe Funcionario que estende Pessoa e implementa a interface marcadora Serializable.

01 public class Funcionario extends Pessoa implements Serializable {

02    private String     depto;

03    private BigDecimal salario;

 

04    public Funcionario() { this(0,null,null,null); }

 

05    public Funcionario(long oid, String nome, String depto, BigDecimal salario) {

06       super(oid,nome);

07       this.depto   = depto;

08       this.salario = salario;

...

Quer ler esse conteúdo completo? Tenha acesso completo