1.Criar uma classe simples serializável
Implemente Serializable e serialize/deserialize um objeto usando ObjectOutputStream e ObjectInputStream.
2. Testar a compatibilidade de versão
Serializar um objeto de uma versão antiga da classe e tentar desserializá-lo após uma mudança na estrutura da classe.
Testar o comportamento do serialVersionUID.
3. Criar uma forma serializada personalizada
Usar writeObject e readObject para definir manualmente a serialização de uma classe.
4. Explorar riscos de segurança
Criar um objeto vulnerável a ataques via serialização (exemplo: permitir modificação de campos privados via desserialização).
Corrigir a vulnerabilidade implementando readObject.
5. Comparar serialização com outras abordagens
Implementar persistência de um objeto com JSON (Gson, Jackson) e comparar com a serialização Java.
1. Faça uma classe serializável e demonstre a serialização e desserialização de um objeto dessa classe.
import java.io.*;// Classe serializávelclass Pessoa implements Serializable { private static final long serialVersionUID = 1L; // UID para manter compatibilidade private String nome; private int idade; public Pessoa(String nome, int idade) { this.nome = nome; this.idade = idade; } @Override public String toString() { return "Pessoa{nome='" + nome + "', idade=" + idade + "}"; }}public class SerializacaoExemplo { public static void main(String[] args) { Pessoa pessoa = new Pessoa("João", 30); // Serializar objeto try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("pessoa.ser"))) { out.writeObject(pessoa); System.out.println("Objeto serializado com sucesso!"); } catch (IOException e) { e.printStackTrace(); } // Desserializar objeto try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("pessoa.ser"))) { Pessoa pessoaLida = (Pessoa) in.readObject(); System.out.println("Objeto desserializado: " + pessoaLida); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } }}
📌 Explicação: Esse código cria uma classe Pessoa serializável, salva um objeto em um arquivo e depois o lê de volta.
2. Modifique a classe do exercício anterior para incluir um novo campo e demonstre o problema de compatibilidade ao desserializar um objeto antigo.
import java.io.*;class Pessoa implements Serializable { private static final long serialVersionUID = 1L; // UID fixo para manter compatibilidade private String nome; private int idade; private String email; // Novo campo adicionado public Pessoa(String nome, int idade, String email) { this.nome = nome; this.idade = idade; this.email = email; } @Override public String toString() { return "Pessoa{nome='" + nome + "', idade=" + idade + ", email='" + email + "'}"; }}public class SerializacaoProblema { public static void main(String[] args) { // Tentativa de desserializar um objeto criado antes da adição do campo 'email' try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("pessoa.ser"))) { Pessoa pessoaLida = (Pessoa) in.readObject(); System.out.println("Objeto desserializado: " + pessoaLida); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } }}
📌 Explicação: Se um objeto da versão anterior (sem email) for desserializado, pode causar InvalidClassException, pois o serialVersionUID e a estrutura da classe mudaram.
3. Corrija o problema do exercício anterior implementando uma forma serializada compatível usando serialVersionUID.
import java.io.*;class Pessoa implements Serializable { private static final long serialVersionUID = 1L; private String nome; private int idade; private transient String email; // Campo transient não será serializado public Pessoa(String nome, int idade, String email) { this.nome = nome; this.idade = idade; this.email = email; } @Override public String toString() { return "Pessoa{nome='" + nome + "', idade=" + idade + "', email='" + (email != null ? email : "N/A") + "'}"; }}public class SerializacaoCorrigida { public static void main(String[] args) { // Desserializar objeto antigo sem email try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("pessoa.ser"))) { Pessoa pessoaLida = (Pessoa) in.readObject(); System.out.println("Objeto desserializado: " + pessoaLida); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } }}
📌 Explicação: O campo email foi marcado como transient, o que impede sua serialização e mantém a compatibilidade.
4. Demonstre como a serialização pode ser uma falha de segurança permitindo a modificação de um objeto privado.
import java.io.*;class Usuario implements Serializable { private static final long serialVersionUID = 1L; private String nome; private String senha; // Senha não protegida! public Usuario(String nome, String senha) { this.nome = nome; this.senha = senha; } @Override public String toString() { return "Usuario{nome='" + nome + "', senha='" + senha + "'}"; }}public class FalhaSeguranca { public static void main(String[] args) { Usuario usuario = new Usuario("admin", "1234"); // Serializar usuário try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("usuario.ser"))) { out.writeObject(usuario); } catch (IOException e) { e.printStackTrace(); } // Modificar o arquivo manualmente e desserializar (Simulando um ataque) try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("usuario.ser"))) { Usuario usuarioHackeado = (Usuario) in.readObject(); System.out.println("Usuário comprometido: " + usuarioHackeado); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } }}
📌 Explicação: A senha é armazenada em texto plano e pode ser extraída ou modificada por um invasor ao alterar o arquivo serializado.
5. Corrija o problema de segurança do exercício anterior tornando os campos sensíveis transient e implementando writeObject() e readObject().
import java.io.*;class UsuarioSeguro implements Serializable { private static final long serialVersionUID = 1L; private String nome; private transient String senha; // Agora a senha não será serializada public UsuarioSeguro(String nome, String senha) { this.nome = nome; this.senha = senha; } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(encrypt(senha)); // Salva senha criptografada } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); senha = decrypt((String) in.readObject()); // Recupera senha descriptografada } private String encrypt(String senha) { return senha == null ? null : new StringBuilder(senha).reverse().toString(); // Simples inversão } private String decrypt(String senhaCriptografada) { return senhaCriptografada == null ? null : new StringBuilder(senhaCriptografada).reverse().toString(); } @Override public String toString() { return "UsuarioSeguro{nome='" + nome + "', senha='" + senha + "'}"; }}public class SegurancaCorrigida { public static void main(String[] args) { UsuarioSeguro usuario = new UsuarioSeguro("admin", "1234"); // Serializar try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("usuarioSeguro.ser"))) { out.writeObject(usuario); } catch (IOException e) { e.printStackTrace(); } // Desserializar try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("usuarioSeguro.ser"))) { UsuarioSeguro usuarioLido = (UsuarioSeguro) in.readObject(); System.out.println("Usuário seguro: " + usuarioLido); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } }}
📌 Explicação: O campo senha agora é transient, e os métodos writeObject() e readObject() aplicam uma criptografia simples.
5. Comparar serialização com outras abordagens
5.1 Persistência usando Serialização Java
import java.io.*;// Classe serializávelclass Pessoa implements Serializable { private static final long serialVersionUID = 1L; private String nome; private int idade; public Pessoa(String nome, int idade) { this.nome = nome; this.idade = idade; } @Override public String toString() { return "Pessoa{nome='" + nome + "', idade=" + idade + "}"; }}public class SerializacaoJava { public static void main(String[] args) { Pessoa pessoa = new Pessoa("João", 30); // Serializar em arquivo try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("pessoa.ser"))) { out.writeObject(pessoa); System.out.println("Objeto serializado: " + pessoa); } catch (IOException e) { e.printStackTrace(); } // Desserializar do arquivo try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("pessoa.ser"))) { Pessoa pessoaLida = (Pessoa) in.readObject(); System.out.println("Objeto desserializado: " + pessoaLida); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } }}
📌 Pontos positivos: Integrado ao Java, eficiente em binário.
📌 Pontos negativos: Problemas de compatibilidade ao mudar a classe, não legível para humanos.
5.2 Persistência usando JSON com Gson
import com.google.gson.Gson;import com.google.gson.GsonBuilder;import java.io.*;// Classe normal sem Serializableclass PessoaGson { private String nome; private int idade; public PessoaGson(String nome, int idade) { this.nome = nome; this.idade = idade; } @Override public String toString() { return "PessoaGson{nome='" + nome + "', idade=" + idade + "}"; }}public class PersistenciaGson { public static void main(String[] args) { PessoaGson pessoa = new PessoaGson("João", 30); Gson gson = new GsonBuilder().setPrettyPrinting().create(); // Serializar em JSON try (FileWriter writer = new FileWriter("pessoa.json")) { gson.toJson(pessoa, writer); System.out.println("Objeto serializado em JSON:\n" + gson.toJson(pessoa)); } catch (IOException e) { e.printStackTrace(); } // Desserializar do JSON try (Reader reader = new FileReader("pessoa.json")) { PessoaGson pessoaLida = gson.fromJson(reader, PessoaGson.class); System.out.println("Objeto desserializado: " + pessoaLida); } catch (IOException e) { e.printStackTrace(); } }}
📌 Pontos positivos: Legível, fácil de integrar com APIs e persistência de dados.
📌 Pontos negativos: JSON ocupa mais espaço e é um pouco mais lento que binário.
5.3 Persistência usando JSON com Jackson
import com.fasterxml.jackson.databind.ObjectMapper;import java.io.*;// Classe normal sem Serializableclass PessoaJackson { private String nome; private int idade; public PessoaJackson() {} // Jackson precisa de um construtor vazio public PessoaJackson(String nome, int idade) { this.nome = nome; this.idade = idade; } @Override public String toString() { return "PessoaJackson{nome='" + nome + "', idade=" + idade + "}"; }}public class PersistenciaJackson { public static void main(String[] args) { PessoaJackson pessoa = new PessoaJackson("João", 30); ObjectMapper objectMapper = new ObjectMapper(); // Serializar em JSON try { objectMapper.writeValue(new File("pessoa_jackson.json"), pessoa); System.out.println("Objeto serializado em JSON com Jackson"); } catch (IOException e) { e.printStackTrace(); } // Desserializar do JSON try { PessoaJackson pessoaLida = objectMapper.readValue(new File("pessoa_jackson.json"), PessoaJackson.class); System.out.println("Objeto desserializado: " + pessoaLida); } catch (IOException e) { e.printStackTrace(); } }}
📌 Pontos positivos: Alto desempenho, flexível, suporta diversas configurações.
📌 Pontos negativos: Requer bibliotecas externas, pode exigir configurações extras.
5.4 Comparação das abordagens
📌 Conclusão:
- A serialização Java é eficiente, mas tem problemas de compatibilidade e não é legível.
- O Gson é simples, fácil de usar e ideal para APIs.
- O Jackson tem melhor desempenho e é mais configurável, sendo ótimo para grandes aplicações.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse