Jaka jest różnica między Serializable
i Externalizable
w Javie?
Jaka jest różnica między Serializable
i Externalizable
w Javie?
Odpowiedzi:
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.Externalizable
zapewniono interfejs, który jest podobny, java.io.Serializable
ale z niestandardowymi mechanizmami do wykonywania funkcji rozróżniania i rozróżniania (musisz zaimplementować readExternal
i writeExternal
metody 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ść Externalizable
z 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 Externalizable
jest 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, Externalizable
jest reliktem Java 1.1 dni. Naprawdę nie ma już takiej potrzeby.
Externalizable
pomaga wiele .
Externalizable
bardziej 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.
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.
Kluczowe różnice między Serializable
iExternalizable
Serializable
jest interfejsem znaczników bez żadnych metod. Externalizable
interfejs zawiera dwie metody: writeExternal()
i readExternal()
.Serializable
interfejs. Zdefiniowany przez programistę proces szeregowania zostanie uruchomiony dla klas implementujących Externalizable
interfejs.Externalizable
interfejsem. Możesz obsługiwać różne wersje swojego obiektu. Jeśli wdrożysz Externalizable
, Twoim obowiązkiem jest serializacja super
klasySerializable
wykorzystuje odbicie do konstruowania obiektu i nie wymaga konstruktora arg. Ale Externalizable
wymaga publicznego konstruktora bez argumentów.Patrz blog o Hitesh Garg
więcej szczegółów.
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.
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.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 {};
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
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;
}
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 ObjectOutput
został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ć
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ć.
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ć.
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.
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.
Zasadniczo Serializable
jest interfejsem znaczników, który sugeruje, że klasa jest bezpieczna do serializacji, a JVM określa jej sposób serializacji. Externalizable
zawiera 2 metody readExternal
oraz writeExternal
. Externalizable
pozwala implementatorowi decydować o sposobie serializacji obiektu, przy czym Serializable
serializuje obiekty w sposób domyślny.
Niektóre różnice:
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.
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.
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.