객체 직렬화 ( Object Serialization )
객체의 내용을 파일 혹은 네트워크를 통하여 바이트 스트림으로 입출력
객체의 내용을 입출력 형식을 생각하지 않고 손쉽게 저장, 전송할 수 있다.
객체를 저장장치( 파일, 데이타베이스등)에 저장, 검색함으로써 객체의 경량 영속성 제공->자바빈즈에서 유용
객체 직렬화의 비밀
객체가 입출력되는 스트림은 분명 바이트 스트림일테니 바이트로 계산되지 않는다면 입출력도 불가능하다
그러므로 객체를 바이트로 계산해내는 기능이 제공되는 것이 분명하다.
직렬화라는 말은 무언가 일렬로 되어 있지 않은 것을 일렬로 만드는 작업을 뜻한다.
java.io.Serializable : 메소드도 없고 변수도 serializableVersionUID 하나만 가진 인터페이스이다.
객체를 직렬화 할수 있는지 없는지를 판별해주는 아주 중요한 역할을 한다.
java.lang.Object 클래스는 java.io.Serializable 인터페이스를 구현하지 않는다.
java.lang.Ojbect 클래스는 직렬화 될수 없으므로 스트림에서 사용할수 없다는 말이된다
java.lang.String 클래스는 java.io.Serializable 인터페이스를 구현하고 있다.
스트링 객체는 스트림상에서 이리저리 돌아 다닐수 있다는 얘기가 된다.
화면의 창객체를 띄우고 스트림으로 보내서 다른 컴퓨터에서 받아 똑같은 창을 뛰우는 일도 할수있다.
배열도 통체로 직렬화 시켜서 스트림위에 올려 놓을수 있다.
도대체 어떻게 객체들이 바이트들의 연속으로 변하는 것일까?
몰라도 된다.
java.io.ObjectInputStream과 java.io.ObjectOutputStream에서 이를 제공해준다.
객체가 직렬화 될때 객체와 상관없는 static 필드는 제외된다.
transient라고 선언된 인스턴스 필드들도 제외가 된다.
transient는 특정변수가 전송되거나 저장되어도 쓸모가 없는 변수에 사용한다.
ObjectInputStream, ObjectOutputStream 클래스를 사용하여 입출력
성능을 향상시킨 객체 직렬화 방법
http://wwwipd.ira.uka.de/~hauma/
OutputStream ObjectOutput
(abstract) ( interface)
| |
-----------------------
ObjectOutputStream new ObjectOutputStream(OutputStream out)
output destination
(outputStream)
InputStream ObjectInput
(abstract) ( interface)
| |
-----------------------
ObjectInputStream new ObjectInputStream(InputStream in)
Input destination
(InputStream)
ObjectOutput 인터페이스
기본 자료형의 자료 및 객체를 출력하기 위한 메소드 정의
DataOutput 인터페이스의 하위 인터페이스
★ 메소드
writeObject( Object obj)
flush()
close()
import java.io.*; class ObjectOutputTest |
ObjectInput 인터페이스
기본 자료형의 자료 및 객체를 입력하기 위한 메소드 정의
DataInput 인터페이스의 하위 인터페이스
★ 메소드
Object readObject() throws ClassNotFoundException
int read()
int read(byte[] buf)
int read(byte buf[], int off, int len)
long skip( long n )
int available()
close()
import java.io.*; class ObjectInputTest |
Serializable 인터페이스와 디폴트 객체 직렬화 매카니즘
Serializable 인터페이스를 구현하지 않는 클래스의 객체는 객체 직렬화로 입출력 할 수 없다.
NoSerializableException 이 발생
많은 표준 클래스들은 이미 Serializable 인터페이스를 구현하고 있다.
객체가 직렬화에 의해 입출력 될때, Serializable 인터페이스를 구현하는 최상위 클래스 부터 시작하여
transient로 선언되지 않은 인스턴스 변수가 입출력 된다.
인스턴스 변수가 객체를 참조하는 경우 그 객체도 입출력된다.
( 그 객체의 클래스도 Serializable 인터페이스를 구현하여야 한다. )
객체가 입력되면 해당 클래스의 객체가 생성되고, 그 인스턴스 변수의 값이 입력으로 부터 셋팅된다.
NonSerial ---------------------
MyList(Serializable) | int v1 |
---------------------
| int v2 |
| transient String v3 |
| MyList next --------->
---------------------
node1 node2 node3
next ------> next -------->
import java.io.*; class NonSerial public class MyList extends NonSerial implements Serializable public MyList(int v1, int v2, String v3) public static void main(String[] args) FileInputStream inFile |
1, 11, first: 2, 12, second: 3, 13, third:
0, 11, null: 0, 12, null: 0, 13, null:
Serializable을 구현하는 클래스에 다음 메소드가 정의되면, 그 클래스에 직접정의되어 있는
( 상위 혹은 하위 클래스에 정의 된 것은 제외)
인스턴스 변수가 입출력 되려고 할때, 대신 다음 메소드가 호출된다.
private void writeObject(ObjectOutputStream stream) throws IOException
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
import java.io.*; public class MyList2 extends NonSerial implements Serializable private void writeObject(ObjectOutputStream stream) public static void main(String[] args) FileInputStream inFile |
Writing MyList2 class part of object...
Writing MyList2 class part of object...
Writing MyList2 class part of object...
Reading MyList2 class part of object...
Reading MyList2 class part of object...
Reading MyList2 class part of object...
1, 11, first: 2, 12, second: 3, 13, thi
1, 11, null: 2, 12, null: 3, 13, null:
어느 한 객체가 직렬화 되기 위해서는 그 객체가 상속받는 클래스에 대한 정보도 모두 포함되어야 하기 때문에 직렬화된 정보에는 상위클래스 정보도 포함된다.
단, 상위클래스가 Serializable 인터페이스를 구현하지 않으면 포함될수 없다.
만일 이렇게 되었다면 복원시 문제가 된다. 그런경우를 위해 제한적이나마 직렬화에 관여 할수 있도록 마련된 클래스가 있으니 다음의 메소드이다.
private void writeObject ( java.io.ObjectOutputStream out ) throws IOException |
private void readObject ( java.io.ObjectInputStream in ) throws IOException, ClassNotFoundException |
객체가 직렬화 될때 writeObject() 메소드가 그 객체에 존재한다면 이 메소드를 이용해서 직렬화된 것에 자신만의 표현을 덧붙일수 가 있다.
이렇게 덧붙이기위해서는 항상 기본적인 직렬화 작업을 하는 defaultReadObject(), defaultWriteObject 메소드를 호출해야한다.
만일 객체에 readObject(),writeObject()가 없다면 ObjectInputStream의 defaultReadObject()메소드와 ObjectOutputStream의 defaultWriteObjet() 메소드만이 객체를 직렬화 하는데 쓰이게 된다.
import java.io.*; throws IOException, ClassNotFoundException {
class UnSerializable // implements Serializable 이렇게 하는것이 가장좋다. |
writeObject ----> 자동으로 호출
readObject
read : 1000, 2000
UnSerializable 클래스에는 인자가 있는 생성자 + 인자 없는 생성자 또는 인자 없는 생성자 또는 표기 안함의 생성자가 반드시 있어야 한다. 복원시 인자 없는 생성자가 사용되기 때문이다.
지금까지는 객체 직렬화와 복원에 관해서 간접적으로 관여한 예이지만 아예 이작엇을 자신의 코드로 대체할수 있는 수단도 있다.
바로 java.io.Externalizable 인터페이스이다.
Externalizable 인터페이스
이 인터페이스는 java.io.Serializable 인터페이스를 상속하며 다음과 같은 메소드가있다.
private void readExternal ( java.io.ObjectInput in ) |
private void writeExternal ( java.io.ObjectOutput out ) |
직렬화와 복원에 관한 모든 제어를 할수 있는 강력한 방법이다.
Serializable
|
Externalizable
: writeExternal( ObjectOutput stream ) throws IOException
readExteranl( ObjectInput stream ) throws IOException
객체 직렬화 방식에 대한 완전한 제어
보다 효율적인 직렬화가 가능
Externalizable 인터페이스를 구현하는 클래스의 객체가 입출력 될 때는 writeExternal, readExternal 메소드가 그 객체의 전체 입출력을 담당한다.
import java.io.*; public class ExternalizableTest implements Externalizable public static void main(String[] args) FileInputStream inFile |
Writing entire object...
Reading entire object...
1
1
import java.io.*; public class ExternalizableTest implements Externalizable public static void main(String[] args) FileInputStream inFile |
import import java.io.*; |
writeExternal()
readExternal()
1000
1000
인자없는 생성자가 반드시 필요 직렬화된 데이터를 읽어들여서 객체를 만들기 위해서는 먼저 인자없는 생성자를 이용한다.
인자있는 생성자만 선언되어있다면 java.lang.NoSuchMethodError가 발생하기 된다.
여러 객체의 직렬화
객체의 필드가 객체 참조값인 경우, 그 객체 또한 직렬화 된다.
각 ObjectOutputStream, ObjectInputStream객체는 그 객체를 통하여 현재까지 입출력된 객체들을 기억하여 비교함으로써 동일한 객체를 중복되게 입출력 하지 않는다.
--> 객체 참조의 공유 및 원형 참조 문제를 해결
ObjectOutputStream의 reset() 메소드를 호출하면, 현재까지 기억된 객체들을 버린다.
배열도 Object이므로 객체 직렬화롤 입출력 될수 있다.
import java.io.*; class ObjectSharingTest FileInputStream inFile |
MyList@2498b5
MyList@25ab41
MyList@e3e60
MyList@25ab41
MyList@25ab41
1234
객체 직렬화와 클래스 파일
객체가 출력될 때, 그 객체의 정확한 클래스 이름도 함께 객체 스트림에 출력된다.
객체가 입력될 때, 자바 가상머쉰은 그 객체의 클래스 파일을 읽어들여서 객체를 생성한다.
객체를 읽어들일때 해당 클래스 파일을 찾지 못하면 ClassNotFoundException이 발생한다.
객체 직렬화 관련 예외 클래스
IOException
- ObjectStreamException
- NotSerializableException
- NotActiveException
- OptionalDataException
- WriteAbortException
- StreamCorruptException
- InvalidClassException
- InvalidObjectException