티스토리 뷰

1.    객체 입출력 보조 스트림

-      객체를 파일 또는 네트워크로/출력할 수 있는 기능을 제공한다.

-      객체 직렬화 : 객체는 문자가 아니므로 바이트 기반 스트림으로 데이터를 변경할 필요가 있다. ( 객체를 바이트로 만드는 것을 직렬화, 바이트를 객체로 만드는 것을 역직렬화라고 한다. )

-      해당 보조 스트림 : ObjectInputStream / ObjectOutputStream

-      직렬화 가능한 클래스 ( Serializable ) :

-      자바에서는 Serializable 인터페이스를 구현한 클래스만 직렬화 할 수 있도록 제한

-      , transient 필드는 제외한다. ( transient(일시적인) 필드는 직렬화에서 제외가 된다. )

-      Transient 필드는 직렬화로 보내지 않았기에, 역직렬화 할 필요도 없다.

-      객체 직렬화를 할 때, private 필드를 포함한 모든 필드를 바이트로 변환할 수 있다.

 

2.    serialVersionUID 필드 ( p. 1047 )

-      직렬화된 객체를 역직렬화 할 때는 직렬화 했을 때에 같은 클래스를 사용해야 한다.

-      클래스의 이름이 같더라도 클래스의 내용이 변경된 경우, 역직렬화에 실패한다.

-      모든 객체는 클래스를 통해서 생성되기 때문에, 동일한 클래스를 가지고 있지 않다면 객체를 역직렬화 할 수 없다.

-      serialVersionUID :

-      서로 같은 클래스임을 알려주는 식별자 역할을 해준다.

-      서로 필드가 다르다 하더라도 serialVersionUID가 서로 같으면 같은 클래스로 간주

-      Serializable 인터페이스 구현하면, 컴파일 시 자동적으로 serialVersionUID 정적 필드를 추가해 준다.

-      재컴파일하면, serialVersionUID의 값이 변경된다.

-      불가피한 수정이 있을 경우에는 명시적으로 serialVaersionUID를 선언하면 된다.

-      Ex. static final long serialVersionUID = 정수값;

 

3.    writeObject( )readObject( ) 메소드

-      1) writeObject( ObjectOutputStream out ) 메소드 :

-      직렬화 직전에 자동 호출되며, 추가 직렬화 할 내용을 작성할 수 있다.

-      2) readObject( ObjectInputStream in ) 메소드 :

-      역직렬화 직전에 자동으로 호출되며, 추가적으로 역직렬화 내용을 작성할 수 있다.

-      3) 추가 직렬화 및 역직렬화가 필요한 경우 :

-      부모 클래스가 Serializableimplements 하지 않고, 자식 클래스가 Serializable을 구현했을 때 필요하게 된다.

 

4.    네트워크

-      네트워크 ( Network ) : 여러 대의 컴퓨터를 통신 회선으로 연결된 망

-       + 홈 네트워크 : 컴퓨터가 방마다 있고, 이들 컴퓨터를 유/무선 등의 통신 회선으로 연결

-       + 지역 네트워크 ( LAN : Local Area Network ) : 회사, 건물, 특정 영역에 존재하는 컴퓨터를 통신회선으로 연결

-       + 인터넷 ( 광역 네트워크 : WAN ( Wild Area Network ) ) : 지역 네트워크를 통신 회선으로 연결한 것

 

5.    서버와 클라이언트

-      서버와 클라이언트는 모두 프로그램을 의미하지만, 서로의 역할이 다르다.

-      클라이언트 : 처리 요청을 보내는 프로그램 ( 서비스를 받는 프로그램 )

-      네트워크 데이터를 필요로 하는 모든 애플리케이션이 클라이언트에 해당된다.

-      서버 : 클라이언트의 요청을 처리하고, 그 처리결과를 다시 요청을 보낸 클라이언트에게 보내주는 프로그램 ( 서비스를 제공하는 프로그램 )

