1. 소개

이 예제에서 우리는 자바의 java.io.Externalizable 인터페이스 를 간단히 살펴볼 것 입니다. 이 인터페이스의 주요 목표는 사용자 지정 직렬화 및 역직렬화를 용이하게 하는 것입니다.

계속 진행하기 전에 Java의 직렬화 문서 를 확인 하십시오. 다음 장에서는 이 인터페이스를 사용하여 Java 객체를 직렬화하는 방법에 대해 설명합니다.

그런 다음 java.io.Serializable 인터페이스 와 비교하여 주요 차이점에 대해 논의할 것 입니다.

2. 외부화 가능한 인터페이스

Externalizablejava.io.Serializable 마커 인터페이스에서 확장됩니다. Externalizable 인터페이스 를 구현하는 모든 클래스 는 writeExternal() , readExternal() 메서드 를 재정의해야 합니다 . 그렇게 하면 JVM의 기본 직렬화 동작을 변경할 수 있습니다.

2.1. 직렬화

이 간단한 예를 살펴보겠습니다.

public class Country implements Externalizable {
  
    private static final long serialVersionUID = 1L;
  
    private String name;
    private int code;
  
    // getters, setters
  
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(name);
        out.writeInt(code);
    }
  
    @Override
    public void readExternal(ObjectInput in) 
      throws IOException, ClassNotFoundException {
        this.name = in.readUTF();
        this.code = in.readInt();
    }
}

여기 에서 Externalizable 인터페이스를 구현하고 위에서 언급한 두 가지 방법 을 구현하는 Country 클래스를 정의했습니다.

writeExternal() 메서드 에서 객체의 속성을 ObjectOutput 스트림에 추가합니다. 여기에는 String 에 대한 writeUTF()int 값에 대한 writeInt() 와 같은 표준 메서드가 있습니다.

다음으로, 객체를 역직렬화하기 위해 readUTF(), readInt() 메서드를 사용하여 ObjectInput 스트림 에서 읽어서 작성된 것과 동일한 순서로 속성을 읽습니다.

serialVersionUID 를 수동으로 추가하는 것이 좋습니다 . 이것이 없으면 JVM이 자동으로 추가합니다.

자동으로 생성된 숫자는 컴파일러에 따라 다릅니다. 이것은 가능성이 없는 InvalidClassException 을 일으킬 수 있음을 의미합니다 .

위에서 구현한 동작을 테스트해 보겠습니다.

@Test
public void whenSerializing_thenUseExternalizable() 
  throws IOException, ClassNotFoundException {
       
    Country c = new Country();
    c.setCode(374);
    c.setName("Armenia");
   
    FileOutputStream fileOutputStream
     = new FileOutputStream(OUTPUT_FILE);
    ObjectOutputStream objectOutputStream
     = new ObjectOutputStream(fileOutputStream);
    c.writeExternal(objectOutputStream);
   
    objectOutputStream.flush();
    objectOutputStream.close();
    fileOutputStream.close();
   
    FileInputStream fileInputStream
     = new FileInputStream(OUTPUT_FILE);
    ObjectInputStream objectInputStream
     = new ObjectInputStream(fileInputStream);
   
    Country c2 = new Country();
    c2.readExternal(objectInputStream);
   
    objectInputStream.close();
    fileInputStream.close();
   
    assertTrue(c2.getCode() == c.getCode());
    assertTrue(c2.getName().equals(c.getName()));
}

이 예에서는 먼저 Country 개체를 만들고 파일에 씁니다. 그런 다음 파일에서 개체를 역직렬화하고 값이 올바른지 확인합니다.

인쇄된 c2 객체 의 출력 :

Country{name='Armenia', code=374}

이것은 객체가 성공적으로 역직렬화되었음을 보여줍니다.

2.2. 계승

클래스가 Serializable 인터페이스 에서 상속할 때 JVM은 하위 클래스의 모든 필드도 자동으로 수집하여 직렬화 가능하게 만듭니다.

