자바

자바 객체 입출력 보조 스트림 - ObjectInputStream , ObjectOutputStream

알통몬_ 2017. 4. 10. 09:17
반응형


안녕하세요 알통몬입니다.

공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^

 


객체 입출력 보조 스트림 :

자바는 메모리에 생성된 객체를 파일이나 네트워크로 출력이 가능합니다.

 => 객체는 문자가 아니기 때문에 바이트 기반 스트림으로 출력해야합니다.

객체 직렬화 : 객체를 출력하기 위해 객체의 데이터를 일렬로 늘어선 연속적인 바이트로 변경하는 것을 말합니다.

객체 역직렬화 : 파일에 저장되어 있거나 네트워크에서 전송된 객체를 읽을 수 있는데,

입력 스트림으로부터 읽어 들인 연속적인 바이트를 객체로 복원하는 것을 말합니다.


ObjectInputStream / ObjectOutputStream

 : 객체를 입력 또는 출력할 수 있는 보조 스트림입니다.

ObjectOutputStream은 객체 직렬화에 사용되고

ObjectInputStream은 객체 역직렬화에 사용됩니다.


다른 보조 스트림들과 마찬가지로 연결할 바이트 입출력 스트림을 매개값으로 가집니다.

ObjectOutputStream objectOutputStream = new ObjectOutputStream(바이트 출력  스트림);

ObjectInputStream ObjectInputStream = new ObjectInputStream(바이트 입력 스트림);


writeIbject()를 사용하면 객체를 직렬화 할 수 있고 ( objectOutputStream.writeObject(객체);

readObject()를 사용하면 역직렬화 할 수 있습니다.

객체타입 변수 = (객체타입)objectInputStream.readObject();

예제)

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;


public class Example {

 public static void main(String[] args) throws Exception {

  FileOutputStream fileOutputStream = new FileOutputStream("C:/Temp/Object.dat");

  ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);  

  

  objectOutputStream.writeObject(new Integer(10));

  objectOutputStream.writeObject(new Double(3.14));

  objectOutputStream.writeObject(new int[] { 1, 2, 3 });

  objectOutputStream.writeObject(new String("티스토리"));

  

  objectOutputStream.flush();

  objectOutputStream.close(); 

  fileOutputStream.close(); // 다양한 객체를 파일에 저장.

  

  FileInputStream fileInputStream = new FileInputStream("C:/Temp/Object.dat");

  ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

  

  Integer obj1 = (Integer) objectInputStream.readObject();

  Double obj2 = (Double) objectInputStream.readObject();

  int[] obj3 = (int[]) objectInputStream.readObject();

  String obj4 = (String) objectInputStream.readObject();// 저장한 객체를 파일로부터 읽어 객체 복원.

  

  objectInputStream.close();

  fileInputStream.close();

  

  System.out.println(obj1);

  System.out.println(obj2);

  System.out.println(obj3[0] + "," + obj3[1] + "," + obj3[2]);

  System.out.println(obj4);

 }

}


직렬화가 가능한 클래스 (Serializable)

 : Serializable 인터페이스를 구현한 클래스만이 직렬화가 가능합니다.

 Serializable 인터펭스는 필드나 메소드가 없는 빈 인터페이스입니다.

객체를 직렬화 할 때 private 필드를 포함한 모든 필드를 바이트로 변환해도 좋다는 표시 역할을 합니다.


public class ABC implements Serializable { /// }

필드는 객체 직렬화를 통해 변환 되는데요, 필드 선언에 static이나 transient가 붙어 있다면 직렬화가 안됩니다.

생성자와 메소드도 직렬화에 포함되지 않습니다.

예제)

public class AAA implements Serializable {

   public int a;

   protected int b;

   int c;

   private int d;

   public static int e; // 직렬화

   transient int f; // 대상에서 제외

 }


예제1)

import java.io.Serializable;


public class ClassA implements Serializable {

 int field1;

 ClassB field2 = new ClassB();

 static int field3; // 직렬화 불가.

 transient int field4; // 직렬화 불가

 

}

예제2)

import java.io.Serializable;


public class ClassB implements Serializable {