-      순서 :

-      클라이언트의 연결요청 - > 서버가 연결을 수락 -> 클라이언트가 처리 요청을 보냄 - > 서버가 처리요청을 처리 - > 서버가 처리결과(응답)를 클라이언트로 보냄

-      종류 :

-      (1) 클라이언트( 고객 ) – 서버 ( 서비스를 제공하는 자 ) 모델 : C / S 모델

-      (2) P2P ( Peer – To – Peer ) : ex. Torrent

 

6.    IP주소와 포트 ( port )

-      IP주소 ( Internet Protocol ) :

-      네트워크 상에서 컴퓨터를 식별하는 번호로 네트워크 어뎁터(Lan 카드)마다 할당

-      IP주소 확인 방법 : 명령 프롬프트를 사용 - > c:\>ipconfig /all ( xxx.xxx.xxx.xxx 형식으로 표현된다. )

-      포트 ( Port : 항구 ) :

-      같은 컴퓨터 내에서 프로그램을 식별하는 번호

-      클라이언트는 서버 연결 요청 시 IP주소와 Port번호를 같이 제공한다.

-      0 – 65535 범위의 값을 가진다.

-      명령 프롬프트에 nc -L -p 포트번호 작성하여 활용할 수도 있다.

-      telnet 서버 IP주소 서버포트번호

-      포트의 범위 :

-      1 ) Well Know Port Numbers

-      2 ) Registered Port Numbers

-      3 ) Dynamic Or Private Port Numbers


더보기

01. 객체 입출력 보조 스트림 - 직렬화

package objectinput_objectoutput_stream;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class ObjectInputOutputStreamExample {
	
	public static void main (String[] args) throws Exception { // 직렬화 (***)
		
//		===============================================================================================
		
		// 1. 지정된 파일에 객체를 저장하기 위한 바이트 기반의 파일출력 스트림 생성
		FileOutputStream fos = new FileOutputStream("C:/Temp/Object.dat");
		
//		===============================================================================================
		
		// 2. 파일출력스트림에 객체출력보조스트림 연결 ( 생성자 매개변수로 )
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		
//		===============================================================================================
		
		// 3. 다양한 참조차입의 객체를 출력 ( 이때, 내부적으로 객체의 " 직렬화 "가 발생 ) (***)
		// 조건 : 출력할 객체를 찍어낸 클래스는 기본적으로 Serializable 태그 인터페이스를 반드시 implements 해야 한다!!(****)
		
		oos.writeObject(new Integer(10)); // Integer 객체 저장
		oos.writeObject(3.14); // double 객체 저장
		oos.writeObject(new int [] { 1,2,3}); // 배열 객체를 저장
		oos.writeObject(new String ("홍길동")); // 문자열 객체를 저장
		
		// Wrapper 타입, String, 배열은 모두 Serializable 태그 인터페이스를 implements하고 있다.(***)
		// 이를 통해서 직렬화 및 역직렬화가 가능하다.
		
//		===============================================================================================
		
		ClassC cobj = new ClassC();
		oos.writeObject(cobj);
		
		// Serializable를 implements 할 때에는 오류가 발생하지 않지만, Serializable를 지워 버리면 예외가 발생한다.
		
//		===============================================================================================
		
		// 4. 출력스트림은 기본 출력버퍼를 가지고 있으니, 강제 flush 수행!!
		oos.flush();
		
//		===============================================================================================
		
		// 5. 자원해제 : 보조스트림부터 먼저 닫고, 다음으로 기반 스트림을 닫는다!! ( 순서 )
		oos.close();
		fos.close();
		
		
	} // main

} // end class

더보기

02. 객체 입출력 보조 스트림 - 역직렬화

package objectinput_objectoutput_stream;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Arrays;

public class ObjectInputOutputStreamExample2 {
	
