출처 : http://hashcode.co.kr/questions/3297/%EC%A7%81%EB%A0%AC%ED%99%94%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B0%9C%EB%85%90-%EC%A7%88%EB%AC%B8%EC%9E%85%EB%8B%88%EB%8B%A4
직렬화의 목적을 올바르게 이해하시면 됩니다.
- 컴퓨터 A에 있는 정보를 컴퓨터 B에 전송하기 위한 것이
직렬화
입니다. 객체
를직렬화
하게 되면 일련의 바이트 스트림으로 변환됩니다.직렬화
된 정보를 복원하는 것을역직렬화
라고 하는 데, 이를 통해서 객체의 원형을 복원할 수 있습니다.
질문에서 언급한대로, 객체에서 getter와 setter를 사용하는 것은 같은 컴퓨터에 있을 경우에는 가능합니다. 따라서 같은 컴퓨터에 사용할 경우에는 직렬화에 대한 고민을 할 필요가 없습니다.
그러나 사용하고 있는 이 객체를 다른 컴퓨터(혹은 종료된 후, 재실행되었을 때)에 전송하여, 그대로 사용하는 것에 대한 고민한다면, 직렬화
가 필요로 합니다.
객체를 어떻게 해야 다른 컴퓨터에 전송하여 그 객체를 그대로 사용할 수 있을까요?
기본적으로 다음과 같은 생각을 해볼 수 있을 겁니다.
- 원본에서 사용하고 있는 프로그램코드(함수, 로직 등)이 그대로 대상컴퓨터에도 있어야할 겁니다.
- 1항이 만족하더라도, 코드만 존재할 뿐 사용하고 있는
객체
가 가지고 있었던 정보는 대상컴퓨터에 없습니다. - 따라서 컴퓨터A에서 존재하였던
객체
가 가진 정보를 컴퓨터B에도 전달해야할 필요가 있습니다.
자바의 Serializable(직렬화)
- 자바는 분산환경(서로 다른 컴퓨터에서 주어진 목적을 위해서 협력하는 환경)에서 분산된 컴퓨터들에서 객체정보를 주고 받을 수 있게 하기 위해서 RMI (Remote Method Invocation, 원격 메서드 호출)을 지원합니다. (일반적으로 RPC - Remote Procedure Call, 원격 프로시저 호출 - 라고 부르며, 다른 언어나 플랫폼에도 개념이 있습니다. )
- 메서드의 경우 메서드 인자로 객체가 전달될 수 있고, 또한 메서드의 반환(return)결과로 객체가 전달될 수 있습니다.
- 그런데 이 함수를 호출하는 쪽은 컴퓨터A이고, 이 메서드가 실행되는 곳은 컴퓨터B인 환경을 지원하는 것이 자바의 RMI입니다.
- RMI를 동작하게 하기위해서는 객체를 네트워크로 전송가능한 형태로 바꿀 필요가 있는 데, 이 때 사용되는 개념이 직렬화/역직렬화입니다.
- 객체를 복원가능하면서 전송이 가능한 상태로 바꾸는 작업이
직렬화
입니다.역직렬화
는 반대로 작업하여 다시 객체로 되게 만드는 것입니다. - 자바는 객체중에 직렬화가 가능한 객체를 표시하기 위해서는 Serializable 인터페이스를 상속하게 하고 있습니다. 이 경우, 객체를 메모리에 생성할 때부터, 직렬화를 염두에 두고 관리합니다.
직렬화
직렬화
는 자바의 Serializable를 사용하지 않더라도, 정보를 대상 시스템에 복원가능하게 전달하기 위해서 다른 형태(텍스트, 바이트, JSON, XML 등)로 변경하는 행위 일체를 말합니다. 자바의 경우,Serializable 인터페이스
를 통해서 편의성을 제공해줍니다.
출처 : https://kin.naver.com/qna/detail.nhn?d1id=1&dirId=1040201&docId=70781469&qb=U2VyaWFsaXphYmxlLCBFeHRlcm5hbGl6YWJsZQ==&enc=utf8§ion=kin&rank=1&search_sort=0&spq=0&pid=TwRoSlpVuE0ssulrKs8ssssssnR-380719&sid=1FEBAQEW3WeV64lYvS6u3A%3D%3D
scjp 공부하고 있는데 처음 문제에서 막히네요 ㅡ.ㅡ
implements Serializable .. 뭐 객체 직렬화하는 것이라는데 뭔 소린지..
객체 직렬화라는게 뭔지 알기 쉽게 좀 설명해주세요..
============================================
객체 직렬화란
객체의 내용을 바이트 단위로 변환하여
파일 또는 네트워크를 통해서
송수신(스트림)이 가능하게 만들어 주는 것을 말합니다.
여기서 객체란 멤버변수의 메모리만으로 구성된 것을 말하구요.
따라서 객체 직렬화에서 메서드와 생성자는 제외된다는 것을 잊지 마세요!
이렇게 객체를 직렬화 함으로써 얻을 수 있는 장점은
객체 자체의 내용을 입출력 형식에 구애 받지 않고
객체를 파일에 저장함으로써
영속성을 제공할 수 있고,
객체 자체를 네트워크를 통하여 손쉽게 교환할 수 있게 됩니다.
객체 직렬화는 자바 1.1 이후에 도입이 되었는데 그 이유는 RMI와 Bean 때문이었습니다.
RMI는 원격객체통신을 지원해야 하기 때문에 객체가 그대로 이동할 수 있어야 합니다.
따라서 이를 지원하기 위해서 객체직렬화가 필수이구요.
그리고 Bean은 설계시 상태에 대한 정보를 지정할때 이 객체직렬화를 사용하면 편하게 객체 상태를 저장할 수 있습니다.
그 외에도 네트워크 프로그램이나 파일 프로그램에서 객체직렬화를 사용하면 코딩도 짦아지고 작업이 수월하게 됩니다.
Serializable 인터페이스와 ObjectInputStream 클래스, ObjectOutputStream클래스
객체 직렬화를 하기 위해서 먼저 객체를 객체직렬화가 가능하도록 Serializable 인터페이스를 구현해야 합니다.
그리고 ObjectInputStream 클래스와 ObjectOutputStream클래스는 객체를 입출력을 하기 위해 사용되는
클래스이다.
이들은 다 java.io 패키지에 정의되어 있는데요.
각각에 대해서 알아보자구요.
1> Serializable 인터페이스
객체 직렬화를 하기 위해서 먼저 객체를 객체직렬화가 가능하도록 Serializable 인터페이스를 구현해야 합니다.
이 인터페이스는 객체직렬화가 제공되어야 함을 자바가상머신에 알려주는 역활을 하구요.
Serializable 인터페이스는 다른 인터페이스와는 달리 구현해야 할 메서드가 없으므로 단지 선언만 해주시면 됩니다.
2> ObjectInputStream 클래스
ObjectInputStream 클래스는 파일에 저장되어 있는 객체 또는 네트워크를 통해
직렬화되어 전달된 객체를 "직렬해제" 하는 기능을 제공해 주고 있다.
한가지 주의할 점은, java.io.Serializable 인터페이스와 java.io.Externalizable 인터페이스를 지원해 주는
객체에 대해서만 가능하다는 것인데,
이는 등록된(즉 Serializable 인터페이스와 Externalizable 인터페이스를 구현한 클래스 객체) 객체를 말한다.
이 때, readObject 메소드를 이용하여 스트림으로부터 직렬화된 객체를 읽을 수 있습니다.
그리고, 이렇게 읽은 객체는 배열, 문자열, 또는 각 객체 등
원래의 형으로 캐스팅 해 주어야 합니다.
3> ObjectOutputStream 클래스
ObjectOutputStream 클래스는
객체들을 출력하는 기능을 제공해 주고, 출력 스트림에 출력하기 전에 직렬화를 수행한다.
ObjectOutputStream 클래스는 자바 기본형 데이터 또는 객체들을 파일에 저장하거나 네트워크를 통해
전달하기 위해 전달할 객체를 직렬화하는 기능을 제공해 준다.
이 클래스도 역시 ObjectInputStream 클래스와
마찬가지로 java.io.Serializable 인터페이스와 java.io.Externalizable 인터페이스를 구현한 객체에 대해서만
가능하구요. 이 때, writeObject 메소드를 이용하여 스트림에 직렬화된 객체를 출력할 수 있습니다.
이해되셨길 바라면서..;
단어 그대로 풀이하면 객체를 순서대로 나열하는 것입니다.
JVM메모리 상에 있는 객체를 다른 PC로 보내고 싶은데,
일반적인 방법으로 보내면, 다른 PC에서 객체를 받아 사용할 수 가 없습니다.
그래서, 객체직렬화 과정을 거처, 객체를 일련의 순서대로 나열하여 보냅니다.
객체를 받는 PC에서는 역직렬화 과정을 거쳐, 직렬화된 객체를 사용할수 있게 됩니다.
Serializble인터페이스는 Mark Interface로 구현 메소드 없이,
단지 객체직렬화가 될수 있다는 표시를 하는 인터페이스 입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | package vo; import java.io.Serializable; public class PersonVo3 implements Serializable { String name; transient int age;//직렬화 제외 String addr; private void PersonVo3() { // TODO 자동 생성된 메소드 스텁 } public PersonVo3(String name, int age, String addr) { super(); this.name = name; this.age = age; this.addr = addr; } @Override public String toString() { // TODO Auto-generated method stub String str = String.format("[%s , %d , %s]", name,age,addr); return str; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | package mymain.serial; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import vo.PersonVo3; public class MyMain_객체직렬화_Array { public static void main(String[] args) throws Exception { /* PersonVo3 [] p_array = { new PersonVo3("일길동", 20, "서울 구로구 구로1동"), new PersonVo3("이길동", 21, "서울 구로구 구로2동"), new PersonVo3("삼길동", 22, "서울 구로구 구로3동") }; //저장(직렬화) : p_array.dat for (int i = 0; i < p_array.length; i++) { //PersonVo3 personVo3 = p_array[i]; System.out.println(p_array[i]); } OutputStream fis = new FileOutputStream("person_list_array.dat"); ObjectOutputStream oos = new ObjectOutputStream(fis); //저장 oos.writeObject(p_array); oos.close(); fis.close(); */ //복원(역직렬화) PersonVo3 [] p_array_list = null; InputStream fis = new FileInputStream("person_list_array.dat"); ObjectInputStream ois = new ObjectInputStream(fis); p_array_list = (PersonVo3 []) ois.readObject(); System.out.println(p_array_list); for (PersonVo3 p : p_array_list) { System.out.println(p); } ois.close(); fis.close(); } } | cs |
출처: http://hyeonstorage.tistory.com/255 [개발이 하고 싶어요]
Externalizable 인터페이스
Externalizable 인터페이스는 Serializable 인터페이스를 확장한 형태로 세부적인 필드를 직접 제어할 수 있는 특징이 있다.
transient 키워드를 사용하여 특정 필드를 제외시키는 것도 가능하지만 좀 더 세부적인 작업이 필요할 때 Externalizable 인터페이스를 사용할 수 있다.
1. Externalizable 인터페이스
Externalizable 인터페이스를 사용하면
객체 스트림을 통하여 객체를 읽고 쓸 때 내부의 내용을 자유롭게 조작하면서 원하는 내용을 출력할 수 있도록 도와준다.
이를 위해서 이 인터페이스는 Serializable 인터페이스를 확장하고 두 가지 메소드를 추가하였다.
하나는 출력을 위한 writeExternal() 메소드이고 또 하나는 입력을 위한 readExternal() 메소드다.
2. 객체 스트림 직렬화시의 동작
(1) 출력 (직렬화)
- 객체 스트림을 통하여 객체를 출력하는 메소드가 호출되면 가장 먼저 그 객체의 클래스가 Externalizable 인터페이스를 구현했는지 확인한다. 만약 구현했다면 writeExternal() 메소드를 통해서 전송 데이터를 직렬화 한다.
- Externalizable 인터페이스를 구현하지 않았다면 Serializable 인터페이스를 구현했는지 확인한다.
Serializable 인터페이스를 구현했다면 그대로 객체 스트림에 객체 전체를 직렬화 한다.
- Externalizable, Serializable 인터페이스를 구현하지 않았다면 Exception이 발생한다.
(2) 입력 (역직렬화)
- 스트림을 통해 전송받은 객체가 Externalizable 인터페이스를 구현했는지 확인한다. 구현했다면 readExternal() 메소드를 통해서 전송받은 데이터를 순서대로 읽어온다.
- Externalizable 인터페이스를 구현하지 않았다면 Serializable 인터페이스를 통해 객체 전체를 역직렬화 한다.
(3) writeExternal(), readExternal()
- Externalizable 인터페이스를 구현할 때는 writeExtenral()과 readExternal() 메소드를 작성해야 한다.
- Serializable 인터페이스는 모든 변수(transient 제외)를 직렬화하는 반면
- Externalizable 인터페이스는 직렬화할 대상을 직접 writeExternal() 메소드에 구현해야 한다.
(transient 변수도 직렬화하여 전송할 수 있다.)
- out.writeObject(name); 과 같이 직렬화할 변수 하나하나 writeObject()에 써줘야 한다.
- 스트림을 통해 객체를 받아 역직렬화를 할 때에도 readExternal() 메소드에서 하나하나 받은 데이터를 변수에 넣어줘야 한다.
- in.readObject(); 를 사용하여, writeExternal() 메소드에서 writeObject()에 넣어 준 변수의 순서대로 데이터를 가져온다.
- 따라서 writeExternal()에 직렬화한 변수의 순서와 readObject() 에서 꺼내는 순서가 일치해야한다.
- 이때 형변환을 해야 한다. ex> id = (String)in.readObject();
- writeExternal(), readExternal()의 인자인 ObjectOupt, ObjectInput 객체는 JVM이 자동으로 인자를 설정하여 매핑해 준다.
3. Externalizable 사용 예제
UserClass.java
import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; // 직렬화 한다. public class UserClass implements Externalizable{ private static final long serialVersionUID = 4220461820168818967L; String name; transient int age;// age 비전송 public UserClass() {} public UserClass(String name, int age){ this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "UserClass [name=" + name + ", age=" + age + "]"; } // Externalizable 구현으로 인한 직렬화 동작 메소드 public void writeExternal(ObjectOutput out) throws IOException{ out.writeObject(this.name); // 이름이 "제이슨" 이면 값을 80으로 직렬화한다. if(this.name.equals("제이슨")){ out.writeObject(80); }else{ out.writeObject(this.age); } } // Externalizable 구현으로 인한 역직렬화 동작 메소드 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException{ this.name = (String)in.readObject(); this.age = (Integer)in.readObject(); } } |
ObjectStream.java
- UserClass 객체는 Externalizable 인터페이스를 구현하여, writeExternal(), readExternal() 메소드를 통해 직렬화, 역 직렬화시 데이터를 제어할 수 있다.
- writeExternal() 에서 데이터를 writeObject()로 넣어준 순서대로 직렬화가 되며, readExternal()에서 readObject()시 순서대로 데이터를 가져온다.
- Serializable 인터페이스 구현시에는 tradient 를 선언한 변수를 제외한 모든변수를 자동으로 직렬화 하여 전송하지만, Externalizable에서는 전송 데이터를 각각 지정해줘야 하며, tradient 선언이 되어 있어도 전송 된다.
- ObjectStream.java 에서 3개의 UserClass 객체를 생성한 후에 파일에 쓰고, 읽어와 프린트 하여 테스트 한다.
'IT > 자바' 카테고리의 다른 글
자바(Java)의 기초 박살내기 - Java GUI③ (0) | 2018.05.24 |
---|---|
JAVA 이스케이프 시퀀스 Escape Sequence - 자바 줄바꿈 역슬래시 큰따옴표 (0) | 2018.05.24 |
File 클래스 (0) | 2018.05.23 |
JAVA, 잡아버려 | 17강! Wrapper클래스 – 기본자료형을 클래스로 변환 (0) | 2018.05.23 |
trhow와 throws로 예외처리를 하는 방법 (0) | 2018.05.22 |