Qual é a diferença entre Serializable e Externalizable em Java?

Respostas:

267

Para adicionar às outras respostas, implementando java.io.Serializable , você obtém o recurso de serialização "automática" para objetos de sua classe. Não há necessidade de implementar nenhuma outra lógica, apenas funcionará. O tempo de execução do Java usará a reflexão para descobrir como organizar e remover a organização de seus objetos.

Na versão anterior do Java, a reflexão era muito lenta e, portanto, serializar grandes gráficos de objetos (por exemplo, em aplicativos RMI cliente-servidor) era um problema de desempenho. Para lidar com essa situação, java.io.Externalizablefoi fornecida a interface, que é semelhante a java.io.Serializablemecanismos escritos sob medida para executar as funções de empacotamento e descompactação (você precisa implementar readExternalewriteExternal métodos em sua classe). Isso fornece os meios para contornar o gargalo do desempenho da reflexão.

Nas versões recentes do Java (1.3 em diante, certamente), o desempenho da reflexão é muito melhor do que costumava ser e, portanto, isso é muito menos problemático. Eu suspeito que você seria pressionado para obter um benefício significativo deExternalizable com uma JVM moderna.

Além disso, o mecanismo de serialização Java embutido não é o único; você pode obter substituições de terceiros, como a JBoss Serialization, que é consideravelmente mais rápida e é uma substituição do padrão.

Uma grande desvantagem Externalizableé que você deve manter essa lógica - se você adicionar, remover ou alterar um campo em sua classe, precisará alterar seus writeExternal/ readExternalmétodos para dar conta disso.

Em resumo, Externalizableé uma relíquia dos Java 1.1 dias. Realmente não há mais necessidade disso.

skaffman
fonte
61
Não de acordo com esses benchmarks: [ code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking] , a serialização manual (usando externizable) é muito, muito mais rápida do que a serialização padrão do java. Se a velocidade é importante para o seu trabalho, escreva definitivamente seu próprio serializador.
volni
6
atualizar a nova ligação github.com/eishay/jvm-serializers/wiki sugerido por @jack
noquery
3
O "java-manual" em github.com/eishay/jvm-serializers/wiki se não usar Externalizable, o que implicaria usando ObjectOutputStream. Consulte github.com/eishay/jvm-serializers/wiki/ToolBehavior para obter um link para o código. Em vez disso, é o código escrito à mão que usa o DataOutputStream, portanto não sofre com as coisas que tornam o ObjectOutputStream lento (como acompanhar as instâncias de objetos e dar suporte aos ciclos de objetos).
Esko Luontola 23/05
7
Ter que manter a lógica é uma desvantagem se a classe nunca mudar e você nunca precisar ler versões persistentes de dados antigos. Se você quer liberdade para mudar de classe sem ter que escrever códigos infernais para desserializar versões antigas, isso Externalizableajuda bastante .
precisa
2
Eu apenas tive que escrever uma coleção personalizada e devo dizer que isso Externalizableme convém muito melhor, pois não quero gerar matrizes com espaços vazios ou objetos de espaço reservado, além da interface explícita de que você pode lidar com herança, o que significa que meu sub sincronizado -class pode facilmente adicionar bloqueio à chamada writeExternal(). Então, o Externalizable ainda é muito relevante, certamente para objetos grandes ou complexos.
Haravikk
37

A serialização fornece a funcionalidade padrão para armazenar e recriar posteriormente o objeto. Ele usa o formato detalhado para definir o gráfico inteiro dos objetos a serem armazenados, por exemplo, suponha que você tenha uma lista vinculada e codifique como abaixo, a serialização padrão descobrirá todos os objetos que estão vinculados e serializará. Na serialização padrão, o objeto é construído inteiramente a partir de seus bits armazenados, sem chamadas do construtor.

  ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("/Users/Desktop/files/temp.txt"));
        oos.writeObject(linkedListHead); //writing head of linked list
        oos.close();

