Serializable 인터페이스는 자바에서 객체를 직렬화(Serialization)할 수 있도록 해주는 마커 인터페이스입니다. 직렬화란 객체의 상태를 바이트 스트림으로 변환하여
파일, 네트워크 등으로 전송하거나 저장할 수 있게 하는 기능입니다.
java.io.Serializabletransient 키워드를 사용하면 해당 필드는 직렬화 대상에서 제외됩니다.private static final long serialVersionUID = -6849794470754667710L;private static final long serialVersionUID = 8683452581122892189L;private static final long serialVersionUID = 362498820763181265L;private static final long serialVersionUID = 1360826667806852920L;private static final long serialVersionUID = 7523967970034938905L;이처럼 JDK의 주요 표준 클래스들도 serialVersionUID를 명시적으로 선언하여, 버전 호환성과 직렬화 안정성을 보장하고 있습니다.
serialVersionUID를 명시하지 않으면, 자바는 클래스 구조(필드, 메서드 등)를 바탕으로 자동으로 serialVersionUID를 생성합니다. 이때 클래스 구조가
변경되면 serialVersionUID 값도 달라져, 이전에 직렬화한 객체를 역직렬화할 때 InvalidClassException이 발생할 수 있습니다.
// 버전 1
public class AutoUIDPerson implements Serializable {
private String name;
public AutoUIDPerson(String name) {
this.name = name;
}
}
// 객체 직렬화
AutoUIDPerson person = new AutoUIDPerson("홍길동");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"));
oos.
writeObject(person);
oos.
close();
// 버전 2 (필드 추가)
public class AutoUIDPerson implements Serializable {
private String name;
private int age; // 필드 추가
public AutoUIDPerson(String name, int age) {
this.name = name;
this.age = age;
}
}
// 이전에 저장한 파일 역직렬화
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"));
AutoUIDPerson person = (AutoUIDPerson) ois.readObject(); // InvalidClassException 발생
ois.
close();
이처럼 serialVersionUID를 명시하지 않으면, 클래스 구조가 변경될 때마다 자동 생성된 값이 달라져 역직렬화에 실패할 수 있습니다. 따라서 버전 호환성을 위해
serialVersionUID를 명시적으로 선언하는 것이 안전합니다.
마커 인터페이스 패턴은 별도의 메서드 없이, 특정 인터페이스를 구현했다는 사실만으로 객체에 특별한 의미나 속성을 부여하는 디자인 패턴입니다. Serializable,
Cloneable, Remote 등이 대표적인 예입니다.
private void checkSerializable(Object object) throws NotSerializableException {
if (object instanceof Serializable) {
// 직렬화 OK!
} else {
throw new NotSerializableException();
}
}
아래는 Person 객체를 파일에 직렬화(저장)하고, 다시 역직렬화(복원)하는 예시입니다.
import java.io.*;
public class SerializationExample {
public static void main(String[] args) {
Person person = new Person("홍길동", 30, "secret");
String filename = "person.ser";
// 객체 직렬화
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
oos.writeObject(person);
System.out.println("직렬화 완료: " + person);
} catch (IOException e) {
e.printStackTrace();
}
// 객체 역직렬화
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename))) {
Person deserialized = (Person) ois.readObject();
System.out.println("역직렬화 결과: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
직렬화 완료: Person{name='홍길동', age=30, password='secret'}
역직렬화 결과: Person{name='홍길동', age=30, password='null'}
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient String password; // 직렬화 제외
public Person(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", password='" + password + '\'' +
'}';
}
}
아래와 같이 Serializable 인터페이스를 구현하지 않은 클래스는 직렬화가 불가능하며, 시도 시 NotSerializableException이 발생합니다.
public class NotSerializablePerson {
private String name;
private int age;
private Thread thread;
public NotSerializablePerson(String name, int age, Thread thread) {
this.name = name;
this.age = age;
this.thread = thread; // Thread는 직렬화 불가능
}
}