개인참고자료/자바(네트워크)

객체 스트림 - 나의 책 목록(객체 스트림을 이용해 저장, 읽기)

경진 2008. 7. 17. 00:24
나의 책 목록

1. 책목록은 java.util.ArrayList에 저장한다.
2. 책의 정보를 저장하기 위한 Book 클래스를 만든다. Book 클래스는 마샬링될 수 있도록 반드시 java.io.Serializable 인터페이스를 구현해야한다.
3. Book 클래스는 필드로서 isbn, 제목, 저자, 가격 정보를 포함하며, 각각 java.lang.String, java.langString, java.lang.String, int형으로 선언된다.
4. 마샬링을 하려면 java.io.ObjectOutputStream을 이용해야 하며, 언마샬링을 하려면 java.io.ObjectInputStream을 이용하면 된다.
5. 객체를 직렬화해서 저장할 파일명은 booklist.dat다.

객체의 경우 마샬링을 하려면 반드시 java.io.Serializable 인터페이스를 구현해야 한다고 했다. 그렇기 때문에 사용자가 만드는 Book 클래스의 경우 java.io.Serializable 인터페이스를 구현해야 한다.

그렇다면 java.util.Vector, java.util.ArrayList, java.lang.String 클래스는 API를 찾아보면 해당 객체들 모두 java.io.Serializable 인터페이스를 이미 구현했다.

API를 통해서 java.io.Serializable 인터페이스에 대한 내용도 살펴본다. 자주 사용되는 많은 객체들이 java.io.Serializable 인터페이스를 구현하고 있는 것을 확인할 수 있다.

객체 스트림을 이용해서 저장하고, 읽어 들이기 위한 예제이다.

import java.io.Serializable;

public class Book implements Serializable {
    private String isbn;
    private String title;
    private String author;
    private int price;

