직렬화된 객체를 저장하는 방법
Download
Report
Transcript 직렬화된 객체를 저장하는 방법
14장. 직렬화와 파일 입출력
학습목표
객체를 저장하는 방법
직렬화된 객체
객체 역직렬화
파일 저장 및 불러오기
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
객체 저장
비트박스 패턴을 저장합시다.
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
Save 버튼을 누르면 패턴을 저장할 수 있도록 만들
어봅시다.
데이터 저장 방법
직렬화를 사용하는 방법
• 저장된 데이터를 자바 프로그램에서만 사용하는 경우
• 객체를 통째로 저장한다고 생각하면 됩니다.
일반 텍스트 파일로 저장하는 방법
• 다른 프로그램에서도 사용할 수 있도록 할 때
• 파싱하기 좋도록 적당한 구분자로 각 필드를 구분해주는 것이 좋
습니다.
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
상태 저장 방법
1. 직렬화된 캐릭터 객체 세 개를 파일
에 저장하는 방법
“Ỉsr Character5 3+
2. 일반 텍스트 파일로 저장하는 방법
50,Elf,bow,sword,dust
200,Troll,bare hands,big ax
120,Magician,spells,invisibility
GameCharacter
int power
String type
Weapon[] wepaons
getWeapon()
useWeapon()
increasePower()
…
power: 50
type: Elf
wepons: bow,
sword, dust
power: 200
type: Troll
wepons: bare
hands, big ax
power: 120
type: Magician
wepons: spells,
invisibility
직렬화된 객체를 파일에 저장하는 방법
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
1. FileOutputStream 만들기
FileOutputStream fileStream = new FileOutputStream(“MyGame.ser”);
2. ObjectOutputStream 만들기
ObjectOutputStream os = new ObjectOutputStream(fileStream);
3. 객체 저장
os.writeObject(characterOne);
os.writeObject(characterTwo);
os.writeObject(characterThree);
4. ObjectOutputStream 닫기
os.close();
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
스트림
연결 스트림(connection stream)
출발지 또는 목적지로의 연결을 나타내는 스트림
일반적으로 너무 저수준이라서 연결 스트림만 가지고는
작업을 하기 힘듭니다.
연쇄 스트림(chain stream)
다른 스트림에 연쇄시켜야만 쓸 수 있음
고수준의 작업을 처리하기 위한 메소드를 제공
ObjectOutputStream
객체
0110100100110
0110100
100110
FileOutputStream
파일
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
힙 안에 들어있는 객체
width
직렬화된 객체
직렬화된 객체
height
Foo.ser
Foo myFoo = new Foo();
myFoo.setWidth(37);
myFoo.setHeight(70);
FileOutputStream fs = new FileOutputStream(“Foo.ser”);
ObjectOutputStream os = new ObjectOutputStream(fs);
os.writeObject(myFoo);
무엇이 저장될까?
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
“객체의 상태”란 정확하게 무엇을 의미할까?
인스턴스 변수 중에 원시 변수가 아닌 레퍼런스 변수가
있다면?
그 레퍼런스 변수가 참조하는 객체에 또 다른 객체를 참
조하는 인스턴스 변수가 있다면?
저장했을 때와 똑같은 상태의 객체를 얻기 위해 어떤 것
이 필요할지 생각해봅시다.
바보 같은 질문은 없습니다
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
직렬화할 때 정적 변수도 저장되나요?
그렇지 않습니다. 정적 변수는 클래스마다 하나씩 있는
변수이므로 현재 정적 변수에 들어있는 값을 받게 됩니다.
직렬화할 수 있는 객체를 만들 때는 동적으로 바뀔 수 있
는 정적 변수에 의존하지 않도록 만들어야 합니다.
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
객체의 직렬화
객체를 직렬화할 때는 그 객체와 관련된 모든 것이
저장됩니다.
인스턴스 변수로 참조된 모든 객체가 줄줄이 엮여
서 저장됩니다.
name
col
dogs
String Collar
Dog[]
Dog 객체
Kennel 객체
name
foof
col
barf
String Collar
Dog
Dog
Dog[] 객체
Dog 객체
Serializable 인터페이스
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
클래스를 직렬화할 수 있도록 하고 싶다면
Serializable 인터페이스를 구현해야 합니다.
Serializable 같은 인터페이스는 표지 인터페이스,
또는 태그 인터페이스라고 부르기도 합니다.
objectOutputStream.writeObject(myBox);
import java.io.*;
public class Box implements Serializable {
…
}
직렬화가 되지 않으면?
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
어떤 객체를 저장할 때 그 객체와 관련된 것들이 모
두 제대로 직렬화되지 않으면 그 직렬화 작업은 제
대로 완료되지 않습니다.
직렬화하고자 하는 모든 객체가 직렬화할 수 있는
(즉 Serializable을 구현하는) 클래스에 속해야 합니
다.
직렬화가 제대로 되지 않는 경우에는
NotSerializableException 예외가 발생합니다.
특정 변수를 생략하고 싶다면 어떻게 해야 할까요?
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
transient 키워드
특정 인스턴스 변수가 직렬화되지 않도록 하고 싶
다면 transient 키워드를 쓰면 됩니다.
import java.net.*;
import java.io.*;
class Chat implements Serializable {
transient String currentID;
String userName;
// 나머지 코드
}
역직렬화(deserialization)
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
직렬화된 객체를 원래 상태로 돌려놓는 것
1. FileInputStream 만들기
FileInputStream fileStream = new FileInputStream(“MyGame.ser”);
2. ObjectInputStream 만들기
ObjectInputStream os = new ObjectInputStream(fileStream);
역직렬화(deserialization)
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
3. 객체 읽기
Object one = os.readObject();
Object two = os.readObject();
Object three = os.readObject();
4. 객체 캐스팅
GameCharacter elf = (GameCharacter) one;
GameCharacter troll = (GameCharacter) two;
GameCharacter magician = (GameCharacter) three;
5. ObjectInputStream 닫기
os.close();
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
역직렬화 과정
1. 스트림으로부터 객체를 읽어옴
2. JVM에서 객체의 클래스 유형 결정
3. 클래스를 찾아서 불러오려는 시도를 함. 클래스를
찾거나 불러올 수 없으면 예외 발생
4. 객체가 힙 안에 자리를 잡는데, 이 때 생성자는 실
행되지 않습니다.
5. 상속 트리에서 윗부분에 직렬화할 수 없는 클래스
가 있으면 그 클래스의 생성자가 실행됩니다.
6. 인스턴스 변수에 직렬화된 값, 또는 기본값이 대
입됩니다.
바보 같은 질문은 없습니다
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
transient로 지정했던 변수는 나중에 역직렬화할
때 어떤 값으로 복구되나요?
무조건 null 값을 가지게 됩니다.
null이 들어있으면 안 되는 경우의 해결책
1. 특정 기본값을 정해놓고 복구할 때 항상 기본값을 대입하는 방
법
2. 해당 변수에서 중요한 역할을 하는 값을 따로 저장했다가 그 값
을 이용하여 복구하는 방법
텍스트 파일에 문자열로 저장하는 방법
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
직렬화된 객체를 저장하는 방법
objectOutputStream.writeObject(someObject);
String으로 저장하는 방법
fileWriter.write(“My First String to Save”);
import java.io.*;
class WriteAFile {
public static void main(String[] args) {
50,Elf,bow,sword,dust
try {
200,Troll,bare hands,big ax
FileWriter writer = new FileWriter(“Foo.txt”);
120,Magician,spells,invisibility
writer.write(“hello foo!”);
writer.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
암기장 프로그램
QuizCardBuilder
암기장을 만들고 저장하기 위한 간단한 도구
QuizCardReader
암기장을 불러와서 사용자에게 질문을 해 주는 클래스
QuizCard
카드 데이터를 나타내는 간단한 클래스
QuizCard
QuizCard(q, a)
question
answer
getQuestion()
getAnswer()
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
영화 “굿 윌 헌팅”에 나오는
대학교 이름은?
암기장 프로그램
세션 빈의 원격 레퍼런스에 대해
EJBObject.getPrimaryKey()를 호
출하면 어떤 일이 일어날까요?
QuizCardReader
QuizCardBuilder
QuizCardBuilder 개요
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
go() 메소드
GUI를 만들고 화면에 표시함
NextCardListener 내부 클래스
Next Card 버튼을 클릭했을 때 필요한 메소드 정의
SaveMenuListener 내부 클래스
메뉴에서 Save를 선택했을 때 필요한 메소드 정의
NewMenuListener 내부 클래스
메뉴에서 New를 선택했을 때 필요한 메소드 정의
saveFile() 메소드
파일에 내용을 저장하는 작업을 처리
java.io.File 클래스
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
File 클래스
디스크에 있는 “파일”을 나타내는 클래스
1. 이미 존재하는 파일을 나타내는 File 객체 만들기
File f = new File(“MyCode.txt”);
2. 새 디렉토리 만들기
File dir = new File(“Chapter14”);
dir.mkdir();
java.io.File 클래스
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
3. 디렉토리에 들어있는 내용의 목록 출력
if (dir.isDirectory()) {
String[] dirContents = dir.list();
for (int i = 0; i < dirContents.length; i++) {
System.out.println(dirContents[i]);
}
}
4. 파일 또는 디렉토리의 절대 경로명 구하기
System.out.println(dir.getAbsolutePath());
5. 파일 또는 디렉토리 삭제
boolean isDeleted = f.delete();
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
버퍼(buffer)
버퍼 ==
“Boulder”
“Boulder” “Aspen”
“Denver”
“Aspen Denver Boulder”
BufferedWriter
FileWriter
BufferedWriter writer = new BufferedWriter(new FileWriter(aFile));
Aspen
Denver
Boulder
파일
텍스트 파일 읽기
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
import java.io.*;
class ReadAFile {
public static void main(String[] args) {
try {
File myFile = new File(“MyText.txt”);
FileReader fileReader = new FileReader(myFile);
BufferedReader reader = new BufferedReader(fileReader);
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
QuizCardReader 개요
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
go() 메소드
GUI를 만들고 화면에 표시함
NextCardListener 내부 클래스
Next Card 버튼을 클릭했을 때 필요한 메소드 정의
OpenMenuListener 내부 클래스
메뉴에서 Open을 선택했을 때 필요한 메소드 정의
loadFile() 메소드
파일을 불러오고 ArrayList를 만드는 작업을 처리
makeCard() 메소드
텍스트 파일에서 한 행씩 읽어와서 문제와 답으로 파싱
후 CardList라는 ArrayList에 저장함
String의 split() 메소드
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
파란색과 노란색을 더하면?/녹색
빨간색과 파란색을 더하면?/보라색
StringTokenizer
String 객체와 구분자를 지정하면 알아서 조각을 내 주는
기능을 하는 클래스
파란색과 노란색을 더하면?
String toTest = “파란색과 노란색을 더하면?/녹색”
String[] result = toTest.split(“/”);
for (String token : result) {
System.out.println(token);
}
/
녹색
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
버전 ID
클래스를 변경했을 때 역직렬화 과정에서 문제가
생길 수 있는 경우
인스턴스 변수를 삭제한 경우
인스턴스 변수의 유형을 변경한 경우
transient로 지정하지 않았던 것을 transient로 지정하는
경우
클래스를 상속 계층에서 위나 아래로 옮기는 경우
Serializable이었던 클래스를 그렇지 않은 클래스로 변경
하는 경우
인스턴스 변수를 정적 변수로 변경하는 경우
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
버전 ID
클래스를 변경해도 역직렬화와 관련된 문제가 생기
지 않는 경우
인스턴스 변수를 추가하는 경우
상속 트리에 클래스를 추가하는 경우
상속 트리에서 클래스를 제거하는 경우
인스턴스 변수의 접근 레벨을 역직렬화 과정에서 변수에
값을 대입하는 데 문제가 없는 범위 내에서 변경하는 경
우
transient로 지정했던 인스턴스 변수를 transient가 아닌
것으로 지정하는 경우
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
101101
101101
1010101010
1010110101
클래스 버전 ID #343
으로 직렬화된 객체
Dog.class
클래스 버전 ID
#343
101101
10111101
1010101110
1010110101
Dog.class
클래스 버전 ID
#728
버전 ID
Dog 객체
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
serialVersionUID
serialVersionUID
객체를 직렬화할 때 그 객체에 찍히는 클래스의 버전 ID
클래스 구조에 대한 정보를 바탕으로 계산됨
serialVersionUID가 다르면 역직렬화가 되지 않음
클래스가 바뀔 것 같으면 클래스에 ID를 따로 저장
클래스가 바뀌더라도 버전 ID가 똑같도록 하면 JVM에서
직렬화된 객체와 클래스가 서로 호환되는 것으로 간주함
클래스를 변경할 때 역직렬화 과정에서 문제가 생기지 않
도록 주의
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
serialVersionUID
1. serialver라는 프로그램으로 버전 ID 확인
% serialver QuizCard
QuizCard: static final long serialVersionUID = -4640357841316419615L;
2. 출력된 결과를 클래스에 붙여넣음
public class Dog {
static final long serialVersionUID = -4640357841316419615L;
private String name;
private int size;
}
3. 역직렬화할 때 문제가 생기지 않도록 주의
패턴을 저장할 수 있는 비트박스 프로그램
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
RUNNING HEADER, 14 PT., ALL CAPS, Line Spacing=1 line
숙제
본문을 꼼꼼하게 읽어봅시다.
연필을 깎으며 및 14장 끝에 있는 연습문제를 모두
각자의 힘으로 해결해봅시다.
모든 코드를 직접 입력해서 실행시켜보고 코드를
다시 한 번 보면서 코드를 다른 사람한테 소개하는
연습을 해 봅시다.
API 문서에서 이 장에 나와있는 클래스 및 메소드
에 대한 내용을 직접 찾아봅시다.