Jaka jest różnica między serializowalnym a zewnętrznym w Javie?

Odpowiedzi:

267

Aby dodać do innych odpowiedzi, implementując java.io.Serializable, otrzymujesz możliwość „automatycznej” serializacji obiektów klasy. Nie trzeba implementować żadnej innej logiki, po prostu będzie działać. Środowisko wykonawcze Java użyje refleksji, aby dowiedzieć się, jak organizować i rozmontowywać obiekty.

We wcześniejszej wersji Javy odbicie było bardzo powolne, dlatego serializowanie dużych wykresów obiektowych (np. W aplikacjach RMI klient-serwer) stanowiło trochę problem z wydajnością. Aby poradzić sobie z tą sytuacją, java.io.Externalizablezapewniono interfejs, który jest podobny, java.io.Serializableale z niestandardowymi mechanizmami do wykonywania funkcji rozróżniania i rozróżniania (musisz zaimplementować readExternali writeExternalmetody w swojej klasie). Dzięki temu można obejść wąskie gardło związane z wydajnością odbicia.

W najnowszych wersjach Javy (z pewnością od wersji 1.3) wydajność odbicia jest znacznie lepsza niż kiedyś, więc jest to o wiele mniejszy problem. Podejrzewam, że trudno byłoby ci uzyskać znaczącą korzyść Externalizablez nowoczesnej maszyny JVM.

Ponadto, wbudowany mechanizm serializacji Java nie jest jedynym, można uzyskać zamienniki innych firm, takie jak JBoss Serialization, który jest znacznie szybszy i zastępuje domyślnie.

Dużym minusem Externalizablejest to, że musisz sam utrzymywać tę logikę - jeśli dodajesz, usuwasz lub zmieniasz pole w swojej klasie, musisz zmienić metody writeExternal/ readExternal, aby to uwzględnić.

Podsumowując, Externalizablejest reliktem Java 1.1 dni. Naprawdę nie ma już takiej potrzeby.

skaffman
źródło
61
Zgodnie z tymi testami : [ code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking] ręczna serializacja (przy użyciu możliwej do interpretacji) jest znacznie szybsza niż przy użyciu domyślnej serializacji Java. Jeśli prędkość ma znaczenie dla Twojej pracy, zdecydowanie napisz własny serializator.
volni
6
aktualizacja do nowego linku github.com/eishay/jvm-serializers/wiki zasugerowany przez @Jack
noquery
3
W „java-podręcznik” w github.com/eishay/jvm-serializers/wiki ma nie używać Externalizable, co oznaczałoby użyciu ObjectOutputStream. Zobacz github.com/eishay/jvm-serializers/wiki/ToolBehavior o link do kodu. Zamiast tego jest to odręczny kod, który używa DataOutputStream, więc nie cierpi z powodu rzeczy, które spowalniają ObjectOutputStream (takich jak śledzenie instancji obiektów i wspieranie cykli obiektów).
Esko Luontola
7
Konieczność samodzielnego utrzymywania logiki jest tylko wadą, jeśli klasa nigdy się nie zmienia i nigdy nie musisz czytać utrwalonych wersji starych danych. Jeśli chcesz swobodę zmienić profesję bez konieczności pisania kodu piekielną deserializacji starych wersjach nim, Externalizablepomaga wiele .
Tim Boudreau,
2
Po prostu musiałem napisać własną kolekcję i muszę powiedzieć, że Externalizablebardziej mi odpowiada, ponieważ nie chcę wyprowadzać tablic z pustymi spacjami lub obiektami zastępczymi, a także z jawnym interfejsem, w którym można obsłużyć dziedziczenie, co oznacza, że ​​mój zsynchronizowany sub -klasa może łatwo dodać blokowanie wokół połączenia do writeExternal(). Tak, Eksternalizowalny jest nadal bardzo istotny, z pewnością w przypadku dużych lub złożonych obiektów.
Haravikk
37

Serializacja zapewnia domyślną funkcjonalność do przechowywania, a następnie odtworzenia obiektu. Używa pełnego formatu, aby zdefiniować cały wykres obiektów, które mają być przechowywane, np. Załóżmy, że masz linkedList i kodujesz jak poniżej, wówczas domyślna serializacja wykryje wszystkie połączone obiekty i serializuje. W domyślnej serializacji obiekt jest zbudowany w całości z przechowywanych bitów, bez wywołań konstruktora.

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

