1. Interface Serializable
Lembra dos exemplos da aula passada? As classes Java que queremos serializar devem implementar uma interface especial — java.io.Serializable. Trata-se de uma interface “marcadora”: ela não contém nenhum método, apenas “marca” a classe como adequada para serialização. Se a classe implementa essa interface, a JVM permite serializar seus objetos pelos meios padrão.
Não é desejável serializar tudo, porque nem todos os objetos podem ou precisam ser salvos em bytes. Alguns objetos dependem do estado do sistema operacional, de arquivos abertos ou de conexões de rede. Por isso o Java exige marcar explicitamente a classe como serializável.
Uma interface marcadora é como um adesivo “apto para embalar” na caixa. Se esse adesivo não existir, o embalador (JVM) se recusa a trabalhar.
Exemplo: declaração de uma classe serializável
import java.io.Serializable;
public class User implements Serializable {
private String name;
private int age;
// Construtor, getters e setters
public User(String name, int age) {
this.name = name;
this.age = age;
}
// Apenas para ficar bonito: método toString()
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
Atenção:
- Apenas adicionamos implements Serializable à declaração da classe.
- Não é preciso implementar nenhum método (a interface é vazia).
- Todas as classes padrão do Java que podem ser serializadas (por exemplo, ArrayList, HashMap, String) já implementam Serializable.
2. Como tornar sua classe serializável
Regra nº 1: basta adicionar implements Serializable
Isso é tudo que é necessário para a própria classe. Mas há nuances!
Importante: todos os objetos referenciados também devem ser serializáveis.
Se sua classe tiver campos que são referências a outros objetos, eles também devem ser serializáveis. Por exemplo:
public class Profile implements Serializable {
private User user; // User deve ser serializável!
private int level;
}
Se pelo menos um dos campos não for serializável, uma exceção será lançada ao tentar serializar.
3. Exemplo de serialização e desserialização
Vamos ver como serializar e desserializar um objeto para/desde um arquivo. Para isso, usamos as classes ObjectOutputStream e ObjectInputStream.
Exemplo: serializando um objeto User para um arquivo
import java.io.*;
public class SerializeDemo {
public static void main(String[] args) {
User user = new User("Alice", 30);
// Salvamos o objeto em um arquivo
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
oos.writeObject(user);
System.out.println("Objeto serializado com sucesso no arquivo user.ser");
} catch (IOException e) {
System.out.println("Erro de serialização: " + e.getMessage());
}
}
}
O que está acontecendo aqui?
- Criamos um objeto User.
- Abrimos um ObjectOutputStream que escreve no arquivo "user.ser".
- Chamamos writeObject(user). Nesse momento, a JVM transforma o objeto em um fluxo de bytes e o salva no arquivo.
Exemplo: desserializando um objeto de um arquivo
import java.io.*;
public class DeserializeDemo {
public static void main(String[] args) {
// Lemos o objeto do arquivo
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
User user = (User) ois.readObject();
System.out.println("Objeto restaurado com sucesso: " + user);
} catch (IOException | ClassNotFoundException e) {
System.out.println("Erro de desserialização: " + e.getMessage());
}
}
}
O que está acontecendo aqui?
- Abrimos um ObjectInputStream lendo do arquivo "user.ser".
- Chamamos readObject(). A JVM reconstrói o objeto a partir dos bytes.
- Não se esqueça de fazer o cast para o tipo necessário (User), pois readObject() retorna Object.
- Pode ocorrer ClassNotFoundException se a classe User não for encontrada durante a desserialização.
Tudo junto: serialização e desserialização
import java.io.*;
public class SerializationExample {
public static void main(String[] args) {
User user = new User("Bob", 22);
// Serialização
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
oos.writeObject(user);
System.out.println("Serialização concluída!");
} catch (IOException e) {
System.out.println("Erro de serialização: " + e.getMessage());
}
// Desserialização
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
User loaded = (User) ois.readObject();
System.out.println("Desserialização concluída! " + loaded);
} catch (IOException | ClassNotFoundException e) {
System.out.println("Erro de desserialização: " + e.getMessage());
}
}
}
Resultado:
Serialização concluída!
Desserialização concluída! User{name='Bob', age=22}
4. O que acontece “por baixo do capô” durante a serialização
Quando você chama writeObject, a JVM primeiro verifica se a classe implementa a interface Serializable. Se a classe não estiver marcada como serializável, é lançada uma exceção. Em seguida, a JVM percorre todos os campos normais do objeto (ou seja, aqueles que não são static nem transient) e grava seus valores no fluxo de bytes. Se entre esses campos houver outros objetos, a serialização é aplicada a eles recursivamente, mas somente se também implementarem Serializable.
Durante a desserialização, o objeto é criado sem chamar o construtor usual, e seus campos são preenchidos com os valores salvos — é como se um “construtor sem construtor” trouxesse o objeto de volta a partir do fluxo de bytes.
Alguns campos não serão serializados. Campos estáticos (static) pertencem à própria classe, não a um objeto específico, portanto seus valores não são salvos. Campos marcados como transient também são ignorados — isso é útil para dados temporários, cache ou informações sigilosas como senhas.
Esquema do processo de serialização
flowchart TB
A[Objeto User na memória] -- writeObject --> B[ObjectOutputStream]
B -- salva os bytes --> C[Arquivo user.ser]
C -- readObject --> D[ObjectInputStream]
D -- restaura --> E[Objeto User na memória]
5. Erros comuns ao usar Serializable
Erro nº 1: campo referência para um objeto não serializável. Se na classe User aparecer um campo de um tipo como Thread ou Socket, a serialização não funcionará. Nem todos os objetos podem ser serializados — lembre-se disso!
Erro nº 2: classes internas não serializáveis. Se a classe User contiver uma classe interna que não seja static, a serialização pode falhar. Prefira usar classes internas static ou classes separadas em arquivos próprios.
Erro nº 3: tentar serializar um campo static. Campos estáticos não são serializados — eles pertencem à classe, não ao objeto. Após a desserialização, o campo static terá o valor definido na classe, e não no objeto serializado.
Erro nº 4: incompatibilidade de versões da classe. Se você alterar a estrutura da classe após a serialização (por exemplo, adicionar ou remover um campo) e depois tentar desserializar um objeto antigo, pode ocorrer o erro InvalidClassException. Para controle de versões é usado o campo especial serialVersionUID — falaremos mais sobre ele na próxima aula.
GO TO FULL VERSION