Java에서 Serializable과 Externalizable의 차이점은 무엇입니까?


답변:


267

다른 답변에 추가하기 위해을 구현 java.io.Serializable하면 클래스 객체에 대한 "자동"직렬화 기능이 제공됩니다. 다른 로직을 구현할 필요가 없으며 작동합니다. Java 런타임은 리플렉션을 사용하여 객체를 마샬링하고 마샬링 해제하는 방법을 알아냅니다.

이전 버전의 Java에서는 리플렉션이 매우 느리기 때문에 클라이언트-서버 RMI 응용 프로그램 등에서 큰 객체 그래프를 직렬화하는 것이 약간의 성능 문제였습니다. 이 상황을 처리 하기 위해 마샬링 및 비 정렬 화 기능 ( 클래스에서 구현 및 메소드 가 필요함)을 수행하기위한 사용자 정의 작성 메커니즘과 java.io.Externalizable유사 java.io.Serializable하지만 인터페이스가 제공되었습니다 . 이를 통해 반사 성능 병목 현상을 해결할 수 있습니다.readExternalwriteExternal

최근 버전의 Java (확실히 1.3 이상)에서는 리플렉션 성능이 예전보다 훨씬 우수하므로 문제가 훨씬 적습니다. Externalizable최신 JVM 을 통해 의미있는 이점을 누리기 위해 열심히 노력한 것 같습니다 .

또한 기본 제공 Java 직렬화 메커니즘이 유일한 것은 아니며 JBoss Serialization과 같은 타사 대체물을 얻을 수 있으며 이는 훨씬 빠르며 기본값을 대체하는 대체물입니다.

큰 단점은 Externalizable이 논리를 직접 유지해야한다는 것입니다. 클래스에서 필드를 추가, 제거 또는 변경하는 경우이를 처리하기 위해 writeExternal/ readExternal메소드를 변경해야 합니다.

요약 Externalizable하면 Java 1.1 일의 유물입니다. 더 이상 필요가 없습니다.


61
[ code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking] 벤치 마크에 따르면 수동 직렬화 (externizable 사용)는 Java의 기본 직렬화를 사용하는 것보다 훨씬 빠릅니다. 작업 속도가 중요한 경우에는 직접 직렬 변환기를 작성하십시오.
volni

6
@Jack이 제안한 새 링크 github.com/eishay/jvm-serializers/wiki로 업데이트
noquery

3
github.com/eishay/jvm-serializers/wiki 의 "java-manual"은 Externalizable을 사용 하지 않으므로 ObjectOutputStream을 사용합니다. 코드에 대한 링크는 github.com/eishay/jvm-serializers/wiki/ToolBehavior 를 참조하십시오 . 대신, DataOutputStream을 사용하는 수작업으로 작성된 코드이므로 ObjectOutputStream을 느리게 만드는 것 (예 : 객체 인스턴스 추적 및 객체주기 지원)을 겪지 않습니다.
Esko Luontola

7
클래스를 변경하지 않고 기존 버전의 오래된 데이터를 읽을 필요가없는 경우 로직을 직접 유지 관리해야하는 것은 단점입니다. 오래된 버전을 역 직렬화하기 위해 지옥 같은 코드를 작성하지 않고도 클래스를 자유롭게 변경하려면 많은Externalizable 도움이됩니다 .
Tim Boudreau

2
방금 사용자 정의 컬렉션을 작성해야했고 Externalizable빈 공간이나 자리 표시 자 객체로 배열을 출력하고 싶지 않고 상속을 처리 할 수있는 명시 적 인터페이스를 사용하여 배열을 출력하고 싶지 않기 때문에 훨씬 더 적합 하다고 말해야합니다. -class는 호출에 대한 잠금을 쉽게 추가 할 수 있습니다 writeExternal(). 그렇습니다. Externalizable은 여전히 ​​크거나 복잡한 객체와 관련이 있습니다.
Haravikk 2016 년

37

직렬화는 개체를 저장하고 나중에 다시 만드는 기본 기능을 제공합니다. verbose 형식을 사용하여 저장할 오브젝트의 전체 그래프를 정의합니다. 예를 들어 linkedList가 있고 아래와 같이 코드화 한 경우 기본 직렬화는 링크 된 모든 오브젝트를 발견하고 직렬화합니다. 기본 직렬화에서 객체는 생성자 호출없이 저장된 비트로 만 구성됩니다.

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

