Fazia bastante tempo que eu não lia um capítulo da SCJP 6, mas como eu preciso retomar meus estudos, então, por tabela, tammbém preciso retomar minhas anotações/posts sobre o que eu andar lendo...
Em Ciência da Computação, no contexto de armazenamento e transmissão de dados, a serialização é o processo de salvar um objeto em um meio de armazenamento (como um arquivo de computador ou um buffer de memória) ou transmiti-lo por uma conexão de rede, seja em forma binária ou em formato de texto como o XML. Esta série de bytes pode ser usada para recriar um objeto com o mesmo estado interno que o original. [Wikipedia]
Você, estagiário (não tenho saudades dessa época), vai se lembrar de serialização quando estiver desenvolvendo alguma aplicação web, por exemplo. Eu nunca havia feito serialização manualmente até o momento em que eu li essa seção do livro da SCJP 6.
import java.util.*; import java.io.*; class Pessoa implements Serializable { private String nome; public Pessoa (String nome){ this.nome = nome; } public String getNome() { return this.nome; } public void setNome(String nome){ this.nome = nome; } public String toString() { String result = "nome: " + this.nome; return result; } } public class Principal { public static void main(String[] args){ Pessoa pessoa = new Pessoa("Leandro"); String fileName = Pessoa.class.getName() + ".txt"; System.out.println("Antes da serialização - " + pessoa); serializeObject(fileName, pessoa); Pessoa pessoaNova = (Pessoa) deserializeObject(fileName); System.out.println("Depois da serialização - " + pessoaNova); } public static void serializeObject( String fileName, Object object){ try { FileOutputStream fos = new FileOutputStream(fileName); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(object); oos.close(); } catch (Exception ex){ ex.printStackTrace(); } } public static Object deserializeObject(String fileName){ Object object = null; try { FileInputStream fis = new FileInputStream(fileName); ObjectInputStream ois = new ObjectInputStream(fis); object = ois.readObject(); ois.close(); } catch (Exception ex){ ex.printStackTrace(); } return object; } }
Execute esse código e abra o arquivo Pessoa.txt só por curiosidade. Note que você consegue entender parte do que foi escrito apenas por mera casualidade, pois o Java deve utilizar uma estrutura bem formada para organizar os dados de um objeto num arquivo.
Agora, vamos complicar. E se eu quiser que uma das variáveis de instância da classe Pessoa seja uma referência a outro objeto? Insira no código acima a classe Contato:
class Contato { private String telefone; Contato(String telefone){ this.telefone = telefone; } public String getTelefone(){ return this.telefone; } public void setTelefone(String telefone){ this.telefone = telefone; } public String toString() { String result = "telefone: " + telefone; return result; } }
Modifique a classe Pessoa:
class Pessoa implements Serializable { private String nome; private int idade; private Contato dadosContato; Pessoa ( String nome, int idade, Contato dadosContato){ this.nome = nome; this.idade = idade; this.dadosContato = dadosContato } ... public int getIdade() { return this.idade; } public void setIdade(int idade){ this.idade = idade; } public Contato getDadosContato() { return this.dadosContato; } public void setDadosContato(Contato dadosContato){ this.dadosContato = dadosContato; } public String toString() { String result = "nome: " + this.nome + "\n"; result += "idade: " + this.idade + "\n"; result += "contato - " + this.dadosContato; return result; } }
E a classe Principal também:
public class Principal { public static void main(String[] args){ Contato dadosContato = new Contato("9876-5432"); Pessoa pessoa = new Pessoa("Leandro",23, dadosContato); } ... }
Agora tente executar novamente. Conseguiu? Eu acho que não...
Como você pode ver, o programa não consegue serializar a propriedade dadosContrato da classe Pessoa, porque a classe Contrato não é serializavel. Você conseguiria resolver esse problema, fazendo a classe Contrato implementar Serializable, mas e se não tiver acesso ao seu código fonte?
Incluindo o modificador de acesso transient à propriedade dadosContrato de Pessoa, você não salva o seu estado quando serializar a instância de Pessoa, acabando com o erro em tempo de execução. Entretanto, vale lembrar que nenhum construtor da classe a ser serializada é chamado, muito menos os valores atribuidos em tempo de declaração das variáveis de instância são usados, o que significa que as variáveis transientes são inicializadas com valores default.
Para testar isso, tente serializar a classe abaixo:
class Teste implements Serializable{ private transient int numero = 42; private String texto; Teste() { this.numero = 25; this.texto = "Mais um teste"; } Teste(String texto, int numero){ this.texto = texto; this.numero = numero; } public String getTexto() { return this.texto; } public void setTexto(String texto){ this.texto = texto; } public int getNumero() { return this.numero; } public void setNumero(int numero){ this.numero = numero; } public String toString(){ String result = "texto: " + this.texto + "\n"; result += "numero: " + this.numero; return result; } }
Mas e se for justamente salvar esta propriedade não serializável o que você precisa?
Você pode, além de fazer esta propriedade virar transient, implementar um par de métodos na sua classe Pessoa, para serializar você mesmo os dados de Contato durante a serialização de Pessoa. Altere a classe Pessoa conforme código abaixo:
class Pessoa implements Serializable { private String nome; private int idade; private transient Contato dadosContato; ... private void writeObject(ObjectOutputStream os) { try { os.defaultWriteObject(); Boolean checkDadosContrato = (this.dadosContato != null); os.writeBoolean(checkDadosContrato); if (checkDadosContrato) { os.writeObject(this.dadosContato .getTelefone()); } } catch (Exception ex){ ex.printStackTrace(); } } private void readObject(ObjectInputStream is) { try { is.defaultReadObject(); Boolean checkDadosContrato = is.readBoolean(); if (checkDadosContrato) { String telefone = (String) is.readObject(); this.dadosContato = new Contato(telefone); } } catch (Exception ex){ ex.printStackTrace(); } }
Acho que agora você consegue executar o programa.
Tá quase acabando, prometo :D Vimos que nenhum construtor da classe serializavel é executado. Vimos que variaveis transientes não são salvas. Vimos como serializar uma propriedade não serializável. Mas e quando a super classe não for serializável? E quanto às variáveis estáticas?
Bem, variáveis estáticas não correspondemm ao estado do objeto (instância da classe) em si. Perceba que todas as instâncias de uma classe compartilham o mesmo valor de uma variável estatica. Que sentido faria salvá-la? E se duas ou mais instâncias de uma classe fossem salvas, qual valor para a variável estática seria utilizado?
Quanto à super classe não serializável... Bem, dissemos que o construtor da classe serializável não é chamado, nem as variáveis de instância são inicializadas com os valores descritos em tempo de declaração. Mas o construtor da primeira classe não serializável é chamado, que chama o construtor da sua super classe, e assim por diante. Quer tirar a dúvida?
class TesteBase { private long numeroLongo = 100000L; private String textoBase; TesteBase() { textoBase = "texto base"; } TesteBase(long numeroLongo, String textoBase){ this.numeroLongo = numeroLongo; this.textoBase = textoBase; } public String toString(){ String result = "textoBase: " + this.textoBase + "\n"; result += "numeroLongo: " + this.numeroLongo; return result; } } class Teste extends TesteBase implements Serializable{ private transient int numero = 42; private String texto; Teste() { this.numero = 25; this.texto = "Mais um teste"; } Teste( String texto, int numero, long numeroLongo, String textoBase){ super(numeroLongo,textoBase); this.texto = texto; this.numero = numero; } public String getTexto() { return this.texto; } public void setTexto(String texto){ this.texto = texto; } public int getNumero() { return this.numero; } public void setNumero(int numero){ this.numero = numero; } public String toString(){ String result = "texto: " + this.texto + "\n"; result += "numero: " + this.numero + "\n"; result += super.toString(); return result; } }