Ale jeśli chcesz ograniczyć serializację lub nie chcesz, aby jakaś część twojego obiektu była serializowana, użyj opcji Zewnętrzna. Interfejs zewnętrzny umożliwia rozszerzenie interfejsu szeregowego i dodaje dwie metody: writeExternal () i readExternal (). Są one automatycznie wywoływane podczas serializacji lub deserializacji. Pracując z Externalizable powinniśmy pamiętać, że domyślny konstruktor powinien być publiczny, w przeciwnym razie kod zgłosi wyjątek. Postępuj zgodnie z poniższym kodem:

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);
    }
}
}

Jeśli skomentujesz domyślny konstruktor, kod wygeneruje wyjątek poniżej:

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

Możemy zauważyć, że ponieważ hasło jest poufną informacją, więc nie serializuję go metodą writeExternal (ObjectOutput oo) i nie ustawiam jego wartości w readExternal (ObjectInput oi). To elastyczność zapewniana przez firmę zewnętrzną.

Dane wyjściowe powyższego kodu są następujące:

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

Możemy to zaobserwować, ponieważ nie ustawiamy wartości hasła, więc jest ono puste.

To samo można również osiągnąć, deklarując pole hasła jako przejściowe.

private transient String passWord;

Mam nadzieję, że to pomoże. Przepraszam, jeśli popełniłem jakieś błędy. Dzięki.

Próbować
źródło
22

Kluczowe różnice między SerializableiExternalizable

  1. Interfejs znaczników : Serializablejest interfejsem znaczników bez żadnych metod. Externalizableinterfejs zawiera dwie metody: writeExternal()i readExternal().
  2. Proces szeregowania : Domyślny proces szeregowania zostanie uruchomiony dla klas implementujących Serializableinterfejs. Zdefiniowany przez programistę proces szeregowania zostanie uruchomiony dla klas implementujących Externalizableinterfejs.
  3. Konserwacja : niezgodne zmiany mogą przerwać serializację.
  4. Kompatybilność wsteczna i kontrola : Jeśli musisz obsługiwać wiele wersji, możesz mieć pełną kontrolę nad Externalizableinterfejsem. Możesz obsługiwać różne wersje swojego obiektu. Jeśli wdrożysz Externalizable, Twoim obowiązkiem jest serializacja superklasy
  5. publiczny konstruktor bez argonu : Serializablewykorzystuje odbicie do konstruowania obiektu i nie wymaga konstruktora arg. Ale Externalizablewymaga publicznego konstruktora bez argumentów.

Patrz blog o Hitesh Gargwięcej szczegółów.

Ravindra babu
źródło
1
(3) jest niepoprawny. Istnieje duży repertuar zmian, które można wprowadzić w klasie bez przerywania serializacji istniejących obiektów, a dodanie elementów możliwych do serializacji jest z pewnością jednym z nich. Usunięcie ich to kolejne. Zobacz rozdział Wersja obiektów w specyfikacji Java Object Serialization Specification.
Markiz Lorne
1
Zdanie ponownie sformułowane.
Ravindra babu
Dziękujemy za udostępnienie linku do specyfikacji serializacji.
JL_SO,
21

Serializacja wykorzystuje pewne domyślne zachowania do przechowywania, a następnie odtworzenia obiektu. Możesz określić, w jakiej kolejności lub w jaki sposób obsługiwać odwołania i złożone struktury danych, ale ostatecznie sprowadza się to do użycia domyślnego zachowania dla każdego pierwotnego pola danych.

Eksternalizacja jest stosowana w rzadkich przypadkach, gdy naprawdę chcesz przechowywać i odbudowywać obiekt w zupełnie inny sposób i bez użycia domyślnych mechanizmów serializacji dla pól danych. Na przykład wyobraź sobie, że masz swój własny unikalny schemat kodowania i kompresji.