Mas se você deseja serialização restrita ou não deseja que parte do seu objeto seja serializada, use Externalizable. A interface Externalizável estende a interface Serializável e adiciona dois métodos, writeExternal () e readExternal (). Eles são chamados automaticamente durante a serialização ou desserialização. Ao trabalhar com Externalizable, devemos lembrar que o construtor padrão deve ser público; caso contrário, o código gerará uma exceção. Por favor, siga o código abaixo:

public class MyExternalizable implements Externalizable
{

private String userName;
private String passWord;
private Integer roll;

public MyExternalizable()
{

}

public MyExternalizable(String userName, String passWord, Integer roll)
{
    this.userName = userName;
    this.passWord = passWord;
    this.roll = roll;
}

@Override
public void writeExternal(ObjectOutput oo) throws IOException 
{
    oo.writeObject(userName);
    oo.writeObject(roll);
}

@Override
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException 
{
    userName = (String)oi.readObject();
    roll = (Integer)oi.readObject();
}

public String toString()
{
    StringBuilder b = new StringBuilder();
    b.append("userName: ");
    b.append(userName);
    b.append("  passWord: ");
    b.append(passWord);
    b.append("  roll: ");
    b.append(roll);

    return b.toString();
}
public static void main(String[] args)
{
    try
    {
        MyExternalizable m  = new MyExternalizable("nikki", "student001", 20);
        System.out.println(m.toString());
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/Desktop/files/temp1.txt"));
        oos.writeObject(m);
        oos.close();

        System.out.println("***********************************************************************");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/Desktop/files/temp1.txt"));
        MyExternalizable mm = (MyExternalizable)ois.readObject();
        mm.toString();
        System.out.println(mm.toString());
    } 
    catch (ClassNotFoundException ex) 
    {
        Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
    }
    catch(IOException ex)
    {
        Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
    }
}
}

Aqui, se você comentar o construtor padrão, o código será exibido abaixo da exceção:

 java.io.InvalidClassException: javaserialization.MyExternalizable;     
 javaserialization.MyExternalizable; no valid constructor.

Podemos observar que, como a senha é uma informação sensível, não a serializo no método writeExternal (ObjectOutput oo) e não configuro o valor igual em readExternal (ObjectInput oi). Essa é a flexibilidade fornecida pelo Externalizable.

A saída do código acima é conforme abaixo:

userName: nikki  passWord: student001  roll: 20
***********************************************************************
userName: nikki  passWord: null  roll: 20

Podemos observar que não estamos definindo o valor de passWord, portanto é nulo.

O mesmo também pode ser alcançado declarando o campo da senha como transitório.

private transient String passWord;

Espero que ajude. Peço desculpas se cometi algum erro. Obrigado.

Tentando
fonte
22

Principais diferenças entre SerializableeExternalizable

  1. Interface do marcador : Serializableé a interface do marcador sem nenhum método. ExternalizableA interface contém dois métodos: writeExternal()e readExternal().
  2. Processo de serialização : o processo de serialização padrão será ativado para as classes que implementam a Serializableinterface. O processo de serialização definido pelo programador será ativado nas classes que implementam a Externalizableinterface.
  3. Manutenção : Alterações incompatíveis podem interromper a serialização.
  4. Compatibilidade com versões anteriores e controle : se você precisar oferecer suporte a várias versões, poderá ter controle total com a Externalizableinterface. Você pode suportar versões diferentes do seu objeto. Se você implementar Externalizable, é sua responsabilidade serializar a superclasse
  5. public No-arg constructor : Serializableusa reflexão para construir objeto e não requer nenhum construtor arg. Mas Externalizableexige um construtor público sem argumentos.

Consulte o blog por Hitesh Gargpara mais detalhes.

Ravindra babu
fonte
1
(3) está incorreto. Há um grande repertório de alterações que você pode fazer em uma classe sem interromper a desserialização de objetos existentes, e adicionar membros serializáveis ​​é certamente um deles. Excluí-los é outra. Consulte o capítulo Versão do Objeto da Especificação de Serialização de Objetos Java.
Marquês de Lorne
1
Frase reformulada.
Ravindra babu
Obrigado por compartilhar o link com as especificações de serialização.
JL_SO 20/08/19
21

