[JAVA] Externalizable 인터페이스

2019. 8. 7. 11:45 JAVA/Java IO

Externalizable 인터페이스


Externalizable 인터페이스는 Serializable 인터페이스를 확장한 형태로 세부적인 필드를 직접 제어할 수 있는 트정이 있다. transient 키워드를 사용하여 특정 필드를 제외시키는 것도 가능하지만 좀 더 세부적인 작업이 필요할 때 Externalizable 인터페이스를 사용할 수 있다.


[JAVA/Java IO] - [JAVA] Serializable 과 transient


1. Externalizable 인터페이스


Externalizable 인터페이스를 사용하면 객체 스트림을 통하여 객체를 읽고 쓸 때 내부의 내용을 자유롭게 조작하면서 원하는 내용을 출력할 수 있도록 도와준다. 이를 위해서 이 인터페이스는 Serializable 인터페이스를 확장하고 두 가지 메소드를 추가하였다.

하나는 출력을 위한 writeExternal() 메소드이고 또 하나는 입력을 위한 readExternal() 메소드다.


2. 객체 스트림 직렬화시의 동작


(1) 출력 (직렬화)

객체 스트림을 통하여 객체를 출력하는 메소드가 호출되면 가장 먼저 그 객체의 클래스가 Externalizable 인터페이스를 구현했는지 확인한다. 만약 구현했다면 writeExternal() 메소드를 통해서 전송 데이터를 직렬화 한다.

Externalizable 인터페이스를 구현하지 않았다면 Serializable 인터페이스를 구현했는지 확인한다.

Serializable 인터페이스를 구현했다면 그대로 객체 스트림에 객체 전체를 직렬화 한다.

Externalizable, Serializable 인터페이스를 구현하지 않았다면 Exception이 발생한다.


(2) 입력 (역직렬화)

- 스트림을 통해 전송받은 객체가 Externalizable 인터페이스를 구현했는지 확인한다. 구현했다면 readExternal() 메소드를 통해서 전송받은 데이터를 순서대로 읽어온다.

- Externalizable 인터페이스를 구현하지 않았다면 Serializable 인터페이스를 통해 객체 전체를 역직렬화 한다.


(3) writeExternal(), readExternal()

- Externalizable 인터페이스를 구현할 때는 writeExtenral()과 readExternal() 메소드를 작성해야 한다.

- Serializable 인터페이스는 모든 변수(transient 제외)를 직렬화하는 반면 Externalizable 인터페이스는 직렬화할 대상을 직접 writeExternal() 메소드에 구현해야 한다. (transient 변수도 직렬화하여 전송할 수 있다.)

- out.writeObject(name); 과 같이 직렬화할 변수 하나하나 writeObject()에 써줘야 한다.


- 스트림을 통해 객체를 받아 역직렬화를 할 때에도 readExternal() 메소드에서 하나하나 받은 데이터를 변수에 넣어줘야 한다.

- in.readObject(); 를 사용하여, writeExternal() 메소드에서 writeObject()에 넣어 준 변수의 순서대로 데이터를 가져온다. 

- 따라서 writeExternal()에 직렬화한 변수의 순서와 readObject() 에서 꺼내는 순서가 일치해야한다.

- 이때 형변환을 해야 한다. ex> id = (String)in.readObject();


- writeExternal(), readExternal()의 인자인 ObjectOupt, ObjectInput 객체는 JVM이 자동으로 인자를 설정하여 매핑해 준다.



3. Externalizable 사용 예제

 

UserClass.java


 

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

// 직렬화 한다.
public class UserClass implements Externalizable{
   
    private static final long serialVersionUID = 4220461820168818967L;
   
    String name;
   
    // age 비 전송
    transient int age;
       
   
    public UserClass() {
       
    }
   
    public UserClass(String name, int age){
       
        this.name = name;
        this.age = age;
       
    }
   
    // Externalizable 구현으로 인한 직렬화 동작 메소드
    public void writeExternal(ObjectOutput out) throws IOException{
       
        out.writeObject(this.name);
       
        // 이름이 "제이슨" 이면 값을 80으로 직렬화한다.
        if(this.name.equals("제이슨")){
           
            out.writeObject(80);
       
        }else{
           
            out.writeObject(this.age);
       
        }
       
    }
   
    // Externalizable 구현으로 인한 역직렬화 동작 메소드
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException{
       
        this.name = (String)in.readObject();
        this.age = (Integer)in.readObject();
   
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
   

   

    @Override
    public String toString() {
        return "UserClass [name=" + name + ", age=" + age + "]";
    }
   
}


ObjectStream.java



- UserClass 객체는 Externalizable 인터페이스를 구현하여, writeExternal(), readExternal() 메소드를 통해 직렬화, 역 직렬화시 데이터를 제어할 수 있다.

- writeExternal() 에서 데이터를 writeObject()로 넣어준 순서대로 직렬화가 되며, readExternal()에서 readObject()시 순서대로 데이터를 가져온다.

- Serializable 인터페이스 구현시에는 tradient 를 선언한 변수를 제외한 모든변수를 자동으로 직렬화 하여 전송하지만, Externalizable에서는 전송 데이터를 각각 지정해줘야 하며, tradient 선언이 되어 있어도 전송 된다.


- ObjectStream.java 에서 3개의 UserClass 객체를 생성한 후에 파일에 쓰고, 읽어와 프린트 하여 테스트 한다.


ObjectStream.zip



출처: https://hyeonstorage.tistory.com/255?category=578560 [개발이 하고 싶어요]