그러나 제한된 직렬화를 원하거나 객체의 일부를 직렬화하지 않으려면 Externalizable을 사용하십시오. Externalizable 인터페이스는 Serializable 인터페이스를 확장하고 writeExternal () 및 readExternal ()의 두 가지 메소드를 추가합니다. 직렬화 또는 역 직렬화 중에 자동으로 호출됩니다. Externalizable로 작업하는 동안 기본 생성자는 public이어야하며 그렇지 않으면 코드에서 예외가 발생합니다. 아래 코드를 따르십시오

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

기본 생성자를 주석 처리하면 코드에서 예외가 발생합니다.

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

우리는 암호가 민감한 정보이기 때문에 writeExternal (ObjectOutput oo) 메소드에서 암호를 직렬화하지 않고 readExternal (ObjectInput oi)에서 동일한 값을 설정하지 않습니다. 이것이 Externalizable에서 제공하는 유연성입니다.

위 코드의 출력은 다음과 같습니다.

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

passWord의 값을 설정하지 않았으므로 null이 될 수 있습니다.

비밀번호 필드를 임시로 선언하여 동일한 결과를 얻을 수도 있습니다.

private transient String passWord;

도움이 되길 바랍니다. 실수 한 경우 사과드립니다. 감사.


22

간의 주요 차이점 SerializableExternalizable

  1. 마커 인터페이스 : Serializable방법이없는 마커 인터페이스입니다. Externalizable인터페이스는 두 가지 방법을 포함 writeExternal()하고 readExternal().
  2. 직렬화 프로세스 : Serializable인터페이스를 구현하는 클래스의 경우 기본 직렬화 프로세스가 시작 됩니다. Externalizable인터페이스를 구현하는 클래스에 대해 프로그래머 정의 직렬화 프로세스가 시작 됩니다.
  3. 유지 관리 : 호환되지 않는 변경으로 인해 직렬화가 중단 될 수 있습니다.
  4. 이전 버전과의 호환성 및 제어 : 여러 버전을 지원해야하는 경우 Externalizable인터페이스를 완벽하게 제어 할 수 있습니다 . 다른 버전의 객체를 지원할 수 있습니다. 구현하는 경우 클래스 Externalizable를 직렬화하는 것은 귀하의 책임입니다.super
  5. public No-arg constructor : Serializable리플렉션을 사용하여 객체를 생성하며 인수 생성자가 필요하지 않습니다. 그러나 Externalizable인수없는 공개 생성자가 필요합니다.

참조 블로그Hitesh Garg자세한 내용은.


1
(3)이 맞지 않습니다. 기존 객체의 직렬화 해제를 중단하지 않고 클래스를 변경할 수있는 대규모 레퍼토리가 있으며 직렬화 가능한 멤버를 추가하는 것도 그 중 하나입니다. 그것들을 삭제하는 것은 또 다른 것입니다. Java 객체 직렬화 사양객체 버전 관리 장을 참조하십시오 .
Lorne의 후작

1
문장을 다시 표현하십시오.
Ravindra babu

직렬화 사양에 대한 링크를 공유해 주셔서 감사합니다.
JL_SO

21

직렬화는 특정 기본 동작을 사용하여 개체를 저장하고 나중에 다시 만듭니다. 참조 및 복잡한 데이터 구조를 처리하는 순서 또는 방법을 지정할 수 있지만 결국 각 기본 데이터 필드에 대한 기본 동작을 사용하게됩니다.

외부화는 데이터 필드에 대한 기본 직렬화 메커니즘을 사용하지 않고 완전히 다른 방식으로 객체를 실제로 저장하고 다시 빌드하려는 드문 경우에 사용됩니다. 예를 들어, 고유 한 인코딩 및 압축 체계가 있다고 가정하십시오.


5
우리는 "선택한 ID"의 대규모 컬렉션에 Externalizable을 사용했습니다. 기본 직렬화보다 카운트 및 기본 정수의 배열로 훨씬 효율적으로 외부화했습니다. 그것은 "특별한"또는 "독특한"것이 아닌 매우 간단한 유스 케이스입니다.
Thomas W