	public static void main (String[] args) throws Exception { // 역직렬화 (***)
		
//		===============================================================================================
		
		// 1. 여러 참조 타입 객체를 저장한 파일에 대한 바이트 기반의 파일입력스트림 생성
		FileInputStream fis = new FileInputStream("C:/Temp/Object.dat");
		
//		===============================================================================================
		
		// 2. 객체 입력보조스트림 생성 ( by the argument of the constructor )
		ObjectInputStream ois = new ObjectInputStream(fis);
		
//		===============================================================================================
		
		// 3. 객체입력보고스트림의 readObject() 메소드로,
		// 파일에 쓴 순서대로 다시 객체를 읽어 들인다.
		// 이때 내부적으로 파일에 저장된 객체의 " 역직렬화 " 수행한다.
		
		// 조건 : 객체를 읽어들이는 JVM메소드 영역에, 해당 참조타입의 클래스(clazz 객체)가 있어야 하고,
		// 파일에 객체를 저장할 때 사용된 클래스와 
		// 현재 파일로부터 객체를 읽어들일 때 사용하는 클래스( 즉, 양쪽 시점의 클래스가 )가 같아야 한다.
		
		Integer obj1 = (Integer) ois.readObject();
		Double obj2 = (Double) ois.readObject();
		int[] obj3 = (int [] ) ois.readObject();
		String obj4 = (String) ois.readObject();
		
//		===============================================================================================
		
		// 4. 자원 해제 : 보조스트림을 먼저 닫고, 그 후에 기본 스트림을 닫는다.
		ois.close();
		fis.close();
		
//		===============================================================================================
		
		// 5. 제대로 역직렬화를 통해, 파일로부터 객체가 잘 복원되었는지 확인하기 위해 출력한다.
		System.out.println(obj1);
		System.out.println(obj2);
		System.out.println(Arrays.toString(obj3));
		System.out.println(obj4);
		
//		===============================================================================================
		
		// 출력 :
		
//		10
//		3.14
//		[1, 2, 3]
//		홍길동
		
		
	} // main

} // end class

더보기

03. Serializable의 효과

package objectinput_objectoutput_stream;

import java.io.Serializable;

import lombok.NoArgsConstructor;


@NoArgsConstructor
public class ClassC implements Serializable { // Serializable의 효과 / serialVersionUID 생성 (***)
	
	// 직렬화가 필요있는 클래스를 선언할 때에는 자바컴파일러에게 자동추가를 맡기지 않고,
	// 개발자가 직접 필드와 초기값을 지정해야지 유연성을 이용할 수 있다. (***)
	
	private static final long serialVersionUID = 7777777L; // 디폴트 값으로 serialVersionUID을 생성했을 때
	
//	private static final long serialVersionUID = ??? 
	// 컴파일할 때마다, 무작위 정수값으로 serialVersionUID를 생성한다.
	
	// serialVersionUID을 넣어 주지 않으면 컴파일할 때마다 계속 serialVersionUID가 바뀌게 된다.
	
	int field1;
	
	// 객체의 직렬화 이후에, 새로운 필드 추가 -> 컴파일 -> serialVersionUID 변경 ( 무작위 )
	int field2;
	
	// serialVersionUID 변경되자, java.io.InvalidClassException 예외가 발생하게 된다.
	// 그렇기 때문에 serialVersionUID를 직접 지정해야 한다. (***)

} // end class
package objectinput_objectoutput_stream;

import java.io.Serializable;

import lombok.NoArgsConstructor;


@NoArgsConstructor
public class ClassB implements Serializable {
	
	int field1;

} // end class
package objectinput_objectoutput_stream;

import java.io.Serializable;

import lombok.NoArgsConstructor;
import lombok.ToString;


@ToString
@NoArgsConstructor
public class ClassA implements Serializable { // 직렬화에서 제외되는 존재 / serialVersionUID의 필요성
	
	int field1;
	