 int field1;

}

 


예제3) 직렬화해서 파일에 저장

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;


public class SerializableWriter {

 public static void main(String[] args) throws Exception {

  FileOutputStream fileOutputStream = new FileOutputStream("C:/Temp/Object.dat");

  ObjectOutputStream ObjectOutputStream = new ObjectOutputStream(fileOutputStream);  

  ClassA classA = new ClassA();

  classA.field1 = 1;

  classA.field2.field1 = 2;

  classA.field3 = 3;

  classA.field4 = 4;

  ObjectOutputStream.writeObject(classA);

  ObjectOutputStream.flush(); 

  ObjectOutputStream.close();

  fileOutputStream.close(); 

 }

 

}


예제4) 역직렬화하여 복원된 필드 출력

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;


public class SerializableReader {

 public static void main(String[] args) throws Exception {

  FileInputStream fileInputStream = new FileInputStream("C:/Temp/Object.dat");

  ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

  ClassA v = (ClassA) objectInputStream.readObject();

  System.out.println("field1: " + v.field1);

  System.out.println("field2.field1: " + v.field2.field1);

  System.out.println("field3: " + v.field3);

  System.out.println("field4: " + v.field4);

 }

 

}


serialVersionUID : 같은 클래스임을 알려주는 식벽자 역할을 합니다.

역직렬화를 할 때는 객체를 직렬화했을 때와 같은 클래스를 사용해야 합니다.

클래스의 이름이 같아도 내용이 변경되면 역직렬화가 되지 않습니다. 또한 예외가 발생합니다.


Serializable 인터페이스를 구현한 클래스를 컴파일하게 되면 자동적으로 serialVersionUID 정적 필드가

추가됩니다. 그리고 클래스를 재컴파일하게되면 serialVersionUID 값은 달라집니다.

네트워크로 객체를 직렬화하여 보낼 때 보내는 쪽과 받는 쪽이 같은 serialVersionUID를 갖는 클래스를

가져야 하는데 한 쪽에서 클래스를 재컴파일하면 다른 serialVersionUID를 가지게 되서 역직렬화가 되지 않습니다.

예제)

예제1)

import java.io.Serializable;


public class ClassC implements Serializable {

 int field1;

 

}


예제2)

import java.io.FileOutputStream;

import java.io.ObjectOutputStream;


public class SerialVersionUIDExample1 {

 public static void main(String[] args) throws Exception {

  FileOutputStream fos = new FileOutputStream("C:/Temp/Object.dat");

  ObjectOutputStream oos = new ObjectOutputStream(fos);  

  ClassC classC = new ClassC();

  classC.field1 = 1;

  oos.writeObject(classC);

  oos.flush(); oos.close();  fos.close(); 

 }

 

}


예제3)

import java.io.FileInputStream;

import java.io.ObjectInputStream;


public class SerialVersionUIDExample2 {

 public static void main(String[] args) throws Exception {

  FileInputStream fileInputStream = new FileInputStream("C:/Temp/Object.dat");

  ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

  ClassC classC = (ClassC) objectInputStream.readObject();

  System.out.println("field1: " + classC.field1);

 }

 

}

여기까지는 이전과 하는 방법이 같습니다.

그리고 이제 다시 class C에 필드하나를 추가하여 재컴파일하면??

예제4)

import java.io.Serializable;


public class ClassC implements Serializable {

 int field1;

       int field2;

 

}

저장하고 바로 SerialVersionUIDExample2예제를 실행합니다.

예외가 발생하게됩니다.

* 불가피하게 클래스의 수정이 필요하다면 serialVersionUID값을 명시적으로 주어

에러를 피할 수 있습니다.

public class CCC implements Serializable{

   static final long serialVersionUID = 정수값;

  ...

}


writeObject() 와 readObject()

 : 두 클래스가 상속 관계에 있다고 할 때 부모 클래스가 Serializable 인터페이스를 구현하고 있다면

 자식 클래스는 자동적으로 Serializable를 구현하고 있지 않아도 자식 객체를 직렬화하면 

부모 필드 및 자식 필드가 모두 직렬환 됩니다. 

