자바객체(힙에 생성된 )를 , 파일로 저장하든 파일에 저장된 자바객체를 다시 읽어서, 힙으로 복구하든.. 아님, 네트워크로 지구 반대면 프로그램쪽으로 보내는 것이 가능한데...
2Kbyte == 2 * 1024 바이트 = 2048바이트 (아무것도 없는 빈 붕어빵(객체)여도)
위의 것을 가능하게 하는 이유는 바로
"객체의 (역)직렬화"가 있기 때문입니다.
객체의 직렬화라는 기능이
===> 바이트열(2048바이트만큼 숫자로 바꿔줌)
역직렬화는 반대로 바이트열을 객체로 반환해준다.
이 기능을 이용하는 보조스트림이
"객체입출력스트림 (ObjectStream/ObjectOutputStream)" 이라고 한다.
------------------------객체를 파일에 넣고 다시 복원해서 출력 -----------------------------
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("C:/Temp/Object.dat");//데이터를 담는 파일
// 보조스트림을 기본스트림에 연결 by using constructor & parameter
ObjectOutputStream oos = new ObjectOutputStream(fos);
// oos.writeObject 메소드가 "객체의 직렬화(Serialization)" 수행
oos.writeObject(new Integer(10)); // 취소선: deprecated
// oos.writeObject(10); // Auto - boxing
oos.writeObject(3.14);
oos.writeObject(new int[] {1,2,3});
oos.writeObject(new String("홍길동"));
oos.flush();
oos.close();
fos.close();
// String 이진수 = Integer.toBinaryString(100);
// String 팔진수 = Integer.toOctalString(100);
// String 십육진수 = Integer.toHexString(100);
// System.out.println(이진수);
// System.out.println(팔진수);
// System.out.println(십육진수);
//
// ---- 객체의 역직렬화
FileInputStream fis = new FileInputStream("C:/Temp/Object.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
Integer obj1 = (Integer) ois.readObject();
Double obj2 = (Double) ois.readObject();
int[] obj3 = (int[]) ois.readObject();
String obj4 = (String) ois.readObject();
ois.close(); fis.close();
System.out.println(obj1);
System.out.println(obj2);
System.out.println(Arrays.toString(obj3));
System.out.println(obj4);
}// main
직렬화 할때 UID 한 번 찍고 역직렬화 할때 UID 한번 찍는다. 이게 일치해야 복원된다
클래스가 수정되면 UID가 변해버린다. 따라서 지정해줘라
-------------------데이터를 만들어낸다 / Streamclassdesc UID --------------------
public class SerialVersionUIDExample1 {
public static void main(String[] args) throws Exception{
FileOutputStream fos = new FileOutputStream("C:/Temp/Object.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
ClassC classC = new ClassC();
classC.field1 =1;
oos.writeObject(classC);
System.out.println("1. classC: " + classC);
oos.flush();
oos.close();
fos.close();
}// main
---------------데이터를 복원하여 읽는다 / local class UID------------
public class SerialVersionUIDExample2 {
public static void main(String[] args) throws Exception{
FileInputStream fis = new FileInputStream("C:/Temp/Object.dat"); //기본스트림생성
ObjectInputStream ois = new ObjectInputStream(fis);
ClassC classC = (ClassC)ois.readObject();
System.out.println("2. classC: " + classC);
}// main
-------------------------------재료1--------------------------------
import java.io.Serializable;
public class ClassA implements Serializable {
int field1;
ClassB field2 =new ClassB();
// 객체의 직렬화에서 제외된다 - 왜? 소속이 clazz 객체(메소드 영역)이기 때문에..
static int field3;
//객체의 직렬화에서 제외시키고 싶은 필드 앞에
// transient 란 키워드 붙이면 된다.
transient int field4;
} // class
------------------------------재료 2--------------------------------
// Serializable 인터페이스를 implements 하고 있는
// 클래스이기 때문에, 이 클래스에서 찍어낸 붕어빵(=객체)는
// 객체의 직렬화 / 역직렬화가 가능
public class ClassB implements Serializable {
int field1;
} // end class
-----------------------------------<핵심 재료>---------------------------
import java.io.Serializable;
import lombok.NoArgsConstructor;
import lombok.ToString;
// Serializable 인터페이스를 implements 하고 있는
// 클래스이기 때문에, 이 클래스에서 찍어낸 붕어빵(=객체)는
// 객체의 직렬화 / 역직렬화가 가능
// Serializable : Tag interface 라고 부른다! (왜?) 멤버가 없다 꼬리표만 붙인다
@ToString
@NoArgsConstructor(access=lombok.AccessLevel.PUBLIC)
public class ClassC implements Serializable {
// Serializable 인터페이스를 implements 하는 클래스는 반드시 아래의 필드를 가져야함
// 만일 개발자가 아래 필드를 선언하지 않으면, 컴파일시 , 자바 컴파일러가 자동으로 넣어줌
// 아주 큰값으로 넣어줌
private static final long serialVersionUID =777L; //반드시 컴파일에 의존하지말고 값을 지정하세요
int field1;
int field2 = 1000;
} // end class
모든 객체가 다 직렬화가 가능하지는 않음
(1) Serializable 인터페이스를 구현한 클래스만 직렬화가능
상속이 되어있으면 올라가올라가면서 다 직렬화시키려고하는데 Non serializable 하다면 어떨까 ?
반대로 자식이 non이고 부모가 serble 하면 어떨까?
=> 자식만이 아니라 부모객체까지 함께 직렬화된다.
=> 자식객체를 직렬화하면 위와 동일하다. / 즉 부모의 Serializable 성질이 자식에게 상속
=> 자식객체를 직렬화하면 자식객체만 직렬화 된다.
=> 오류난다.
자식은 Serializable 할 때
=> 어떻게 했길래, 왜 자식/부모 모두 객체의 직렬화를 가능하게 할까!? ==> 방법을 알자!중요!
*자식 클래스에 아래의 2개의 메소드를 추가하고 이 메소드 안에서,
직렬화/역직렬화가 애시당초 불가능한 필드에 대해서는, InputStream/OutputStream이 제공하는
메소드로 직접 입/출력을 수행하고, 나머지는 serializable한 필드는 JVM이 하듯이 직접 처리하자
1.writeObject() 2.readObject() -> Non Serializable 한 부모의 필드를 같이 Serializable하게 해주는 생성자 같은 느낌
Serializable
private void writeObject(ObjectOutputStream out) throws IOException
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
사용예제!
-----------------------------재료 부모 클래스-----------------------------
package ttt;
import java.io.Serializable;
//Non - serializable 함 : 객체를 직렬화/ 역직렬화 불가능
public class Parent { // POJO 클래스 : 상속도 없는 단독 클래스
public String field1;
}// class
----------------------------<★자식 클래스★>-----------------------------
@ToString
@NoArgsConstructor
public class Child
extends Parent
implements Serializable {
public String field2;
// 1. 출처: 애매모호함! -> 자바 언어 스펙문서에서만 언급된다.
// 목적: 아래의 메소드 내에서, 직렬화 불가능한 부모객체의 필드를 개발자가 직접
// 목적에 맞게 직렬화 할 수 있는 기회 제공
private void writeObject(ObjectOutputStream out) throws IOException {
System.out.println("------- writeObject--------------- ");
out.writeUTF(field1); // 부모에게서 상속받은 직렬화 불가능한 필드를 UTF를 통해 직접 직렬화 수행
// but UTF메소드는 직렬화 메소드가 아니라 단순히 문자열 출력메소드 이다.
//아래의 메소드는 JVM이 원래 수행하는 직렬화 메소드임
out.defaultWriteObject(); // <<여기서 직렬화를 수행한다.
// defaultWriteObject() 메서드는 transient로 선언하지 않은 모든 인스턴스 필드를 직렬화한다.
} // writeObject
// 2. 출처 :애매모호함! -> 자바 언어 스펙문서에서만 언급된다.
// 목적: 아래의 메소드 내에서, 직렬화 불가능한 부모객체의 필드를 개발자가 직접
// 목적에 맞게 역직렬화 할 수 있는 기회 제공
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
System.out.println(" --------------readObject----------- ");
field1 = in.readUTF(); // 부모에게서 상속받은 역직렬화 불가능한 필드를 UTF를 통해 직접 역직렬화 수행
// // but UTF메소드는 역직렬화 메소드가 아니라 단순히 문자열 읽는 메소드 이다.
//아래의 메소드는 JVM이 원래 수행하는 역직렬화 메소드임
in.defaultReadObject(); // <<여기서 역직렬화를 수행한다.
} //
}//class
-----------------------------실행 구동 클래스---------------------------------
public class NonSerializableParentExample {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("C:/Temp/Object.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//자식 객체 생성 -> 잊지말자 !!!: 이 때 부모객체부터 먼저 생성된다!!
Child child = new Child(); //직렬화 대상 객체 생성 및 필드 초기화
child.field1 = "홍길동";
child.field2 = "홍삼원";
System.out.println("child: "+ child);
System.out.println("child: "+ child.field1);
System.out.println("child: "+ child.field2);
//객체를 직렬화 할 때, 직렬화 대상 객체가 부모/자식 상속관계를 가진다면 ,
// 상속관계를 따라서, 부모객체까지 직렬화 대상에 포함시켜 수행된다.
oos.writeObject(child); //객체의 직렬화 수행
oos.flush(); oos.close(); fos.close(); //자원정리
// ----
FileInputStream fis = new FileInputStream("C:/Temp/Object.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
Child child2 = (Child) ois.readObject();
System.out.println("child2: "+ child2);
System.out.println("child2: "+ child2.field1);
System.out.println("child2: "+ child2.field2);
ois.close(); fis.close();
}//main
}//class
마지막으로 하.. 오늘도 정신없이 이해하고 했지만 보람차고 나름 이해도 잘했다. 갈수록 늘어가는 내 손에 깃든 독수리의 사냥본능 어쩌지? 이거? 성장하고있는데? .. 우리조 진짜 열심히한다. 너무 멋져 리스펙 조별과제도 차근차근 조근조근 발표 너무 잘했다. 물론 강사님이 원하는 바에 우리가 하다가 이건 어때 저건 어때 하면서 계속 추가하고 바꿨지만 나는 지금 배우는 사람들이 이런 호기심 저런 호기심 다 써가면서 생각지도 못했던 또는 누구나 생각하지 않을 법한 방법으로 계속 만들고 시행착오도 겪어야지 나중에 어마무시해질거라 믿는다. 왜냐하면 지금이 실수하기 제일 좋은 때니까! 배울 수 있는 유일한 길은 실수를 통해서다!