	// 집합 ( 부품 )관계 필드
	// 이 클래스 역시 Serializable 한다.
	ClassB field2 = new ClassB();
	
	// 정적필드도 직렬화에 포함되지 않는다. (***)
	static int field3 = 1000;
	
	// 직렬화에서 제외할 필드는 transient 키워드를 붙이면 된다. (****)
	transient int field4;

} // end class

더보기

04. 역직렬화가 불가능한 존재

package objectinput_objectoutput_stream;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class Serializable_Writer {
	
	public static void main (String [] args) throws Exception {
		
		// 1. 파일에 댜한 출력 스트림 생성
		FileOutputStream fos = new FileOutputStream("C:/Temp/Object.dat");
		
//		===================================================================
		
		// 2. 객체를 파일에 출력하기 위한, 객체 출력 보조 스트림 생성
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		
//		===================================================================
		
		// 3. 직렬화할 객체를 생성학고, 필드값을 초기화
		ClassA classA = new ClassA();
		classA.field1 = 1;
		classA.field2.field1 = 2;
		
//		===================================================================
		
		// 직렬화에서 제외된다. ( 직렬화된 값에 저장되어 있지 않다. )
		classA.field3 = 3; // 정적멤버는 정적 멤버답게 사용하는 것이 좋다.
		classA.field4 = 4;
		
		// 위의 두 필드의 값이 초기값인 1으로 출력된다. Why?
		
		System.out.println(classA);
//		출력 : ClassA(field1=1, field2=objectinput_objectoutput_stream.ClassB@27716f4, field4=4)
		
		// 이를 통해서 정적 필드인 field3은 출력이 안되고 있음을 알 수 있는데, 이를 통해 다른 곳에 저장되어있음을 알 수 있다.
		// 그렇기에 이 파일에서 정적필드의 값을 바꿔도 역직렬화에 영향을 주지 않는다. (****)
		// 즉, 정적멤버는 정적멤버로 사용하면 된다.
		
//		===================================================================
		
		// 4. 파일에 객체를 출력 ( 직렬화 발생 )
		oos.writeObject(classA);
		
//		===================================================================
		
		// 5. 자원 정리
		oos.flush();
		
		oos.close();
		fos.close();
		
//		===================================================================
		
		
	} // main

} // end class
public class Serializable_Reader {
	
	public static void main (String [] args) throws Exception {
		
		System.out.println(">>>> ClassA.field3 : " + ClassA.field3);
		// static 필드이기에 역직렬화와는 전혀 상관 없이 값이 출력된다.
		// 출력 : >>>> ClassA.field3 : 1000
		
		// 1. 객체가 저장된 파일에 대한 입력스트림 생성
		FileInputStream fis = new FileInputStream("C:/Temp/Object.dat");
		
		// 2. 객체입력 보조스트림 생성
		ObjectInputStream ois = new ObjectInputStream(fis);
		
		// 3. 보조스트림을 통해, 파일에 저장된 객체의 복원 ( 이때, " 역직렬화 "가 수행된다. )
		ClassA v = (ClassA) ois.readObject();
		
		// 4. 과연 복원된 객체의 다양한 유형의
		System.out.println("field1 : " + v.field1);
		System.out.println("field2.filed1 : " + v.field2.field1);
		
		// 정적멤버 / transient 필드는 직렬화 / 역직렬화가 불가능하기에 값 복원이 불가능 하다.
		System.out.println("field3 : " + v.field3); // 정적 필드 - 정적멤버답게 해줘야 한다.
		System.out.println("filed4 : " + v.field4); // transient 필드
		
		// 출력 :
//		>>>> ClassA.field3 : 1000
//		field1 : 1
//		field2.filed1 : 2
//		field3 : 1000
//		filed4 : 0
		
	} // main

} // end class

더보기

05. SerialVersionUID

