클래스의 인스턴스가 생성되면 인스턴스의 상태/ 인스턴스 변수 값은 마치 생명체처럼 계속 변하게 된다.
인스턴스의 어느 순간 상태를 그대로 저장하거나 네트워크를 통해 전송할 일이 있을 수 있다.
이것을 '직렬화' (Serialization)라고 한다.
그리고 저장된 내용이나 전송받은 내용을 다시 복원하는 것을 '역직렬화' (Deserialization)라고 한다.
다시 말해서 직렬화란 인스턴스의 내용을 연속 스트림으로 만드는 것이다.
스트림으로 만들어야 파일에 쓸 수 있고 네트워크로 전송할 수도 있다. 따라서 직렬화 과정에서 하는 일은 인스턴스 변수 값을 스트림으로 만드는 것이다. 자바에서는 이를 보조 스트림인 ObjectInputStream과 ObjectOutputStream을 사용하여 좀 더 쉽게 구현할 수 있다.
Serialization 직렬화 -
인스턴스의 어느 순간 상태를 그대로 저장하거나 네트워크를 통해 전송하는 것
- 클래스의 인스턴스가 생성되면 인스턴스의 상태, 즉 인스턴스 변수 값은 계속 변화함
- 직렬화로 인스턴스 내용을 연속 스트림으로 만듬
- 스트림으로 만들어야 파일에 쓰거나 네트워크로 전송 가능
- 직렬화 하고자 하는 클래스에서 반드시 Serializable 인터페이스를 구현해야 함
- 직렬화시 모든 유형의 객체를 저장해야 하기 때문에 Object 클래스형으로 저장됨
Deserialization 역직렬화-
저장된 내용이나 전송받은 내용을 다시 복원하는 것
- 역직렬화를 할 때 클래스 정보가 존재하지 않을 수도 있으므로
ClassNotFoundException 예외도 처리해야함 - Object 클래스형으로 읽어 들인 객체를
저장할 클래스 자료형에 맞게 형 변환(Downcasting) 해야 함
ObjectInput / OutputStream
-직렬화를 도와주는 자바의 보조 스트림
ObjectInputStream(InputStream in) | InputStream을 매개변수로 받아 ObjectInputStream 생성 |
ObjectOutputStream(OutputStream out) | OutputStream을 매개변수로 받아 ObjectOutputStream 생성 |
저장할 파일이나 전송할 네트워크 등의 기반 스트림을 매개변수로 받아서 인스턴스 변수 값을 저장하거나 전송한다.
Object readObject() | ObjectInputStream 클래스 일반 메소드 객체 데이터를 스트림으로 불러올 때 사용 |
void writeObject(Object obj) | ObjectOutputStream 클래스 일반 메소드 객체 데이터를 외부로 전송할 때 사용 |
직렬화를 테스트 해보자.
Person 클래스를 하나 만들고 인스턴스로 생성한 후 파일에 썼다가 복원하는 예제를 보자.
먼저 Person클래스의 생성자로 두 인스턴스를 생성한 후 이를 serial.out 파일에 저장하고 - 직렬화
serial.out 파일에서 저장된 내용을 읽어 원래 인스턴스 상태로 복원해보자 - 역직렬화
package stream.serialization;
public class Person {
private static final long serialVersionUID = -1923204802334L;
String name;
String job;
public Person() {}
public Person(String name, String job) {
this.name = name;
this.job = job;
}
public String toString() {
return name + "," + job;
}
}
class Person implements Serializable{
private static final long serialVersionUID = -1503252402544036183L;
String name;
transient String job;
public Person() {}
public Person(String name, String job) {
this.name = name;
this.job = job;
}
public String toString()
{
return name + "," + job;
}
}
public class SerializationTest {
public static void main(String[] args) throws ClassNotFoundException {
Person personAhn = new Person("안재용", "대표이사");
Person personKim = new Person("김철수", "상무이사");
try(FileOutputStream fos = new FileOutputStream("serial.out");
ObjectOutputStream oos = new ObjectOutputStream(fos)){
oos.writeObject(personAhn); // 인스턴스 값을 파일에 씀 - 직렬화
oos.writeObject(personKim);
}catch(IOException e) {
e.printStackTrace();
}
try(FileInputStream fis = new FileInputStream("serial.out");
ObjectInputStream ois = new ObjectInputStream(fis)){
Person p1 = (Person)ois.readObject(); // 파일로부터 값을 읽어들임 - 역직렬화
Person p2 = (Person)ois.readObject();
System.out.println(p1);
System.out.println(p2);
}catch (IOException e) {
e.printStackTrace();
}
}
}
serial.out 파일을 열어보면 날아볼 수 없는 내용들이 써있다. 참고로 역질렬화를 할 떄는 클래스의 정보가 존재하지 않을 수 있기에 ClassNotFoundException도 처리해야한다.
그런데 프로그램을 실행하면 오류가 생긴다.
직렬화는 인스턴스의 내용이 외부로 유출되는 것이기에 프로그래머가 직렬화를 하겠다는 의도를 표시해야한다.
따라서 Person 클래스에 마커 인터페이스인 Serializable 인터페이스를 추가해야한다.
Serializable 인터페이스
- 직렬화는 인스턴스 내용이 외부로 유출되는 것이므로 프로그래머가 직렬화를 하겠다는 의도를 표시해야 하는데, 이 때 구현해야 하는 인터페이스
- Marker Interface (마커 인터페이스)
다른 기능은 없고 해당 클래스를 직렬화하겠다는 의사만 명시하는 인터페이스 - 추상 메소드가 없는 인터페이스
오버라이딩을 하지 않아도 됨(할 수 없음)
형식
class Person implements Serializable {
}
transient 예약어
- 직렬화 / 역직렬화 과정에서 제외하고자 하는 변수 앞에 작성하는 예약어
사전적 의미: '일시적인, 일시적으로 머무르는'
- 직렬화 될 수 없는 클래스(ex. Socket)가 인스턴스 변수로 있는 경우에 사용
- 직렬화를 원치 않는 변수에 사용
- transient 예약어를 사용한 변수에는 그 자료형의 기본 값이 저장됨
ex) 객체 자료형의 경우 null 값이 저장됨
직렬화의 대상이 되는 클래스는 모든 인스턴스 변수가 직렬화되고 복원된다. 그렇기에 어떤 변수를 특정하여 직렬화를 하지 않으려면 이 예약어를 사용하면 된다. 그러면 해당 변수는 직렬화되고 복원되는 과정에서 제외된다. 이 예약어를 사용한 변수 정보는 그 자료형의 기본값으로 저장된다. 따라서 객체 자료형인 경우 null 값이 된다.
// 역직렬화 할때와 직렬화를 했었을 때의 상태가 다르면 오류가 발생한다. 내용이 변경된 경우이다. 이러한 경우 클래스의 버전관리를 개발자가 할 수 있다.
Externalizable 인터페이스
직렬화를 하는 데 사용하는 인터페이스. 프로그래머가 구현해야하는 메서드가 있다. 객체의 직렬화와 역직렬화를 직접 세밀하게 제어하고자 할 때 메서드에 내용을 구현한다.
package stream.serialization;
import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
class Dog implements Externalizable{
String name;
public Dog() {}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name =in.readUTF();
}
public String toString() {
return name;
}
}
public class ExternalizableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Dog myDog = new Dog();
myDog.name = "멍멍이";
FileOutputStream fos = new FileOutputStream("external.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
try(fos; oos){
oos.writeObject(myDog);
}catch(IOException e) {
e.printStackTrace();
}
FileInputStream fis = new FileInputStream("external.out");
ObjectInputStream ois = new ObjectInputStream(fis);
Dog dog = (Dog)ois.readObject();
System.out.println(dog);
}
}
여기서 읽고 쓰는 내용을 프로그래머가 구현해야한다.
'공부 STUDY > JAVA' 카테고리의 다른 글
[JAVA] Assertions.assertThat 비교 하기 (0) | 2023.01.25 |
---|---|
[JAVA] File 클래스 | RandomAccessFile 클래스에 대해 알아보자 (0) | 2023.01.24 |
[JAVA] 보조 스트림(Wrapper 스트림)에 대해 알아보자 |FIilterInputStream, FilterOutputStream (0) | 2023.01.24 |
[JAVA] 자바 입출력과 스트림 - (4) | 문자 단위 스트림 (0) | 2023.01.24 |
[JAVA] 자바 입출력과 스트림 - (3) | 바이트 단위 스트림 - OutputStream (0) | 2023.01.24 |