Serialização e Desserialização em Java

JavaBeginner
Pratique Agora

Introdução

Serialização e desserialização são processos importantes em Java que nos permitem converter um objeto em um fluxo de bytes e restaurá-lo de volta em um objeto, respectivamente. Podemos usar serialização e desserialização para salvar ou persistir o estado de objetos, ou para transmitir objetos através de uma rede. Neste laboratório, aprenderemos como serializar e desserializar objetos em Java usando as classes ObjectInputStream e ObjectOutputStream.

Implementar a Interface Serializable

Para tornar nossos objetos serializáveis, precisamos implementar a interface Serializable. Esta é uma interface de marcador (marker interface) que não exige que nenhum método seja implementado. A interface Serializable é usada para indicar à JVM que os objetos desta classe podem ser serializados.

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;

    // Constructor and getter/setter methods
}

Criar Objetos para Serializar

Crie alguns objetos da classe Student que serializaremos nos próximos passos.

Student student1 = new Student("Alice", 22);
Student student2 = new Student("Bob", 21);

Serializar Objetos

Para serializar um objeto, precisamos criar uma instância de ObjectOutputStream e chamar seu método writeObject(), passando o objeto que queremos serializar. Isso escreverá o objeto como um fluxo de bytes em um arquivo que podemos usar mais tarde para desserialização.

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

public class SerializationDemo {
    public static void main(String[] args) {
        try {
            FileOutputStream fileOut = new FileOutputStream("students.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);

            out.writeObject(student1);
            out.writeObject(student2);

            out.close();
            fileOut.close();

            System.out.println("Serialized data is saved in students.ser file");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Desserializar Objetos

Para desserializar um objeto, precisamos criar uma instância de ObjectInputStream e chamar seu método readObject() para ler o fluxo de bytes do arquivo que serializamos anteriormente. Isso retornará um Object, que podemos então converter para o tipo de classe original.

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;

public class SerializationDemo {
    public static void main(String[] args) {
        try {
            FileInputStream fileIn = new FileInputStream("students.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);

            Student student1 = (Student) in.readObject();
            Student student2 = (Student) in.readObject();

            in.close();
            fileIn.close();

            // Print the students
            System.out.println(student1);
            System.out.println(student2);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Lidar com Campos Transitórios

Se tivermos algum campo em nossa classe que não queremos serializar, podemos declará-los como transient. Campos transient são ignorados durante a serialização e desserialização. Por exemplo, vamos adicionar um campo transient à nossa classe Student que não queremos serializar:

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;
    private transient String password;

    //Constructor, getter/setter methods
}

Personalizar Serialização e Desserialização

Também podemos personalizar o processo de serialização e desserialização implementando os métodos writeObject() e readObject(). Isso pode ser útil se quisermos lidar com campos transient, ou se quisermos realizar processamento adicional durante a serialização e desserialização.

private void writeObject(ObjectOutputStream oos) throws IOException {
    // Default serialization
    oos.defaultWriteObject();

    // Encrypt password before writing to file
    String encryptedPassword = encrypt(password);
    oos.writeObject(encryptedPassword);
}

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    // Default deserialization
    ois.defaultReadObject();

    // Decrypt password after reading from file
    String encryptedPassword = (String) ois.readObject();
    password = decrypt(encryptedPassword);
}

Gerenciar Versionamento

Se atualizarmos nossa classe adicionando ou removendo propriedades, então a versão da classe muda, o que pode causar erros ao desserializar objetos mais antigos. Podemos lidar com o versionamento usando o campo serialVersionUID para especificar um identificador único para nossa classe. Isso garantirá que a serialização e desserialização funcionem corretamente, mesmo que a definição da classe seja alterada.

import java.io.Serializable;

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;

    //Constructor, getter/setter methods
}

Executar o Código

Compile a classe SerializationDemo e execute-a no terminal usando o seguinte comando:

javac SerializationDemo.java
java SerializationDemo

A saída deve mostrar os dados serializados e os objetos desserializados.

Modificar a Classe Estudante

Atualize a classe Student adicionando ou removendo um campo:

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private String email; // Adicionado um campo

    //Constructor, getter/setter methods
}

Gerenciar Versionamento com SerialVersionUID

Agora que modificamos nossa classe, precisamos lidar com o versionamento atualizando o serialVersionUID. Isso garantirá que ainda possamos desserializar objetos que foram serializados antes da classe ser modificada.

public class Student implements Serializable {
    private static final long serialVersionUID = 2L;

    private String name;
    private int age;
    private String email; // Adicionado um campo

    //Constructor, getter/setter methods
}

Resumo

Neste laboratório, aprendemos como serializar e desserializar objetos em Java usando ObjectInputStream e ObjectOutputStream. Também aprendemos como lidar com campos transient, personalizar o processo de serialização e desserialização e lidar com o versionamento usando serialVersionUID. Serialização e desserialização são processos importantes para salvar e transmitir dados em Java, e podemos usá-los para armazenar o estado atual de nossos objetos e, posteriormente, restaurá-los conforme necessário.