Java - 7. I/O: 직렬화(Serialization)

갓김치·2020년 9월 28일
0

고급자바

목록 보기
26/47

직렬화(Serialization)

  • 보조스트림 방법 중 하나

객체의 직렬화

  • 객체를 스트림 형식으로 파일에 저장하는 방법
  • 객체를 직렬화 하기 위해서는 해당 객체에 Serializable 인터페이스를 구현해주어야 한다.

Serializable 인터페이스

  • 구현한 해당 클래스의 모든 멤버변수가 직렬화 대상이 됨
    • static은 제외
  • 객체를 스트림을 통해 입출력 하기 위해서는 직렬화 과정이 필요하다
  • 멤버변수 중 직렬화 대상에서 제외하고 싶다면 transient 키워드를 사용하여 제외시킨다
    • transient: 직렬화 되지 않을 멤버변수에 지정
    • 직렬화가 되지 않는 멤버변수는 기본값으로 저장된다
      • (참조변수: null, 숫자형 변수: 0)
  • 직렬화 - ObjectOutputStream
  • 역직렬화 - ObjectInputStream

회원정보를 객체로 출력하는 예제

  • T15_ObjectStreamTest.java

1단계: 회원정보 담을 VO class

class Member implements Serializable {
// 자바는 Serializable 인터페이스를 구현한 클래스만 직렬화를 허용

  // Serializable 인터페이스를 구현한 클래스이기때문에
  // 해당 클래스의 모든 멤버변수가 직렬화 대상이 된다 (* static 제외)
  }

  private transient String name; // 직렬화 처리 x -> 역직렬화시 기본값 null 로 출력
  private int age;
  private String addr;

  // 생성자와 getter, setter도 만들어준다. 여기선 생략

}

2단계: main에서 직렬화/역직렬화 실행

// Member 인스턴스 생성
Member mem1 = new Member("홍길동", 20, "대전");
Member mem2 = new Member("일지매", 30, "경기");
Member mem3 = new Member("이몽룡", 40, "강원");
Member mem4 = new Member("성춘향", 20, "제주");

try {
  // 1. 직렬화: 객체를 파일에 저장하기 (Output)
  // 출력용 스트림 객체 생성
  ObjectOutputStream oos =
      new ObjectOutputStream
          (new BufferedOutputStream(
              (new FileOutputStream("d:/D_Other/memObj.bin")));

  // 쓰기 작업
  oos.writeObject(mem1); // 직렬화
  oos.writeObject(mem2); // 직렬화
  oos.writeObject(mem3); // 직렬화
  oos.writeObject(mem4); // 직렬화

  System.out.println("쓰기 작업 완료");
  oos.close();

  //=====================================
  // 2. 역직렬화: 저장한 객체를 읽어와 콘솔에 출력하기 (Input) 
  ObjectInputStream ois = 
      new ObjectInputStream
          (new BufferedInputStream
              (new FileInputStream("d:/D_Other/memObj.bin")));
  
  Object obj = null;
  
  try {
    while ((obj = ois.readObject()) != null) {
      // 읽어온 데이터를 원래의 객체형으로 변환 후 사용한다.
      Member mem = (Member) obj;

      System.out.println("이름 : " + mem.getName());
      System.out.println("나이 : " + mem.getAge());
      System.out.println("주소 : " + mem.getAddr());
      System.out.println("----------------------");
    }
    ois.close();
  } catch (ClassNotFoundException e) {
  
  }


} catch (IOException e) {
  // 더이상 읽어올 객체가 없으면 예외 발생함.
  e.printStackTrace();
  System.out.println("출력 작업 끝...");
}
  • 결과
    • e.printStackTrace() 주석처리함

Serializable 구현 못하는 상황에서 수동으로 직렬화 처리하는 방법

  • T16_NonSerializableParentTest.java
  • 이해 하면 다행이고 몰라도 어쩔 수 없음~!!

부모클래스가 Serializable 인터페이스를 구현하고 있지 않을 경우, 부모 객체의 필드값 처리 방법

  • 1) 부모 클래스가 Serializable 인터페이스를 구현하도록 해야한다.
    • 이게 불가능한 경우에는 2번 써야함
      • jdk나 library, framework 개발하는 사람들은 1번방법 못씀
      • Parent 하나 구현시키면 Parent 자식들도 다 구현해야하기때문에 조심스럽게 씀
  • 2) 자식클래스에 writeObject()와 readObject() 메서드를 이용하여 부모객체의 필드값을 처리할 수 있도록 직접 구현한다.

1단계: 부모 클래스 정의

class Parent {
	
  private String parentName; 
  // 직렬화 안되어있어서 그냥 출력하면 transient 처리된 것처럼 null로 나옴

  public String getParentName() {
    return parentName;
  }

  public void setParentName(String parentName) {
    this.parentName = parentName;
  }

}

2단계: 자식 클래스 정의

class Child extends Parent implements Serializable {
	
  private String childName;

  public String getChildName() {
    return childName;
  }

  public void setChildName(String childName) {
    this.childName = childName;
  }

  /**
   * 직렬화될 때 자동으로 호출됨.
   * (접근 제어자가 private이 아니면 자동으로 호출되지 않음)
   * @param out
   * @throws IOException
   */
  // 아래 두 양식은 정해진 규칙임!

  //★★ 부모이름 null로뜨는거 해결하는 방법
  private void writeObject(ObjectOutputStream out) throws IOException{
    // ObjectOutputStream 객체의 메서드를 이용하여 부모객체의 필드값 처리.
    out.writeUTF(getParentName());
    out.defaultWriteObject(); // out.writeUTF가 없으면 기본?인데? 저게 이 앞에 있으므로? 저 기능이 플러스되어서 작동??
  }

  /**
   * 역직렬화될 때 자동으로 호출됨
   * (접근 제어자가 private이 아니면 자동으로 호출되지 않음)
   * @param in
   * @throws IOException
   * @throws ClassNotFoundException 
   */
  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
    setParentName(in.readUTF()); // 부모 객체 필드값 처리
    in.defaultReadObject(); // ClassNotFoundException 던짐
  }
}

3단계: main에서 실행

// 출력
FileOutputStream fos = 
	new FileOutputStream("d:/D_Other/nonSerializableTest.bin");

ObjectOutputStream oos = new ObjectOutputStream(fos); // 보조이기떄문에 기반 필요(=fos)

Child child = new Child();
child.setParentName("붐호");
child.setChildName("자슥");

oos.writeObject(child); // 직렬화
oos.flush(); // 생략가능
oos.close();

//==============================
// 읽어들이기 

FileInputStream fis = new FileInputStream("d:/D_Other/nonSerializableTest.bin");
ObjectInputStream ois = new ObjectInputStream(fis); // 보조이기때문에 기반 필요(=fis)
Child child2 = (Child) ois.readObject(); // 역직렬화 (ClassNotFoundException 던짐)

System.out.println("parentName : " + child2.getParentName());
System.out.println("childName : " + child2.getChildName());

ois.close();
oos.close();
profile
갈 길이 멀다

0개의 댓글