1. Introdução
Em Java, a serialização só funciona com objetos que explicitamente permitem isso. Para tanto, a classe deve implementar a interface especial — java.io.Serializable.
import java.io.Serializable;
public class Person implements Serializable {
// Campos, construtores, métodos
}
Serializable é uma interface marcadora: ela não possui métodos; apenas informa à JVM — “esta classe pode ser serializada, não se preocupe!”. Se você tentar serializar um objeto de uma classe que não implementa Serializable, receberá a exceção NotSerializableException. Mesmo que ao menos um campo (ou um objeto aninhado) não seja serializável — a serialização não funcionará.
ObjectOutputStream e ObjectInputStream
- ObjectOutputStream — classe que grava objetos em um fluxo (por exemplo, em um arquivo ou pela rede).
- ObjectInputStream — classe que lê objetos de um fluxo.
Eles funcionam em pares: um serializa o objeto, o outro — desserializa.
Métodos principais
- writeObject(Object obj) — serializa o objeto e o grava no fluxo.
- readObject() — lê um objeto do fluxo, desserializa e o retorna.
Importante: ambas as classes funcionam sobre fluxos de entrada e saída comuns (OutputStream e InputStream). Com mais frequência, são usadas com fluxos de arquivo — FileOutputStream e FileInputStream, mas também podem ser aplicadas com fluxos de rede.
2. Exemplo de serialização
Vamos escrever um exemplo simples: serializar e desserializar um objeto da classe Person.
Passo 1. Descrever a classe
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
// campo transient não será serializado
private transient String secret;
public Person(String name, int age, String secret) {
this.name = name;
this.age = age;
this.secret = secret;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", secret='" + secret + "'}";
}
}
Passo 2. Serializar o objeto para um arquivo
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class SerializeDemo {
public static void main(String[] args) throws Exception {
Person person = new Person("Alice", 30, "likes pizza");
// Criamos um fluxo para gravar no arquivo
FileOutputStream fileOut = new FileOutputStream("person.bin");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
// Salvamos o objeto
out.writeObject(person);
// Fechamos os fluxos
out.close();
fileOut.close();
System.out.println("Objeto serializado no arquivo person.bin");
}
}
Passo 3. Desserializar o objeto do arquivo
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class DeserializeDemo {
public static void main(String[] args) throws Exception {
// Abrimos o fluxo para leitura do arquivo
FileInputStream fileIn = new FileInputStream("person.bin");
ObjectInputStream in = new ObjectInputStream(fileIn);
// Recuperamos o objeto
Person person = (Person) in.readObject();
in.close();
fileIn.close();
System.out.println("Objeto desserializado: " + person);
}
}
Saída esperada
Objeto serializado no arquivo person.bin
Objeto desserializado: Person{name='Alice', age=30, secret='null'}
Atenção! O campo transient não é serializado. Após a desserialização, ele será igual a null. Isso é importante para dados temporários ou sensíveis.
3. Restrições e particularidades
Todos os campos devem ser serializáveis
Se a classe tiver campos que não implementam Serializable (ou contêm tais objetos), a serialização terminará com erro. Por exemplo, campos do tipo Thread ou Socket não podem ser tornados serializáveis “simplesmente”.
Campos estáticos e transient
- Campos estáticos (static) não são serializados: eles pertencem à classe, não a um objeto específico.
- Campos transient — marcados como transient são explicitamente excluídos da serialização e, após a recuperação, recebem valores padrão (null, 0 etc.).
Exceções
- A tentativa de serializar um objeto que não implementa Serializable lança NotSerializableException.
- Ao desserializar, erros podem ocorrer: arquivo não encontrado, incompatibilidade de classes, dados corrompidos etc.
Versões de classes
Se você alterar a estrutura da classe após a serialização (por exemplo, adicionar/remover campos), durante a desserialização pode ocorrer InvalidClassException. Para controle de versão, usa-se o campo especial serialVersionUID (mais detalhes em uma das próximas aulas).
4. Prática: serialização e desserialização de um objeto para arquivo
Suponha que temos a classe Person e queremos salvar uma lista de pessoas em um arquivo e lê-la de volta.
Classe Person (serializável)
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
private transient String secret; // não será serializado
public Person(String name, int age, String secret) {
this.name = name;
this.age = age;
this.secret = secret;
}
@Override
public String toString() {
return name + " (" + age + "), secret: " + secret;
}
}
Serialização da lista de pessoas
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class SerializeListDemo {
public static void main(String[] args) throws Exception {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 30, "likes pizza"));
people.add(new Person("Bob", 25, "hates broccoli"));
FileOutputStream fileOut = new FileOutputStream("people.bin");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
// Salvamos a lista
out.writeObject(people);
out.close();
fileOut.close();
System.out.println("Lista de pessoas serializada.");
}
}
Desserialização da lista de pessoas
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;
public class DeserializeListDemo {
public static void main(String[] args) throws Exception {
FileInputStream fileIn = new FileInputStream("people.bin");
ObjectInputStream in = new ObjectInputStream(fileIn);
// Recuperamos a lista
List<Person> people = (List<Person>) in.readObject();
in.close();
fileIn.close();
for (Person p : people) {
System.out.println(p);
}
}
}
Resultado:
Alice (30), secret: null
Bob (25), secret: null
5. Erros comuns
Erro nº 1: a classe não implementa Serializable. Se você esquecer de adicionar implements Serializable, ao tentar serializar receberá NotSerializableException. Este é o erro mais comum e simples.
Erro nº 2: Campo não serializável. Se o objeto contiver um campo que não é serializável (por exemplo, Thread, Socket ou qualquer outro tipo sem Serializable), a serialização “vai falhar”. Marque tais campos como transient ou torne-os serializáveis.
Erro nº 3: Alteração da estrutura da classe. Se o objeto foi serializado e, depois, a classe for alterada (campos adicionados/removidos), na leitura pode ocorrer InvalidClassException. Especifique serialVersionUID para um controle de versão estável.
Erro nº 4: Tentativa de serializar campos estáticos. Campos com o modificador static não são serializados. Após a desserialização, seus valores serão aqueles definidos na classe por padrão, e não os que existiam no momento da serialização.
Erro nº 5: Fluxos não fechados. Se você não fechar os fluxos após o uso, pode obter arquivo corrompido ou vazamento de recursos. Use try-with-resources ou feche os fluxos explicitamente.
Erro nº 6: Incompatibilidade de classes. Se a classe foi renomeada ou movida para outro pacote, a desserialização não funcionará — é necessário que o nome e o pacote da classe gravada no fluxo coincidam exatamente.
GO TO FULL VERSION