24
14. 직직직직 직직 직직직

14. 직렬화와 파일 입출력

  • Upload
    jera

  • View
    84

  • Download
    0

Embed Size (px)

DESCRIPTION

14. 직렬화와 파일 입출력. 객체 저장. 직렬화를 사용한다 . 그 데이터를 만들어낸 자바 프로그램에서만 사용한다면 납작해진 ( 직렬화된 ) 객체가 저장된 파일을 만든다 . 그리고 나중에 파일을 열 때는 프로그램에서 직렬화된 객체를 읽어서 다시 살아 숨쉬는 , 힙에 들어있는 형태의 객체로 만들면 된다 . 일반 텍스트 파일로 저장한다 . 데이터를 다른 프로그램에서도 사용한다면 다른 프로그램에서도 파싱할 수 있도록 적당한 구분자를 써서 파일에 저장한다 . - PowerPoint PPT Presentation

Citation preview

Page 1: 14.  직렬화와 파일 입출력

14. 직렬화와 파일 입출력

Page 2: 14.  직렬화와 파일 입출력

Head First JAVA

객체 저장

2

직렬화를 사용한다 .그 데이터를 만들어낸 자바 프로그램에서만 사용한다면 납작해진

( 직렬화된 ) 객체가 저장된 파일을 만든다 . 그리고 나중에

파일을 열 때는 프로그램에서 직렬화된 객체를 읽어서 다시 살아

숨쉬는 , 힙에 들어있는 형태의 객체로 만들면 된다 .

일반 텍스트 파일로 저장한다 .데이터를 다른 프로그램에서도 사용한다면 다른 프로그램에서도

파싱할 수 있도록 적당한 구분자를 써서 파일에 저장한다 .ex> 탭으로 각 필드를 구분하면 나중에 스프레드시트나

DB 어플리케이션 등에서도 그 파일을 이용할 수 있다 .

Page 3: 14.  직렬화와 파일 입출력

Head First JAVA

객체 직렬화 ( 저장 ) 방법 (1/2)

3

FileOutputStream 을 만든다 .FileOutputStream fileStream = new FileOutputStream(“MyGame.ser”);ObjectOutputStream 을 만든다 .ObjectOutputStream os = new ObjectOutputStream(fileStream);객체를 저장한다 .os.writeObject(characterOne);os.writeObject(characterTwo);os.writeObject(characterThree);

ObjectOutputStream 을 닫는다 .os.close();

“MyGame.ser” 이라는 파일이 존재하지 않으면 자동으로 새로 만들어짐

FileOutputStream 은 파일에연결하는 방법을 알고 있다

ObjectOutputStream 을 사용하면 객체를 저장할 수 있는데 파일에 직접 연결할 수는 없고 보조 객체가 필요하다 . 이런 과정을 한 스트림을 다른 스트림과 ‘연쇄 (chain)’ 한다고 부름 .

참조되는 객체를 직렬화하고“MyGame.ser” 에 저장

맨 위에 있는 스트림을 닫으면 그 밑에 있는 스트림도 모두 자동으로 닫힘 .

Page 4: 14.  직렬화와 파일 입출력

Head First JAVA

객체 직렬화 ( 저장 ) 방법 (2/2)

4

객 체

객체를 눌러준다 ( 직렬화 )

ObjectOutputStream( 연쇄 스트림 )

011010010110111001

객체를 바이트 형태로 저장

FileOutputStream( 연결 스트림 )

연쇄

011010010110111001

전달

파 일

Page 5: 14.  직렬화와 파일 입출력

Head First JAVA

직렬화된 객체 (1/5)

5

힙 안에 들어있는 객체 직렬화된 객체

widthheight

00100101 0100

0110

Foo myFoo = new Foo();myFoo.setWidth(37);myFoo.setHeight(70);힙에 들어있는 객체는 상태 ( 객체의 인스턴스 변수 값 ) 가 있다 . 이 값이 바로 어떤 클래스에 속하는 여러 인스턴스를 서로 구분할 수 있게 해 주는 역할을 한다 .

Foo.serFileOutputStream fs = new FileOutputStream(“foo.ser”);ObjectOutputStream os = new ObjectOutputStream(fs);os.writeObject(myFoo);직렬화된 객체에는 인스턴스 변수

값이 저장되어 있다 . 나중에 다시 원상태로 복구하면 힙 안에 이전과 똑같은 내용을 가진 인스턴스를 만들어낼 수 있다 .

0010010101000110

Page 6: 14.  직렬화와 파일 입출력

Head First JAVA

직렬화된 객체 (2/5)

6

객체가 직렬화되면 인스턴스 변수로 참조되는 모든 객체 또한 자동으로 직렬화된다 .

dogs

Dog[]

foof

Dog

barf

Dog

name

Stringcol