9

객체 직렬화는 직렬화 가능 및 외부화 가능 인터페이스를 사용합니다. Java 객체는 직렬화 만 가능합니다. 클래스 또는 그 슈퍼 클래스 중 하나가 java.io.Serializable 인터페이스 또는 해당 서브 인터페이스 java.io.Externalizable을 구현하는 경우 대부분의 자바 클래스는 직렬화 가능하다 .

  • NotSerializableException: packageName.ClassName«직렬화 프로세스에 클래스 객체를 참여 시키려면 클래스가 직렬화 가능 또는 외부화 가능 인터페이스를 구현해야합니다.

여기에 이미지 설명을 입력하십시오


직렬화 가능 인터페이스

오브젝트 직렬화는 저장중인 오브젝트의 Java 클래스에 대한 정보가있는 스트림을 생성합니다. 직렬화 가능 오브젝트의 경우, 클래스 구현의 다른 (그러나 호환 가능한) 버전의 구현이 존재하더라도 해당 오브젝트를 복원하기에 충분한 정보가 유지됩니다. 직렬화 가능 인터페이스는 직렬화 가능 프로토콜을 구현하는 클래스를 식별하기 위해 정의됩니다.

package java.io;

public interface Serializable {};
  • 직렬화 인터페이스에는 메소드 또는 필드가 없으며 직렬화 가능의 의미를 식별하는 역할 만합니다. 클래스를 직렬화 / 역 직렬화하려면 기본 writeObject 및 readObject 메소드를 사용하거나 클래스에서 writeObject 및 readObject 메소드를 대체 할 수 있습니다.
  • JVM은 객체 직렬화를 완벽하게 제어합니다. 데이터 멤버가 직렬화되는 것을 방지 하려면 transient 키워드 를 사용하십시오.
  • 여기서 직렬화 가능한 객체는 실행하지 않고 스트림에서 직접 재구성됩니다.
  • InvalidClassException«역 직렬화 프로세스에서 로컬 클래스 serialVersionUID 값이 해당 발신자의 클래스와 다른 경우 . 다음과 같이 충돌이 발생합니다 java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
  • 클래스의 비 과도 및 비 정적 필드의 값은 직렬화됩니다.

외부화 가능 인터페이스

외부화 가능 객체의 경우 객체 클래스의 ID 만 컨테이너에 의해 저장됩니다. 클래스는 내용을 저장하고 복원해야합니다. 외부화 가능 인터페이스는 다음과 같이 정의됩니다.

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;
}
  • Externalizable 인터페이스에는 두 가지 방법이 있습니다. externalizable 객체는 객체의 상태를 저장 / 복원하기 위해 writeExternal 및 readExternal 메소드를 구현해야합니다.
  • 프로그래머는 직렬화 할 오브젝트를 처리해야합니다. 프로그래머가 직렬화를 처리하므로, 여기서 transient 키워드는 직렬화 프로세스에서 오브젝트를 제한하지 않습니다.
  • Externalizable 객체가 재구성되면, 인수없는 public 생성자를 사용하여 인스턴스가 생성되고 readExternal 메소드가 호출됩니다. 직렬화 가능 오브젝트는 ObjectInputStream에서 읽음으로써 복원됩니다.
  • OptionalDataException« 우리가 썼을 때 필드는 반드시 같은 순서와 유형이어야 합니다. 스트림에서 형식이 일치하지 않으면 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();
    }
  • 직렬화 하기 위해 작성 (노출) 된 클래스의 인스턴스 필드입니다 ObjectOutput.


« 직렬화 가능 구현

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

« 는 Externalizable을 구현합니다.

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

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

@보다


7

직렬화 프로세스 성능을 최적화하기 위해 실제로 Externalizable 인터페이스가 제공되지 않았습니다! 그러나 사용자 정의 처리를 구현하는 수단을 제공하고 객체 및 수퍼 유형의 스트림 형식 및 내용을 완벽하게 제어 할 수 있습니다!