Uri
źródło
5
W przypadku dużych kolekcji „wybranych identyfikatorów” użyliśmy opcji Zewnętrzne - było to znacznie bardziej wydajne uzewnętrznianie, jako mniej więcej liczba i tablica prymitywnych liczb całkowitych, niż domyślna serializacja. To bardzo prosty przypadek użycia, nic „specjalnego” ani „unikalnego”.
Thomas W
9

Serializacja obiektów wykorzystuje interfejsy szeregowalne i możliwe do uzewnętrznienia. Obiekt Java jest możliwy do serializacji. jeśli klasa lub którakolwiek z jej nadklas implementuje interfejs java.io.Serializable lub jego podinterface java.io.Externalizable. Większość klas Java można serializować .

  • NotSerializableException: packageName.ClassName«Aby uczestniczyć w obiektu klasy w procesie serializacji, klasa musi implementować interfejs szeregowy lub zewnętrzny.

wprowadź opis zdjęcia tutaj


Interfejs szeregowy

Serializacja obiektów tworzy strumień z informacjami o klasach Java dla zapisywanych obiektów. W przypadku obiektów możliwych do serializacji przechowywane są wystarczające informacje, aby przywrócić te obiekty, nawet jeśli istnieje inna (ale zgodna) wersja implementacji klasy. Interfejs szeregowalny jest zdefiniowany w celu identyfikacji klas, które implementują protokół szeregowalny:

package java.io;

public interface Serializable {};
  • Interfejs serializacji nie ma żadnych metod ani pól i służy jedynie do identyfikacji semantyki możliwości serializacji. Do serializacji / deserializacji klasy możemy użyć domyślnych metod writeObject i readObject (lub) możemy przesłonić metody writeObject i readObject z klasy.
  • JVM będzie miał pełną kontrolę nad serializowaniem obiektu. użyj przejściowego słowa kluczowego aby zapobiec serializacji członka danych.
  • Tutaj obiekty możliwe do serializacji są odtwarzane bezpośrednio ze strumienia bez wykonywania
  • InvalidClassException«W procesie deserializacji, jeśli wartość lokalnej klasy serialVersionUID jest różna od odpowiedniej klasy nadawcy. wynik jest w konflikcie jako java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
  • Wartości pól nieprzemijających i niestatycznych klasy są serializowane.

Interfejs zewnętrzny

W przypadku obiektów podlegających eksternalizacji tylko tożsamość klasy obiektu jest zapisywana przez kontener; klasa musi zapisać i przywrócić zawartość. Interfejs zewnętrzny można zdefiniować w następujący sposób:

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;
}
  • Interfejs zewnętrzny ma dwie metody, obiekt zewnętrzny musi implementować metody writeExternal i readExternal, aby zapisać / przywrócić stan obiektu.
  • Programista musi zadbać o to, które obiekty mają być serializowane. Jako programista zajmij się serializacją Tak więc przejściowe słowo kluczowe nie ograniczy żadnego obiektu w procesie serializacji.
  • Po zrekonstruowaniu obiektu możliwego do eksternalizacji tworzona jest instancja za pomocą publicznego konstruktora no-arg, a następnie wywoływana jest metoda readExternal. Obiekty możliwe do serializacji są przywracane przez odczytanie ich z ObjectInputStream.
  • OptionalDataException«Pola muszą być w takim samym porządku i rodzaju, jak je zapisaliśmy. Jeśli występuje niezgodność typu ze strumienia, generuje wyjątek 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();
    }
  • Pola instancji klasy, które napisały (ujawniły), aby ObjectOutputzostały serializowane.


Przykład « implementuje 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;
}

Przykład « implementuje eksternalizowalne

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();
    }
}

Przykład

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();
        }
    }
}

@widzieć

Yash
źródło
7

Interfejs zewnętrzny nie został faktycznie udostępniony w celu optymalizacji wydajności procesu serializacji! ale aby zapewnić środki do implementacji własnego przetwarzania niestandardowego i zaoferować pełną kontrolę nad formatem i zawartością strumienia dla obiektu i jego super typów!

Przykładem może być implementacja zdalnego AMF (ActionScript Message Format) do przesyłania natywnych obiektów skryptów akcji przez sieć.

Ali Joudeh
źródło
7

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

