Guia da interface externalizável em Java
1. Introdução
Neste tutorial,we’ll have a quick look at java’s java.io.Externalizable interface. O principal objetivo dessa interface é facilitar a serialização e desserialização personalizadas.
Antes de prosseguirmos, certifique-se de verificar o artigoserialization in Java. O próximo capítulo é sobre como serializar um objeto Java com essa interface.
Depois disso, vamos discutir as principais diferenças em comparação com a interface dejava.io.Serializable.
2. The InterfaceExternalizable
Externalizable estende-se da interface do marcadorjava.io.Serializable. Any class that implements Externalizable interface should override the writeExternal(), readExternal() methods. Dessa forma, podemos alterar o comportamento de serialização padrão do JVM.
2.1. Serialização
Vamos dar uma olhada neste exemplo simples:
public class Country implements Externalizable {
private static final long serialVersionUID = 1L;
private String name;
private int code;
// getters, setters
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(code);
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
this.name = in.readUTF();
this.code = in.readInt();
}
}
Aqui, definimos uma classeCountry que implementa a interfaceExternalizable e implementa os dois métodos mencionados acima.
In the writeExternal() method, we’re adding the object’s properties to the ObjectOutput stream. Tem métodos padrão comowriteUTF() paraStringewriteInt() para os valores inteiros.
A seguir,for deserializing the object, we’re reading from the ObjectInput stream usando os métodosreadUTF(), readInt() para ler as propriedades na mesma ordem exata em que foram escritas.
É uma boa prática adicionarserialVersionUID manualmente. Se isso estiver ausente, a JVM adicionará automaticamente uma.
O número gerado automaticamente depende do compilador. Isso significa que pode causarInvalidClassException improvável.
Vamos testar o comportamento que implementamos acima:
@Test
public void whenSerializing_thenUseExternalizable()
throws IOException, ClassNotFoundException {
Country c = new Country();
c.setCode(374);
c.setName("Armenia");
FileOutputStream fileOutputStream
= new FileOutputStream(OUTPUT_FILE);
ObjectOutputStream objectOutputStream
= new ObjectOutputStream(fileOutputStream);
c.writeExternal(objectOutputStream);
objectOutputStream.flush();
objectOutputStream.close();
fileOutputStream.close();
FileInputStream fileInputStream
= new FileInputStream(OUTPUT_FILE);
ObjectInputStream objectInputStream
= new ObjectInputStream(fileInputStream);
Country c2 = new Country();
c2.readExternal(objectInputStream);
objectInputStream.close();
fileInputStream.close();
assertTrue(c2.getCode() == c.getCode());
assertTrue(c2.getName().equals(c.getName()));
}
Neste exemplo, primeiro estamos criando um objetoCountry e gravando-o em um arquivo. Então, estamos desserializando o objeto do arquivo e verificando se os valores estão corretos.
A saída do objetoc2 impresso:
Country{name='Armenia', code=374}
Isso mostra que desserializamos o objeto com sucesso.
2.2. Herança
Quando uma classe herda da interfaceSerializable, a JVM coleta automaticamente todos os campos das subclasses e os torna serializáveis.
Lembre-se de que podemos aplicar isso aExternalizable também. We just need to implement the read/write methods for every sub-class of the inheritance hierarchy.
Vejamos a classeRegion abaixo, que estende nossa classeCountry da seção anterior:
public class Region extends Country implements Externalizable {
private static final long serialVersionUID = 1L;
private String climate;
private Double population;
// getters, setters
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeUTF(climate);
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
super.readExternal(in);
this.climate = in.readUTF();
}
}
Aqui, adicionamos duas propriedades adicionais e serializamos a primeira.
Observe quewe also called super.writeExternal(out), super.readExternal(in) within serializer methods to save/restore the parent class fields as well.
Vamos executar o teste de unidade com os seguintes dados:
Region r = new Region();
r.setCode(374);
r.setName("Armenia");
r.setClimate("Mediterranean");
r.setPopulation(120.000);
Aqui está o objeto desserializado:
Region{
country='Country{
name='Armenia',
code=374}'
climate='Mediterranean',
population=null
}
Observe quesince we didn’t serialize the population field in Region class, the value of that property is null.
3. Externalizable vsSerializable
Vamos examinar as principais diferenças entre as duas interfaces:
-
Responsabilidade de serialização
A principal diferença aqui é como lidamos com o processo de serialização. Quando uma classe implementa a interfacejava.io.Serializable, a JVM assume total responsabilidade pela serialização da instância da classe. In case of Externalizable, it’s the programmer who should take care of the whole serialization and also deserialization process.
-
Caso de Uso
Se precisarmos serializar o objeto inteiro, a interfaceSerializable é um ajuste melhor. Por outro lado,for custom serialization, we can control the process using Externalizable.
-
atuação
A interfacejava.io.Serializable usa reflexão e metadados, o que causa desempenho relativamente lento. Por comparação, oExternalizable interface gives you full control over the serialization process.
-
Ordem de Leitura
While using Externalizable, it’s mandatory to read all the field states in the exact order as they were written. Caso contrário, teremos uma exceção.
Por exemplo, se alterarmos a ordem de leitura das propriedadescodeename na classeCountry, umjava.io.EOFException será lançado.
Enquanto isso, a interfaceSerializable não tem esse requisito.
-
Serialização Personalizada
Podemos obter serialização personalizada com a interfaceSerializable marcando o campo com a palavra-chavetransient. The JVM won’t serialize the particular field but it’ll add up the field to file storage with the default value. É por isso que é uma boa prática usarExternalizable no caso de serialização personalizada.
4. Conclusão
Neste breve guia para a interfaceExternalizable, discutimos os principais recursos, vantagens e exemplos demonstrados de uso simples. Também fizemos uma comparação com a interfaceSerializable.
Como de costume, o código-fonte completo do tutorial está disponívelover on GitHub.