CollarKennel 객체

Dog[] 배열 객체

Dog 객체

name

String

col

Collar

Dog 객체

“Fido”

Dog 객체

size

int

Collar 객체

Page 7: 14.  직렬화와 파일 입출력

Head First JAVA

직렬화된 객체 (3/5)

7

import java.io.*; // Serializable 이 java.io 패키지에 들어 있음public class Box implements Serializable {

private int width;private int height;public void setWidth(int w) {

width = w; }public void setHeight(int h) {

height = h; }public static void main (String[] args) {

Box myBox = new Box();myBox.setWidth(50);myBox.setHeight(20);try { FileOutputStream fs = new

FileOutputStream(“foo.ser”);ObjectOut[utStream os = new

ObjectOutputStream(fs);os.writeObject(myBox);os.close();

} catch(Exception ex) {ex.printStackTrace();

} } }

클래스를 직렬화할 수 있게 하고 싶다면 Serializable 인터페이스를 구현해야 한다 .

입출력 부분에서예외를 던질 수 있음

구현해야 하는 메소드는 없지만 ‘ implements Serializable’ 이라고 하면 이 유형의 객체는 직렬화할 수 있다는 것을 알 수 있음

“foo.ser” 이라는 이름의 파일이 있으면 그 파일에 연결하고 그렇지 않으면 그 이름을 가지는 파일을 새로 만든다 .

연결스트림에 연쇄되는 ObjectOutputStream 을 만들고 나서 그 객체를 저장하라는 명령을 내림

Page 8: 14.  직렬화와 파일 입출력

Head First JAVA

직렬화된 객체 (4/5)

8