package objectinput_objectoutput_stream;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class SerialVersionUIDExample {
	
	public static void main ( String [] args ) throws Exception {
		
		// 1. 파일에 출력할 출력 스트림 생성
		FileOutputStream fos = new FileOutputStream("C:/Temp/Object.dat");
		
		// 2. 객체 출력 보조스트림 생성
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		
		// 3. 직렬화할 객체 생성 ( Serializable 함 )
		ClassC classC = new ClassC();
		classC.field1 = 1;
		
		// 4. 객체 출력
		oos.writeObject(classC);
		
		// 5. 자원 해제
		oos.flush();
		
		oos.close();
		fos.close();
		
	} // main

} // end class
package objectinput_objectoutput_stream;

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class SerialVersionUIDExample2 {
	
	public static void main ( String [] args ) throws Exception {
		
		// 1. 파일에 출력할 출력 스트림 생성
		FileInputStream fis = new FileInputStream("C:/Temp/Object.dat");
		
		// 2. 입력 스트림에 객체 입력 보조스트림 연결
		ObjectInputStream ois = new ObjectInputStream(fis);
		
		// 3. 객체 복원 ( 역직렬화 수행 )
		ClassC classC = (ClassC) ois.readObject();
		System.out.println("field1 : " + classC.field1); // 출력 : field1 : 1
		System.out.println("field2 : " + classC.field2); // 출력 : field2 : 0
		
		// 이를 통해서 직렬화 이후에 새롭게 작성한 필드도 serialVersionUID를 지정해주자 사용이 가능해졌다. (***)
		// 직렬화 이후에 있던 필드를 삭제하게 된다면, 삭제한 필드는 사용할 수 없고 그 외의 필드는 사용할 수 있다.
		// 그렇지만 serialVersionUID는 같기 때문에 예외가 발생하지는 않는다.
		
	} // main

} // end class

더보기

06. 부모 자식관계에서의 Serializable

package objectinput_objectoutput_stream;

public class Parent { // Serializable 하지 않는다
	
	public String field1; // Serializable하지 않기에, 직렬화에서 제외된다.
 
} // end class
package objectinput_objectoutput_stream;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Child extends Parent implements Serializable { // 부모 : Serialzable X , 자식 : Serializable O
	
//	상속과 객체의 (역)직렬화의 관계 : (****)
//	(1) 만약 부모 클래스가 Serializable 하면, 자식 클래스가 Not - Serializable 해도 직렬화 및 역직렬화가 가능하다.
//	(1) 만약 부모 클래스가 Serializable하지 않고, 자식 클래스만 Serializable하면 직렬화 및 역직렬화가 불가능하다!
	
	public String field2;
	
	// 객체의 직렬화 수행 시, 아래의 메소드를 재정의하면 이 메소드가 대신 호출되게 된다. (***)
	private void writeObject(ObjectOutputStream out) throws IOException{
		
		System.out.println("Child::writeObject(out) invoked.");
		
		out.writeUTF(field1); // 부모로부터 상속 받은 필드의 값을 직접 출력시킨다. ( 직렬하는 것이 아니라, 단순히 출력하는 것 )
		out.defaultWriteObject(); // 직렬화를 시켜준다. (****)
		
	} // writeObject
	
	// 객체의 역직렬화 수행 시, 아래의 메소드를 재정의하면 이 메소드가 대신 호출되게 된다. (***)
	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
		
		System.out.println("Child::readObject(in) invoked.");
		
		field1 = in.readUTF(); // 부모로부터 상속받은 field1를 읽어 들인다는 의미이다. ( 역직렬하는 것이 아니라, 출력된 것을 읽는 것 )
		in.defaultReadObject(); // 역직렬화를 해준다. (****)
		
	} // readObject
	
	// 메소드 writeObject와 readObject는 오버 라이딩한 것이 아니다!!!

} // end class
728x90
댓글
«   2024/09   »
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
최근에 올라온 글
Total
Today
Yesterday
공지사항