    public Book(String isbn, String title, String author, int price){
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public String getIsbn() {
        return isbn;
    }

    public int getPrice() {
        return price;
    }

    public String getTitle() {
        return title;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String toString(){
        return getIsbn() + "," + getTitle() + "," + getAuthor() + "," + getPrice();
    }
}

Book 클래스는 마샬링되기 위해서 java.io.Serializable 인터페이스를 구현하고 있다. Book 클래스에 있는 필드는 java.lang.String형과 기본형인 int로 구성되어 있기 때문에 모두 직렬화할 수 있다.

객체를 직렬화할 수 있다는 것은 객체에 있는 필드 값과 필드가 참조하는 객체들이 마샬링된다는 것을 의미한다.

이 부분을 잘못 이해해서, 객체에 있는 메소드의 코드도 모두 마샬링된다고 생각하면 안 된다. 메소드의 코드까지 모두 마샬링되어 전송되거나 써진다면, 불필요한 과부하가 많이 발생할 것이다. 중요한 것은 객체에 있는 필드의 내용이기 때문이다.

그렇기 때문에 읽어 들이는 쪽에서도 언마샬링을 하기 위해서 전달한 객체의 클래스가 있어야만, 그리고 전달받은 객체에 대한 클래스가 있어야만 원래 형태로 온전하게 복원할 수 있다.

BookObjectOutputTest는 책 세권에 대한 정보를 ArrayList에 저장한 후 객체 스트림을 이용해서 booklist.dat 파일에 저장한다.

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

public class BookObjectOutputTest{

    public static void main(String[] args) {
        FileOutputStream fout = null;
        ObjectOutputStream oos = null;

        ArrayList list = new ArrayList();
        Book b1 = new Book("a0001", "자바완성", "홍길동", 10000);
        Book b2 = new Book("a0002", "스트럿츠", "김유신", 20000);
        Book b3 = new Book("a0003", "기초 EJB", "김성박", 25000);
        list.add(b1);
        list.add(b2);
        list.add(b3);
         
        try{
            fout = new FileOutputStream("booklist.dat");
            oos = new ObjectOutputStream(fout);
           
            oos.writeObject(list);
           
            System.out.println("저장되었습니다.");
           
        }catch(Exception ex){
        }finally{
            try{
                oos.close();
                fout.close();
            }catch(IOException ioe){}
        } // finally
    } // main end
} // class end

        ArrayList list = new ArrayList();
        Book b1 = new Book("a0001", "자바완성", "홍길동", 10000);
        Book b2 = new Book("a0002", "스트럿츠", "김유신", 20000);
        Book b3 = new Book("a0003", "기초 EJB", "김성박", 25000);
        list.add(b1);
        list.add(b2);
        list.add(b3);

ArrayList에 Book 객체 세 개를 추가하고 있다. ArrayList, Book 클래스 모두 java.io.Serializable 인터페이스를 구현하고 있다.

            fout = new FileOutputStream("booklist.dat");
            oos = new ObjectOutputStream(fout);
           
            oos.writeObject(list);
           
            System.out.println("저장되었습니다.");

이 소스가 가장 중요한 부분이다. 객체 스트림인 ObjectOutputStream의 생성자에 인자로 FileOutputStream을 집어넣고 있다. 즉, ObjectOutputStream은 객체를 직렬화해서 FileOutputStream의 생성자에 지정한 booklist.dat 파일에 쓰게 된다.

BookObjectInputTest는 직렬화된 객체를 읽어 들여 원래의 형태로 변환한 후 출력하는 예제이다.

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;

public class BookObjectInputTest{

    public static void main(String[] args) {
        FileInputStream fin = null;
        ObjectInputStream ois = null;

        try{
            fin = new FileInputStream("booklist.dat");
            ois = new ObjectInputStream(fin);
           
            ArrayList list = (ArrayList)ois.readObject();
            Book b1 = (Book)list.get(0);
            Book b2 = (Book)list.get(1);
            Book b3 = (Book)list.get(2);
           
            System.out.println(b1.toString());
            System.out.println(b2.toString());
            System.out.println(b3.toString());
           
        }catch(Exception ex){
        }finally{
            try{
                ois.close();
                fin.close();
            }catch(IOException ioe){}
        } // finally
    } // main end
} // class end

※ BookObjectOutputTest에서 ArrayList를 writeObject 메소드로 저장했다

            fin = new FileInputStream("booklist.dat");
            ois = new ObjectInputStream(fin);

파일로부터 객체를 읽어 들이기 위해서 객체 스트림인 ObjectInputStream의 생성자에 FileInputStream을 인자로 집어 넣었다.

            ArrayList list = (ArrayList)ois.readObject();

ObjectInputStream에 있는 readObject() 메소드를 이용해서 저장된 객체를 읽어 들인다. 저장되었던 객체가 ArrayList였기 때문에 원래의 형태로 형 변환했다.

            Book b1 = (Book)list.get(0);
            Book b2 = (Book)list.get(1);
            Book b3 = (Book)list.get(2);
           
            System.out.println(b1.toString());
            System.out.println(b2.toString());
            System.out.println(b3.toString());

ArrayList에 저장된 Book 객체를 하나씩 읽어 들여 출력한다.

결과 화면

출력 결과

저장된 파일인 booklist.dat 파일에 저장된 값이다. (이클립스의 경우 프로젝트 최상위 루트에 저장된다.)

열어본 화면

booklist.dat 파일을 텍스트 편집기로 열어본 화면

저장된 파일은 일반 텍스트 편집기로는 몇가지 알 수 있는 글자도 있지만 어떠한 내용이 저장되어 있는지 알 수 없다.

※ 책에 대한 목록을 저장하고 읽어오는 것이 무척이나 간편하고 쉽다.

마샬링하고 싶지 않은 필드에 대한 처리

기본형과 java.io.Serializable 인터페이스를 구현한 객체의 경우 마샬링된다고 했다.
하지만 특별한 경우에는 필드가 마샬링되지 않기를 원할 때도 있다. 예를 들어 보안상 중요한 필드(암호 같은 경우)일 경우에 그렇다.

이 경우에는 자바 키워드인 transient를 사용하면 된다.

위에 했던 예제 중에서 Book.java의 저자에 대한 필드를 마샬링하지 않게 해야 한다면 다음과 같이 필드를 마샬링 하지 않게 해야 한다면 다음과같이 필드의 선언 부분을 수정하면된다.

기존코드 :    private String author;
변경코드 :    transient private String author;

코드를 수정했다면 BookObjectInputStream을 실행한 결과다. 결과를 잘 살펴보면 저자에 대한 부분이 모두 null로 출력된다. 즉, 저자에 대한 부분이 마샬링되지 않고 정보가 전달되었다.

출력결과

출력 결과