이것을 Externalizable 에도 적용할 수 있음을 명심하십시오 . 상속 계층의 모든 하위 클래스에 대해 읽기/쓰기 메서드를 구현하기만 하면 됩니다.

아래 에서 이전 섹션 의 Country 클래스 를 확장 한 Region 클래스 를 살펴보겠습니다 .

public class Region extends Country implements Externalizable {
 
    private static final long serialVersionUID = 1L;
 
    private String climate;
    private Double population;
 
    // getters, setters
 
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        super.writeExternal(out);
        out.writeUTF(climate);
    }
 
    @Override
    public void readExternal(ObjectInput in) 
      throws IOException, ClassNotFoundException {
 
        super.readExternal(in);
        this.climate = in.readUTF();
    }
}

여기에 두 개의 추가 속성을 추가하고 첫 번째 속성을 직렬화했습니다.

상위 클래스 필드도 저장/복원하기 위해 직렬 변환기 메서드 내 에서 super.writeExternal(out), super.readExternal(in)호출 했습니다 .

다음 데이터로 단위 테스트를 실행해 보겠습니다.

Region r = new Region();
r.setCode(374);
r.setName("Armenia");
r.setClimate("Mediterranean");
r.setPopulation(120.000);

다음은 역직렬화된 개체입니다.

Region{
  country='Country{
    name='Armenia',
    code=374}'
  climate='Mediterranean', 
  population=null
}

Region 클래스에서 인구 필드를 직렬화하지 않았기 때문에 해당 속성의 값은 null입니다 .

3. 외부화 가능 vs 직렬화 가능

두 인터페이스의 주요 차이점을 살펴보겠습니다.

  • 직렬화 책임

여기서 중요한 차이점은 직렬화 프로세스를 처리하는 방법입니다. 클래스가 java.io.Serializable 인터페이스를 구현할 때 JVM은 클래스 인스턴스 직렬화에 대한 전적인 책임을 집니다. Externalizable 의 경우 전체 직렬화 및 역직렬화 프로세스를 처리해야 하는 것은 프로그래머입니다.

  • 사용 사례

전체 객체를 직렬화해야 하는 경우 직렬화 가능 인터페이스가 더 적합합니다. 반면 에 사용자 정의 직렬화의 경우 Externalizable 을 사용하여 프로세스를 제어할 수 있습니다 .

  • 성능

java.io.Serializable 인터페이스 는 상대적으로 느린 성능을 유발하는 리플렉션 및 메타데이터를 사용합니다. 이에 비해 Externalizable 인터페이스는 직렬화 프로세스를 완전히 제어할 수 있습니다.

  • 읽기 순서

Externalizable 을 사용하는 동안 작성된 순서대로 모든 필드 상태를 읽어야 합니다. 그렇지 않으면 예외가 발생합니다.

예를 들어 Country 클래스 에서 코드이름 속성 의 읽기 순서를 변경하면 java.io.EOFException 이 발생합니다.

한편 Serializable 인터페이스에는 그러한 요구 사항이 없습니다.

  • 사용자 정의 직렬화

일시적인 키워드 로 필드를 표시하여 직렬화 가능 인터페이스로 사용자 정의 직렬화를 달성할 수 있습니다 . JVM은 특정 필드를 직렬화하지 않지만 기본값을 사용하여 파일 스토리지에 필드를 추가합니다 . 그렇기 때문에 사용자 정의 직렬화의 경우 Externalizable 을 사용하는 것이 좋습니다.

4. 결론

Externalizable 인터페이스 에 대한 이 짧은 사용방법(예제) 에서는 주요 기능, 장점 및 간단한 사용의 예를 설명했습니다. 또한 Serializable 인터페이스와 비교했습니다.

평소와 같이 예제의 전체 소스 코드는 GitHub에서 사용할 수 있습니다 .

Generic footer banner