A serialização usa certos comportamentos padrão para armazenar e depois recriar o objeto. Você pode especificar em que ordem ou como lidar com referências e estruturas de dados complexas, mas eventualmente se resume ao uso do comportamento padrão para cada campo de dados primitivo.

A externalização é usada nos casos raros em que você realmente deseja armazenar e reconstruir seu objeto de uma maneira completamente diferente e sem usar os mecanismos de serialização padrão para os campos de dados. Por exemplo, imagine que você tivesse seu próprio esquema de codificação e compactação.

Uri
fonte
5
Usamos Externalizable para grandes coleções de "IDs selecionados" - era muito mais eficiente externalizar como contagem mais ou menos e uma matriz de entradas primitivas do que serialização padrão. É um caso de uso muito simples, nada de "especial" ou "único".
31413 Thomas Thomas W
9

A serialização de objetos usa as interfaces serializáveis ​​e externalizáveis. Um objeto Java é apenas serializável. se uma classe ou qualquer uma de suas superclasses implementa a interface java.io.Serializable ou sua subinterface, java.io.Externalizable. A maioria da classe java é serializável .

  • NotSerializableException: packageName.ClassName«Para participar de um objeto de classe no processo de serialização, a classe deve implementar a interface serializável ou externalizável.

insira a descrição da imagem aqui


Interface serializável

A serialização de objetos produz um fluxo com informações sobre as classes Java para os objetos que estão sendo salvos. Para objetos serializáveis, informações suficientes são mantidas para restaurar esses objetos, mesmo que uma versão diferente (mas compatível) da implementação da classe esteja presente. A interface serializável é definida para identificar as classes que implementam o protocolo serializável:

package java.io;

public interface Serializable {};
  • A interface de serialização não possui métodos ou campos e serve apenas para identificar a semântica de ser serializável. Para serializar / desserializar uma classe, podemos usar os métodos writeObject e readObject padrão (ou) podemos substituir os métodos writeObject e readObject de uma classe.
  • A JVM terá controle completo na serialização do objeto. use a palavra - chave transitória para impedir que o membro de dados seja serializado.
  • Aqui, os objetos serializáveis ​​são reconstruídos diretamente do fluxo sem executar
  • InvalidClassException«No processo de desserialização, se o valor serialVersionUID da classe local for diferente da classe do remetente correspondente. então o resultado está em conflito como java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
  • Os valores dos campos não transitórios e não estáticos da classe são serializados.

Interface Externalizável

Para objetos Externalizáveis, apenas a identidade da classe do objeto é salva pelo contêiner; a classe deve salvar e restaurar o conteúdo. A interface Externalizável é definida da seguinte maneira:

package java.io;

public interface Externalizable extends Serializable
{
    public void writeExternal(ObjectOutput out)
        throws IOException;

    public void readExternal(ObjectInput in)
        throws IOException, java.lang.ClassNotFoundException;
}
  • A interface Externalizável possui dois métodos, um objeto externalizável deve implementar os métodos writeExternal e readExternal para salvar / restaurar o estado de um objeto.
  • O programador precisa cuidar de quais objetos serão serializados. Como um programador cuida da serialização Portanto, aqui a palavra-chave transitória não restringirá nenhum objeto no processo de serialização.
  • Quando um objeto Externalizável é reconstruído, uma instância é criada usando o construtor público no-arg e, em seguida, o método readExternal chamado. Objetos serializáveis ​​são restaurados lendo-os em um ObjectInputStream.
  • OptionalDataException«Os campos DEVEM ESTAR NA MESMA ORDEM E TIPO, como escrevemos. Se houver alguma incompatibilidade de tipo no fluxo, ele lançará OptionalDataException.

    @Override public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt( id );
        out.writeUTF( role );
        out.writeObject(address);
    }
    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.id = in.readInt();
        this.address = (Address) in.readObject();
        this.role = in.readUTF();
    }
  • Os campos de instância da classe que gravaram (expostos) para ObjectOutputserem serializados.


Exemplo « implementa Serializable

class Role {
    String role;
}
class User extends Role implements Serializable {

