직렬화된 객체를 저장하는 방법

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 문서에서 이 장에 나와있는 클래스 및 메소드
에 대한 내용을 직접 찾아봅시다.