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

Java SerializableExternalizableJava 의 차이점은 무엇입니까 ?



답변

다른 답변에 추가하기 위해을 구현 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 일의 유물입니다. 더 이상 필요가 없습니다.


답변

직렬화는 개체를 저장하고 나중에 다시 만드는 기본 기능을 제공합니다. 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;

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


답변

간의 주요 차이점 SerializableExternalizable

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

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


답변

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

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


답변

객체 직렬화는 직렬화 가능 및 외부화 가능 인터페이스를 사용합니다.
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();
        }
    }
}

@보다


답변

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

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


답변

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

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

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

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