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