이에 대한 예로는 네트워크를 통해 기본 조치 스크립트 오브젝트를 전송하기위한 AMF (ActionScript 메시지 형식) 원격 구현이 있습니다.


7

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

기본 직렬화는 다소 장황하며 직렬화 된 객체의 가능한 가장 넓은 사용 시나리오를 가정하므로 기본 형식 (Serializable)은 직렬화 된 객체의 클래스에 대한 정보로 결과 스트림에 주석을 추가합니다.

외부화는 객체 스트림의 제작자에게 클래스의 최소한의 필수 식별 (예 : 이름)을 넘어서 정확한 클래스 메타 데이터 (있는 경우)를 완벽하게 제어합니다. 이는 객체 스트림의 생산자와 소비자 (스트림에서 객체를 유지하는 소비자)가 일치하고 클래스에 대한 추가 메타 데이터가 목적을 제공하지 않으며 성능을 저하시키는 폐쇄 된 환경과 같은 특정 상황에서 분명히 바람직합니다.

또한 (Uri point out으로) 외부화는 Java 유형에 해당하는 스트림의 데이터 인코딩을 완벽하게 제어합니다. (고려 된) 예제의 경우, 부울 true를 'Y'로, false를 'N'으로 기록 할 수 있습니다. 외부화를 통해 그렇게 할 수 있습니다.


2

성능 향상을위한 옵션을 고려할 때는 사용자 지정 직렬화를 잊지 마십시오. Java가 잘하는 일이나 최소한 충분히 좋은 일 을 무료 로하도록하고, 잘못한 일에 대한 사용자 정의 지원을 제공 할 수 있습니다. 이것은 일반적으로 완전한 외부화 지원보다 코드가 훨씬 적습니다.


2

Serializable과 Externalizable 사이에는 많은 차이가 있지만 custom Serializable (overrided writeObject () & readObject ())와 Externalizable의 차이점을 비교할 때 사용자 정의 구현은 Externalizable 경우와 같이 ObjectOutputStream 클래스와 밀접하게 바인딩됩니다. ObjectOutputStream 클래스 일 수도 있고 org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream과 같은 ObjectOutput 구현을 제공합니다.

외부화 가능 인터페이스의 경우

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

더 잘 설명하기 위해 샘플 코드를 추가했습니다. Externalizable의 오브젝트를 체크인 / 체크 아웃하십시오. 이것들은 어떤 구현에도 직접적으로 구속되지 않습니다.
Outstream / Instream은 클래스에 밀접하게 바인딩됩니다. ObjectOutputStream / ObjectInputStream을 확장 할 수는 있지만 사용하기가 약간 어렵습니다.


1
좀 더 자세히 설명해 주시겠습니까? 나는 그것을 읽을 때, 당신이 말하려는 것을 이해하지 못합니다. 또한 일부 단락과 예제를 사용하여 텍스트의 서식을 지정할 수 있다면 큰 대답이 될 수 있습니다.
Shirkam

0

기본적으로 Serializable클래스가 직렬화에 안전하고 JVM이 직렬화 방법을 결정 함을 나타내는 마커 인터페이스입니다. Externalizable이 방법을 포함 readExternal하고 writeExternal. Externalizable구현자가 객체를 직렬화하는 방법을 결정할 수 있습니다 Serializable.


0

몇 가지 차이점 :

  1. 직렬화의 경우 JVM이 Reflection API의 도움으로 동일하게 구성되므로 Object는 해당 클래스의 기본 생성자가 필요하지 않습니다. arg가없는 외부화 생성자가 필요한 경우, 제어는 프로그래밍에 의해 이루어지고 나중에 직렬화 해제 된 데이터를 setter를 통해 객체에 할당하기 때문입니다.

  2. 직렬화에서 사용자가 직렬화 할 특정 특성을 건너 뛰려면 해당 특성을 일시적으로 표시해야하며 그 반대도 외부화에 필요하지 않습니다.

  3. 모든 클래스에 대해 이전 버전과의 호환성 지원이 필요한 경우 Externalizable을 사용하는 것이 좋습니다. 직렬화는 defaultObject 지속을 지원하며 객체 구조가 손상된 경우 deserialize하는 동안 문제가 발생합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.