    private static final long serialVersionUID = 5081877L;
    Integer id;
    Address address;

    public User() {
        System.out.println("Default Constructor get executed.");
    }
    public User( String role ) {
        this.role = role;
        System.out.println("Parametarised Constructor.");
    }
}

class Address implements Serializable {

    private static final long serialVersionUID = 5081877L;
    String country;
}

Exemplo « implementa Externalizável

class User extends Role implements Externalizable {

    Integer id;
    Address address;
    // mandatory public no-arg constructor
    public User() {
        System.out.println("Default Constructor get executed.");
    }
    public User( String role ) {
        this.role = role;
        System.out.println("Parametarised Constructor.");
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt( id );
        out.writeUTF( role );
        out.writeObject(address);
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.id = in.readInt();
        this.address = (Address) in.readObject();
        this.role = in.readUTF();
    }
}

Exemplo

public class CustomClass_Serialization {
    static String serFilename = "D:/serializable_CustomClass.ser";

    public static void main(String[] args) throws IOException {
        Address add = new Address();
        add.country = "IND";

        User obj = new User("SE");
        obj.id = 7;
        obj.address = add;

        // Serialization
        objects_serialize(obj, serFilename);
        objects_deserialize(obj, serFilename);

        // Externalization
        objects_WriteRead_External(obj, serFilename);
    }

    public static void objects_serialize( User obj, String serFilename ) throws IOException{
        FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );

        // java.io.NotSerializableException: com.github.objects.Address
        objectOut.writeObject( obj );
        objectOut.flush();
        objectOut.close();
        fos.close();

        System.out.println("Data Stored in to a file");
    }
    public static void objects_deserialize( User obj, String serFilename ) throws IOException{
        try {
            FileInputStream fis = new FileInputStream( new File( serFilename ) );
            ObjectInputStream ois = new ObjectInputStream( fis );
            Object readObject;
            readObject = ois.readObject();
            String calssName = readObject.getClass().getName();
            System.out.println("Restoring Class Name : "+ calssName); // InvalidClassException

            User user = (User) readObject;
            System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);

            Address add = (Address) user.address;
            System.out.println("Inner Obj : "+ add.country );
            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void objects_WriteRead_External( User obj, String serFilename ) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File( serFilename ));
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );

        obj.writeExternal( objectOut );
        objectOut.flush();

        fos.close();

        System.out.println("Data Stored in to a file");

        try {
            // create a new instance and read the assign the contents from stream.
            User user = new User();

            FileInputStream fis = new FileInputStream(new File( serFilename ));
            ObjectInputStream ois = new ObjectInputStream( fis );

            user.readExternal(ois);

            System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);

            Address add = (Address) user.address;
            System.out.println("Inner Obj : "+ add.country );
            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

@Vejo

Yash
fonte
7

A interface Externalizável não foi realmente fornecida para otimizar o desempenho do processo de serialização! mas para fornecer meios de implementar seu próprio processamento personalizado e oferecer controle completo sobre o formato e o conteúdo do fluxo de um objeto e seus supertipos!

Exemplos disso é a implementação do sistema de comunicação remota AMF (ActionScript Message Format) para transferir objetos de script de ação nativo pela rede.

Ali Joudeh
fonte
7

https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html

A serialização padrão é um tanto detalhada e assume o cenário de uso mais amplo possível do objeto serializado e, portanto, o formato padrão (Serializable) anota o fluxo resultante com informações sobre a classe do objeto serializado.

A externalização fornece ao produtor do fluxo de objetos controle completo sobre os metadados precisos da classe (se houver) além da identificação mínima exigida da classe (por exemplo, seu nome). Isso é claramente desejável em determinadas situações, como ambientes fechados, onde o produtor do fluxo de objetos e seu consumidor (que reifica o objeto do fluxo) são correspondidos, e os metadados adicionais sobre a classe não servem para nada e prejudicam o desempenho.

Além disso (como Uri salienta), a externalização também fornece controle completo sobre a codificação dos dados no fluxo correspondente aos tipos Java. Para um exemplo (artificial), você pode querer registrar boolean true como 'Y' e false como 'N'. A externalização permite que você faça isso.

