Jaka jest różnica między Serializablei Externalizablew Javie?
Jaka jest różnica między Serializablei Externalizablew 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.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.
Externalizablepomaga wiele .
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.
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 SerializableiExternalizable
Serializablejest interfejsem znaczników bez żadnych metod. Externalizableinterfejs zawiera dwie metody: writeExternal()i readExternal().Serializableinterfejs. Zdefiniowany przez programistę proces szeregowania zostanie uruchomiony dla klas implementujących Externalizableinterfejs.Externalizableinterfejsem. Możesz obsługiwać różne wersje swojego obiektu. Jeśli wdrożysz Externalizable, Twoim obowiązkiem jest serializacja superklasySerializablewykorzystuje odbicie do konstruowania obiektu i nie wymaga konstruktora arg. Ale Externalizablewymaga publicznego konstruktora bez argumentów.Patrz blog o Hitesh Gargwię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 = 50818771W 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 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ć
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 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.
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.