하지만 자식 클래스만 Serializable인터페이스를 구현하고 있다면???

자식 필드를 직렬화할 때 부모 필드는 직렬화에서 제외됩니다.

이런 경우에 부모 필드까지 직렬화하고 싶다면? 

1. 부모 클래스가 Serializable인터페이스를 구현하도록 하거나

2. 자식 클래스에서 writeObject() 와 readObject() 메서드를 선언해 부모 객체의 필드를 직접 출력하면 됩니다.

예제)

예제1)

public class Parent {

 public String field1;

 

}


예제2)

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;


public class Child extends Parent implements Serializable {

 public String field2;

 

 private void writeObject(ObjectOutputStream out) throws IOException {

    out.writeUTF(field1);

    out.defaultWriteObject();

 }


 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {

    field1 = in.readUTF();

    in.defaultReadObject();

 }

 

}


예제3)

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;


public class NonSerializableParentExample {

 public static void main(String[] args) throws Exception {

  FileOutputStream fileOutputStream = new FileOutputStream("C:/Temp/Object.dat");

  ObjectOutputStream ojbectOutputStream = new ObjectOutputStream(fileOutputStream);  

  Child child = new Child();

  child.field1 = "티스토리"; 

  child.field2 = "알통몬";

  ojbectOutputStream.writeObject(child);

  ojbectOutputStream.flush();

  ojbectOutputStream.close(); 

  fileOutputStream.close(); 

  

  FileInputStream fileInputStream = new FileInputStream("C:/Temp/Object.dat");

  ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

  Child v = (Child) objectInputStream.readObject();

  System.out.println("field1: " + v.field1);

  System.out.println("field2: " + v.field2);

  objectInputStream.close();

  fileInputStream.close();

 }

}


이상입니다.

다음 포스팅부터는 네트워크에 대해 공부하겠습니다.

스트림관련 포스팅들↓

2017/04/10 - [자바] - 자바 기본 타입 입출력 보조 스트림 - DataInputStream , DataOutputStream / 프린터 보조 스트림 - PrintStream, PrintWriter


2017/04/10 - [자바] - 자바 성능 향상 보조 스트림 - BufferedInputStream, BufferedReader / BufferedOutputStream, BufferedWriter


2017/04/07 - [자바] - 자바 보조 스트림 / 문자 변환 보조 스트림 InputStreamReader / OutputStreamWriter


2017/04/06 - [자바] - 자바 파일 입출력 Java FileReader / FileWriter


2017/04/06 - [자바] - 자바 파일 입출력 Java File 클래스 / FileInputStream 클래스 / FileOutputStream 클래스


2017/04/04 - [자바] - 자바 콘솔 입출력 Java Console, Scanner


2017/04/04 - [자바] - 자바 콘솔 입출력 Java System.in, System.out


2017/04/01 - [자바] - 자바 입력 스트림과 출력 스트림 Java Reader, Writer


2017/04/01 - [자바] - 자바 IO 출력 스트림 java OutputStream -> write(int b), write(byte[] b), write(byte[] b, int off, int len)


2017/04/01 - [자바] - 자바 IO 기반의 입출력과 네트워킹 => IO 패키지 / 입력 스트림, 출력 스트림


2017/03/31 - [자바] - 자바 스트림 Java Stream => 기본 집계 ( sum(), count(), average(), max(), min() ) / 커스텀 집계 ( reduce() )


2017/03/31 - [자바] - 자바 스트림 Java Stream => 루핑 (peek(), forEach()) / 매칭( allMatch(), anyMatch(), noneMatch() )


2017/03/30 - [자바] - 자바 스트림 Java Stream => 매핑( flatMapXXX(), mapXXX() , asXXXStream(), boxed()) / 정렬(sorted())


2017/03/30 - [자바] - 자바 스트림 Java Stream -> 스트림 파이프 라인과 필터링 (distinct(), filter())


2017/03/30 - [자바] - 자바 스트림 Java Stream => 스트림의 종류


2017/03/27 - [자바] - 자바 스트림 Java Stream - 스트림이란?, 반복자 스트림


반응형