Domyślna serializacja jest nieco szczegółowa i zakłada najszerszy możliwy scenariusz użycia serializowanego obiektu, a zatem domyślny format (Serializable) adnotuje wynikowy strumień informacją o klasie obiektu serializowanego.

Eksternalizacja daje producentowi strumienia obiektowego pełną kontrolę nad dokładnymi metadanymi klasy (jeśli istnieje) poza minimalną wymaganą identyfikacją klasy (np. Jej nazwą). Jest to wyraźnie pożądane w niektórych sytuacjach, takich jak środowiska zamknięte, w których dopasowuje się producenta strumienia obiektowego i jego konsumenta (który weryfikuje obiekt ze strumienia), a dodatkowe metadane dotyczące klasy nie mają żadnego celu i obniżają wydajność.

Dodatkowo (jak wskazał Uri) eksternalizacja zapewnia również pełną kontrolę nad kodowaniem danych w strumieniu odpowiadającym typom Java. Na przykład (wymyślony) możesz zapisać logiczną wartość logiczną true jako „Y” i false jako „N”. Eksternalizacja pozwala to zrobić.

alphazero
źródło
2

Rozważając opcje poprawy wydajności, nie zapomnij o niestandardowej serializacji. Możesz pozwolić Javie robić to, co robi dobrze lub przynajmniej wystarczająco dobrze, za darmo i zapewniać niestandardową obsługę tego, co robi źle. Zazwyczaj jest to o wiele mniej kodu niż pełne wsparcie zewnętrzne.

Ed Staub
źródło
2

Istnieje tak wiele różnic między serializowalnym a zewnętrznym, ale gdy porównamy różnicę między niestandardowym szeregowalnym (przesłoniętym writeObject () i readObject ()) a zewnętrznym, to okazuje się, że niestandardowa implementacja jest ściśle powiązana z klasą ObjectOutputStream, gdzie tak jak w przypadku zewnętrznym, my sami zapewnić implementację ObjectOutput, która może być klasą ObjectOutputStream lub inną, np. org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream

W przypadku interfejsu zewnętrznego

@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();
            }

Dodałem przykładowy kod, aby lepiej wyjaśnić. proszę sprawdzić / wyewidencjonować przypadek obiektowy Zewnętrznego. Nie są one związane bezpośrednio z żadną implementacją.
Gdzie jako Outstream / Instream są ściśle związane z klasami. Możemy rozszerzyć ObjectOutputStream / ObjectInputStream, ale będzie to nieco trudne w użyciu.

Ashish Sharma
źródło
1
Czy mógłbyś to bardziej rozwinąć? Kiedy to czytam, nie rozumiem, co próbujesz powiedzieć. Ponadto, jeśli możesz sformatować tekst za pomocą paragrafów i przykładów, może to być świetna odpowiedź.
Shirkam
0

Zasadniczo Serializablejest interfejsem znaczników, który sugeruje, że klasa jest bezpieczna do serializacji, a JVM określa jej sposób serializacji. Externalizablezawiera 2 metody readExternaloraz writeExternal. Externalizablepozwala implementatorowi decydować o sposobie serializacji obiektu, przy czym Serializableserializuje obiekty w sposób domyślny.

Pika the Wizard of the Whales
źródło
0

Niektóre różnice:

  1. W przypadku serializacji nie ma potrzeby domyślnego konstruktora tej klasy, ponieważ Object, ponieważ JVM konstruuje to samo za pomocą Reflection API. W przypadku eksternalizacji wymagany jest konstruktor bez argumentu, ponieważ kontrola jest w rękach programmar, a następnie przypisuje zdezrializowane dane do obiektu za pomocą ustawiaczy.

  2. W serializacji, jeśli użytkownik chce pominąć pewne właściwości, które mają być serializowane, to musi zaznaczyć te właściwości jako przejściowe, i odwrotnie nie jest wymagane dla eksternalizacji.

  3. Jeśli dla dowolnej klasy oczekiwane jest wsparcie zgodności z poprzednimi wersjami, zaleca się skorzystanie z opcji Zewnętrzne. Serializacja obsługuje domyślny obiekt defaultObject, a jeśli struktura obiektu zostanie uszkodzona, spowoduje to problem podczas deserializacji.

Neeraj
źródło