alphazero
fonte
2

Ao considerar opções para melhorar o desempenho, não se esqueça da serialização personalizada. Você pode deixar o Java fazer o que faz bem, ou pelo menos ser bom o suficiente, de graça , e fornecer suporte personalizado para o que faz mal. Geralmente, esse código é muito menor que o suporte total a Externalizável.

Ed Staub
fonte
2

Existem tantas diferenças entre Serializable e Externalizable, mas quando comparamos a diferença entre Serializable personalizado (writeObject (substituído por readObject () e readObject ()) e Externalizable, descobrimos que a implementação personalizada está fortemente ligada à classe ObjectOutputStream, onde, como no caso Externalizable, nós mesmos forneça uma implementação de ObjectOutput que pode ser a classe ObjectOutputStream ou pode ser outra, como org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream

No caso de interface Externalizável

@Override
public void writeExternal(ObjectOutput out) throws IOException {
    out.writeUTF(key);
    out.writeUTF(value);
    out.writeObject(emp);
}

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    this.key = in.readUTF();
    this.value = in.readUTF();
    this.emp = (Employee) in.readObject();
}





**In case of Serializable interface**


        /* 
             We can comment below two method and use default serialization process as well
             Sequence of class attributes in read and write methods MUST BE same.
        // below will not work it will not work . 
        // Exception = java.io.StreamCorruptedException: invalid type code: 00\
              private void writeObject(java.io.ObjectOutput stream) 
              */
            private void writeObject(java.io.ObjectOutputStream Outstream)
                    throws IOException {

                System.out.println("from writeObject()");
                /*     We can define custom validation or business rules inside read/write methods.
 This way our validation methods will be automatically 
    called by JVM, immediately after default serialization 
    and deserialization process 
    happens.
                 checkTestInfo();
                */

                stream.writeUTF(name);
                stream.writeInt(age);
                stream.writeObject(salary);
                stream.writeObject(address);
            }

            private void readObject(java.io.ObjectInputStream Instream)
                    throws IOException, ClassNotFoundException {
                System.out.println("from readObject()");
                name = (String) stream.readUTF();
                age = stream.readInt();
                salary = (BigDecimal) stream.readObject();
                address = (Address) stream.readObject();
                // validateTestInfo();
            }

Eu adicionei um código de exemplo para explicar melhor. verifique / in / out objeto caso Externalizable. Eles não estão vinculados a nenhuma implementação diretamente.
Onde Outstream / Instream estão fortemente vinculados a classes. Podemos estender ObjectOutputStream / ObjectInputStream, mas será um pouco difícil de usar.

Ashish Sharma
fonte
1
Você poderia elaborar mais? Enquanto o leio, não entendo o que você está tentando dizer. Além disso, se você puder formatar o texto com alguns parágrafos e exemplos, essa poderá ser uma ótima resposta.
21417 Shirkam
0

Basicamente, Serializableé uma interface de marcador que implica que uma classe é segura para serialização e a JVM determina como ela é serializada. Externalizablecontém 2 métodos readExternale writeExternal. Externalizablepermite que o implementador decida como um objeto é serializado, onde como Serializableserializa objetos da maneira padrão.

Pika, o Mago das Baleias
fonte
0

Algumas diferenças:

  1. Para serialização, não há necessidade do construtor padrão dessa classe, porque Object porque a JVM constrói o mesmo com a ajuda da API de reflexão. No caso de externalização, o contratador sem argumento é necessário, porque o controle está na mão do programmar e, posteriormente, atribui os dados desserializados ao objeto por meio de setters.

  2. Na serialização, se o usuário desejar pular certas propriedades a serem serializadas, precisará marcar essas propriedades como transitórias, e vice-versa não será necessário para Externalização.

  3. Quando o suporte à compatibilidade com versões anteriores é esperado para qualquer classe, é recomendável usar o Externalizable. A serialização suporta a persistência de defaultObject e, se a estrutura do objeto estiver quebrada, causará problemas durante a desserialização.

Neeraj
fonte