import java.io.*;public class Pond implements Serializable {

private Duck duck = new Duck();public static void main (String[] args) {

Pond myPond = new Pond();try {

FileOutputStream fs = new FileOutputStream(“Pond.ser”);

ObjectOut[utStream os = new ObjectOutputStream(fs);

os.writeObject(myPond);os.close();

} catch(Exception ex) {ex.printStackTrace();

}}

}public class Duck {

// duck 클래스}

객체를 저장할 때 그 객체와 관련된 것들이 모두 제대로 직렬화되지 않으면 그 직렬화는 제대로 완료되지 않는다 .

Pond 클래스에는 Duck 인스턴스 변수 한 개가 있음

Duck 클래스는 Serializable 을 구현하지 않기 때문에 Pond 객체를 직렬화하려고 하면 Pond 에 들어있는 Duck 인스턴스 변수가 저장될 수 없어 결국 직렬화되지 않는다 .

Page 9: 14.  직렬화와 파일 입출력

Head First JAVA

직렬화된 객체 (5/5)

9

import java.io.*; class Chat implements Serializable {

transient String currentID; String userName; // 나머지 코드

}

어떤 인스턴스 변수를 저장할 수 없다면 ( 또는저장해선 안 된다면 ) 그 변수는 transient 로 지정하면 된다 .

transient 는 직렬화할 때 이 변수는 저장하지 않고 그냥 넘어가라고 지정하기 위한 키워드

userName 변수는 직렬화하는 과정에서 객체 상태의 일부로 저장됨

자바 클래스 라이브러리에 있는 것은 대부분 직렬화할 수 있지만 네트워크 연결 , 스레드 , 파일 객체 같은 것은 저장할 수 없다

Page 10: 14.  직렬화와 파일 입출력

Head First JAVA

역직렬화 (1/5)

10

FileInputStream 을 만든다 .FileInputStream fileStream = new FileInputStream(“MyGame.ser”);

ObjectInputStream 을 만든다 .ObjectInputStream os = new ObjectInputStream(fileStream);

“MyGame.ser” 이라는 파일이 없으면 예외가 발생

FileInputStream 객체를 만든다 .FileInputStream 은 기존의 파일에 연결할 수 있다 .

객체를 읽는다 .Object one = os.readObject();Object two = os.readObject();Object three = os.readObject();

ObjectInputStream 은 객체를 읽게 해 주지만 파일에 직접 연결할 수 없기 때문에 반드시 연결 스트림에 연쇄되어야 한다 .

readObject() 를 호출하면 그 스트림의 다음 객체를 받아올 수 있어서 처음에 저장된 순서로 그대로 가져올 수 있다 . 저장 횟수보다 많이 가져오려고 하면 예외 발생

Page 11: 14.  직렬화와 파일 입출력

Head First JAVA

역직렬화 (2/5)

11

객체를 캐스트한다 .GameCharacter elf = (GameCharacter) one;GameCharacter troll = (GameCharacter) two;GameCharacter magician = (GameCharacter) three;

readObject() 의 리턴값은 Object 유형이므로 우리가 알고 있는 원래 유형으로 다시 캐스트해야 한다 .

ObjectInputStream 을 닫는다 .os.close(); 맨 위에 있는 스트림을 닫으면 그 밑에 있는

것도 같이 닫히기 때문에 FileInputStream ( 그리고 그 파일 ) 도 자동으로 닫힌다 .

Page 12: 14.  직렬화와 파일 입출력

Head First JAVA

역직렬화 (3/5)

12

객 체

클래스를 찾아서 불러온 다음인스턴스 변수값을 다시 대입함

ObjectInputStream( 연쇄 스트림 )

011010010110111001

객체를 바이트 형태로 읽음

FileInputStream( 연결 스트림 )

011010010110111001

읽는다

파 일연쇄

JVM 에서 클래스를 찾을 수 없거나 불러올 수 없으면 이 단계에서 예외가 발생

Page 13: 14.  직렬화와 파일 입출력

Head First JAVA

역직렬화 (4/5)

13

스트림으로부터 객체를 읽어온다 .

JVM 에서 객체의 클래스 유형을 결정한다 .

JVM 에서 객체의 클래스를 찾아서 불러오려는 시도를 하는데

클래스를 찾거나 불러오는데 실패한다면 JVM 에서 예외를 던지고

역직렬화는 실패한다 .

새로운 객체는 힙에 공간을 할당 받지만 직렬화된 객체는 처음

생성될 때의 상태가 아닌 직렬화되었을 때의 상태로 되돌아가야

하기 때문에 생성자가 실행되지 않는다 .

Page 14: 14.  직렬화와 파일 입출력

Head First JAVA

역직렬화 (5/5)

14

객체의 상속 트리에서 그 위 어딘가에 직렬화할 수 없는 클래스가

있다면 그 직렬화할 수 없는 클래스의 생성자가 실행된다 .일단 생성자 연쇄 호출이 시작되면 가장 가까운 직렬화할 수 없는

클래스부터 시작해서 그 위로 있는 모든 상위클래스의 생성자에서

상태를 새로 초기화하게 된다 .

객체의 인스턴스 변수에 직렬화된 상태값이 대입된다 . Transient 로 지정된 변수는 레퍼런스인 경우에는 null 이 , 원시변수인 경우에는 0, false 등이 주어진다 .

Page 15: 14.  직렬화와 파일 입출력

Head First JAVA

직렬화 예제 (1/3)

15

import java.io.*;

public class GameSaverTest {public static void main(String[] args) {

GameCharacter one = new GameCharacter(40, "Elf", new String[] {"bow", "sword", "dust"});

GameCharacter two = new GameCharacter(200, "Troll", new String[] {"bare hands", "big ax"});

GameCharacter three = new GameCharacter(120, "Magician", new String[] {"spells",

"invisibility"});

try {ObjectOutputStream os =

new ObjectOutputStream(new FileOutputStream("Game.ser"));os.writeObject(one);os.writeObject(two);os.writeObject(three);os.close();

} catch (IOException ex) {ex.printStackTrace();

}

몇 가지 캐릭터를 만든다 .

이렇게 해도 된다 .

Page 16: 14.  직렬화와 파일 입출력

Head First JAVA

직렬화 예제 (2/3)

16

one = null;two = null;three = null;try {

ObjectInputStream is =new ObjectInputStream(new FileInputStream("Game.ser"));

GameCharacter oneRestore = (GameCharacter) is.readObject();

GameCharacter twoRestore = (GameCharacter) is.readObject();

GameCharacter threeRestore = (GameCharacter) is.readObject();

System.out.println("One's type: " + oneRestore.getType());

System.out.println("Two's type: " + twoRestore.getType());

System.out.println("Three's type: " + threeRestore.getType());

} catch (Exception ex) {ex.printStackTrace();

}}

}

null 로 설정하면 힙에 있는 객체에 접근할 수 없다 .

파일로부터 다시 읽어드린다 .

제대로 읽혔는지 다시 확인한다 .

Page 17: 14.  직렬화와 파일 입출력

Head First JAVA

직렬화 예제 (3/3)

17

import java.io.*;public class GameCharacter implements Serializable {

int power;String type;String[] weapons;public GameCharacter(int p, String t, String[] w) {

power = p;type = t;weapons = w; }

public int getPower() {return power; }

public String getType() {return type; }

public String getWeapons() {String weaponList = "";for(int i = 0; i < weapons.length; i++) {

weaponList += weapons[i] + "";}return weaponList;

}}

Page 18: 14.  직렬화와 파일 입출력

Head First JAVA

핵심정리 (1/3)

18

객체를 직렬화하면 객체의 상태를 저장할 수 있다 .

객체를 직렬화하려면 ObjectOutputStream 이 필요하다 .

스트림에는 연결 스트림과 연쇄 스트림이 있다 .

연결 스트림은 출발지나 목적지에 대한 연결을 나타낸다 .

연쇄 스트림은 반드시 연결 스트림 또는 다른 스트림에 연쇄되어야

한다 .

객체를 직렬화해서 파일로 저장하고 싶다면

FileOutputStream 을 만들고 그 스트림에

ObjectOutputStream 에 연쇄시키면 된다 .

Page 19: 14.  직렬화와 파일 입출력

Head First JAVA

핵심정리 (2/3)

19

객체를 직렬화할 때는 ObjectOutputStream 의

writeObject(theObject) 메소드를 호출하면 된다 .

Serializable 인터페이스를 구현한 객체만 직렬화할 수 있다 . 상위 클래스 중 Serializable 을 구현하는 클래스가 있다면

자동으로 직렬화할 수 있는 클래스가 된다 .

객체가 직렬화되면 그 객체와 연관된 모든 객체가 직렬화된다 .

연관된 객체 중에 직렬화할 수 없는 것이 하나라도 있으면 직렬화

과정에서 예외가 던져진다 .

직렬화할 때 어떤 인스턴스 변수를 건너뛰고 싶다면 transient 키워드를 사용하면 된다 .

Page 20: 14.  직렬화와 파일 입출력

Head First JAVA

핵심정리 (3/3)

20

역직렬화를 할 때는 그 JVM 에서 해당 객체와 연관된 모든 객체의

클래스를 사용할 수 있어야만 한다 .

객체를 읽을 때는 처음에 객체를 저장한 것과 같은 순으로

읽어오게 된다 .

readObject() 의 리턴 유형은 Object 므로 역직렬화 과정에서

원래 유형으로 캐스트해야 한다 .

정적 변수는 직렬화되지 않는다 . 정적 변수 값은 한 유형에

속하는 모든 객체들에 들어있는 단 한 개 뿐인 값을 공유하기

때문에 특정 객체 상태의 일부로 저장할 이유가 없다 .

Page 21: 14.  직렬화와 파일 입출력

Head First JAVA

텍스트 파일로 저장

21

import java.io.*;class WriteAFile {

public static void main(String[] args) {try {

FileWriter writer = new FileWriter(“Foo.txt”);writer.write(“hello foo!”);writer.close();

} catch (IOException ex) {ex.printStackTrace();

}}

}

텍스트 데이터를 저장하는 것은 객체를 저장하는 것과 비슷하지만 객체 대신 String 을 저장하고 FileOutputStream 대신 FileWriter 를 쓴다 . ObjectOutputStream 에 연쇄시키지 않아도 된다 .

Write() 메소드는 String 을 인자로 받아들인다 .

“Foo.txt” 라는 파일이 없으면FileWriter 에서 새로 만든다 .

입출력 관련 코드는 모두try/catch 블록 안에집어넣어야 한다 .IOException 을 던질 수 있다 .

Page 22: 14.  직렬화와 파일 입출력

Head First JAVA

Java.io.File 클래스 (1/2)

22

File 객체는 디스크에 있는 파일이나 디렉토리의 이름과 경로를 나타낸다 . 하지만 그 파일에 들어있는 데이터를 나타낸다거나 그 데이터에 접근할 수 있게 해 주는 것은 아니다 .

이미 존재하는 파일을 나타내는 File 객체를 만든다 .File f = new File(“MyCode.txt”);

새로운 디렉토리를 만든다 .File dir = new File(“Chapter7”);dir.mkdir();

Page 23: 14.  직렬화와 파일 입출력

Head First JAVA

Java.io.File 클래스 (2/2)

23

디렉토리에 들어있는 내용의 목록을 출력한다 .if (dir.isDirectory()) {

String[] dirContents = dir.list();for (int i = 0; i < dirContents.length; i++) {

System.out.println(dirContents[i]);}

}

파일 또는 디렉토리의 절대 경로명을 구한다 .System.out.println(dir.getAbsolutePath());

파일 또는 디렉토리를 삭제한다 ( 성공한 경우에는 true 를 리턴 ).boolean isDeleted = f.delete();

Page 24: 14.  직렬화와 파일 입출력

Head First JAVA

텍스트 파일을 읽는 방법

24

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();

}}

}

File 객체와 FileReader 객체 , BufferedReader 를 이용한다 .

행을 읽어올 때마다 각 행을 저장하기 위한 String 변수를 만든다 .

FileReader 는 텍스트 파일로 연결되는문자를 위한 연결 스트림이다 .

읽기작업의 효율을 향상시키기 위해 FileReader 를 BufferedReader 에 연쇄시킨다 .

텍스트 한 행을 읽은 다음 그 행을 line 이라는 String 변수에 저장하는 부분이다 . 읽을 것이 있으면 null 이 아니기 때문에 방금 읽어온 것을 출력한다 .