Notice
Recent Posts
Recent Comments
Link
«   2026/04   »
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
Tags
more
Archives
Today
Total
관리 메뉴

일상

3, 4주차 - Java 객체 지향 프로그래밍 본문

교육

3, 4주차 - Java 객체 지향 프로그래밍

콜리/khgeung 2025. 5. 17. 17:59

1. Collection (자료구조)

  • 자바 컬렉션 계열의 최상위 인터페이스
  • Generic : 요소의 타입을 미리 지정한다. 

1-1. Map

  • key와 value 쌍으로 저장하며, key로 요소들을 제어하는 자료 구조
  • 요소 검색에 좋다.
    • TreeMap : 정렬
    • LinkedHashMap : 입력 순서 보장
      • map.put(1, "삼") : key 1, value "삼" 넣기 (만약 이미 존재하는 key라면 업데이트된다.)
      • map.size() : map의 크기 반환
      • map.get(1) : key가 1인 항목의 value 반환
      • map.containsKey(4) : key(4)가 존재하면 true, 아니면 false
      • map.remove(1) : key(1)이 존재하면 삭제 후 value 값 리턴, 없으면 null 반환

1-2. 반복자 설계 패턴 (Itrator Design Pattern)

  • 각기 다른 구현 방식의 자료 구조체의 요소에 대해 접근, 제어할 수 있는 표준화된 방식(인터페이스)을 제공하는 디자인 패턴 
  • 어떤 자료 구조가 와도 반복 및 열거할 수 있다. (인터페이스의 표준화 예시)
  • 동일한 방식 : hashNext(), next()으로 다양한 자료구조체의 요소들을 반복, 열거할 수 있다.
  • 형식 : Iterator<타입> 참조변수(일반적으로 iterator) = 분석할자료.iterator(); 
    • iterator.hashNext() : 다음 요소가 있으면 true, 없으면 false 반환
    • iterator.next() : 다음 요소를 반환
package step1;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;

//java.util.Iterator interface : 반복자
public class TestIterator1 {
	public static void main(String[] args) {
	//LinkedHashSet : 중복 인정 x, 입력 순서 보장
	LinkedHashSet<String> set = new LinkedHashSet<>();
	set.add("아이유"); 
	set.add("장기하");
	set.add("아이유");
	set.add("이강인");
	//System.out.println(set);
	//Iterator 을 이용해 요소들을 반복 열거한다.
	Iterator<String> iterator = set.iterator();
	while(iterator.hasNext()) //hasNext() : 다음 요소가 있으면 true, 없으면 false
		System.out.println(iterator.next()); // next() 요소를 반환
	System.out.println("*************************");
	ArrayList<String> list = new ArrayList<String>();
	list.add("당근");
	list.add("오이");
	list.add("상추");
	Iterator<String> it2 = list.iterator();
	while(it2.hasNext()) //다음 요소가 있으면 true, 없으면 false
		System.out.println(it2.next()); //다음 요소를 반환받아 출력한다.
	}
}

1-3. Stack

  • LIFO(Last In First Out) : 마지막에 추가된 요소가 가장 먼저 나온다.
  • 스택 영역의 지역 변수들은 메소드가 끝나면 알아서 꼬리부터 사라진다. (반대로, heap 영역에 저장된 객체 등은 Garbage Collector 이 처리한다.)
    • stack.add() : 항목 추가
    • stack.pop() : 항목 삭제 후 삭제한 항목 반환
    • stack.empty() : 스택이 비어있으면 true, 항목이 있을 시 false 반환 (isEmpty() 와 같은 용법)

1-4. Queue

  • FIFO(First In First Out) : 먼저 추가된 요소가 가장 먼저 나온다. ex) 메시지 큐
    • queue.isEmpty() : 큐가 비어있으면 true 반환, 항목이 있으면 false 반환
    • queue.poll() : pop() 과 같은 역할로 해당 항목을 삭제 후 삭제한 해당 항목을 반환 

2. Exception Handling

2-1. 예외 처리 (Exception Handling)

  • 프로그램 실행 중에 발생하는 예외에 대한 대처 방안을 수행하는 것을 목적으로 한다.
  • ex) 몸에서 = try
    • 통증 = Exception 이 발생 =throw 되면 뇌로 전파하고 = throws
    • 병원, 약 또는 운동과 같은 대처 방안을 실행하고 = catch
    • 계속 공부하고 논다 = 정상 수행 내용
    • 통증과 관계 없이 잘 휴식하고 일한다. = finally
  • 특징 : JVM이 프로그램을 실행하다가 예외 사항을 만나면 Exception 을 발생시키고 즉시 비정상 종료를 한다.
    • 비정상 종료 상황을 try & catch 구문으로 Exception 발생에 대한 대처 방안을 실행하고 정상 실행 되도록 한다.
    • try & catch 를 이용해서 예외 발생 시 대처 방안을 실행하고 정상 수행 시킨다.

2-1-1. try & catch

  • try : 예외 발생 시 예상 지점을 영역화한다.
  • catch : 예외 발생 시 대처 방안을 실행하는 영역이다. 
    • 여러 개 정의할 수 있다.
    • 하나의 try 블럭에서 여러 예외가 발생할 수 있으므로 구체적인 예외 처리에서 추상적인 exception 처리 순으로 정의한다. 
      • 실무에서는 최대한 구체적으로 exception에 대한 대처 방안 (catch) 을 정의하는 것을 권장한다.
    • e.printStackTrace() : exception 메시지와 발생 경로를 보여준다.

2-1-2. finally

  • 항상 실행 되는 공간이다.

2-1-3. throw & throws

  • throws : Exception을 전파한다. 예외를 호출한 곳으로 전파하며, 생성자와 메소드 부분에 기술한다. 
    • CheckedException은 throws 하지 않으면 try & catch 를 작성해야 한다.
  • throw : 예외를 고의로 발생시킨다. 
    • if & else 문으로 해결할 수는 없을까? : return값도 정의해야 하며 정상/비정상이 섞여서 개발에 들어가고, 코드가 너무 복잡해진다. => try & catch 문으로 작성하면 코드가 깔끔해지고 응집도가 높아진다.

2-1-4. UncheckedException & CheckedException 

  UncheckedException CheckedException
의미 Runtime 계열 예외로, 실행 시에 발생하는 예외를 말한다. RuntimeException 계열이 아닌 모든 예외를 말한다.
방법 - 별도의 throws가 필요 없다. (명시할 필요 없음)
- try & catch 하지 않아도 컴파일된다.
- 컴파일 시점에 예외 처리에 대한 정보를 제공한다. 
- 컴파일시에 체크되어 throws 또는 try & catch 해야 한다.

3. IO (입출력)

3-1. IO

  • Input과 Output : 즉 IO는 입력과 출력을 말한다.
  • Stream : 입출력을 위한 장치 

3-2. 4가지 IO Abstract class 

범주 ByteStream (1 byte, 8 bit) CharacterStream (2 byte, 16bit)
입력 InputStream Reader
출력 OutputStream Writer
  • Scanner scanner = new Scanner(System.in);      ...      scanner.close();
    • System.in : System 클래스의 static field 인 InputStream 타입의 in 변수
    • System.out.println() : System 클래스의 static field 인 OutputStream 자식인 PrintStream 타입의 out 변수
    • close() : 모든 입출력 장치인 Stream은 Closeable interface를 implements하고 있고, Closeable interface의 abstract method 인 close() 를 구현해 두고 있다. 
      • 입출력을 위해 생성한 Stream 은 반드시 close 하여 Stream 을 닫아주고 사용한 자원의 제어권을 놓아주도록 해야 한다. 
    • scanner.next() : 공백 전까지 입력된 문자열을 처리한다. (한 단어에 적합)
    • scanner.nextLine() : enter 전까지 입력된 문자열을 처리한다.
  • 추가) label (레이블) : 특정 위치를 벗어나게 한다. ex) exit : while(true) { break exit; }

3-3. File

  • DirInfo 인터페이스 
    • 파일의 생성 및 검색 등이 이루어질 파일 경로를 가지는 인터페이스이다.
package common;

import java.io.File;

public interface DirInfo {
	//java interface 는 아래와 같이 명시하면 자동으로 public static final 상수로 인식된다.
	//여러 운영체제에서 사용하기 위해 역슬래시를 File.separator로 변경해준다.
 String OUTPUT_DIR = "C:"+File.separator+"Users"+File.separator+"KOSA"+File.separator+"Desktop"+File.separator+"kim"+File.separator+"Projects"+File.separator+"iotest"+File.separator;
}
  • File file = new File(dirPath) : 경로인 dirPath를 가지고 file 객체를 생성한다. 즉, 파일 객체를 생성해서 이 객체를 실제 파일로 만들거나, 파일의 존재 여부 및 삭제 등의 작업을 하기 위해 선언해주는 것! (코드 상에서 파일을 다루기 위함이다.)
    • dir.exists() : 폴더(=디렉토리)가 존재하면 true, 아니면 false 를 반환한다.
    • dir.mkdirs() : 디렉토리를 생성한다. 하위 디렉토리까지 모두 생성하며, 생성되면 true, 아니면 false 를 반환한다.
      • mkdir() : 디렉토리 하나만 생성한다.
    • dir.getPath() : 경로를 반환한다.
    • file.createNewFile() : 파일을 디렉토리에 생성한다.
    • file.delete() : 파일을 삭제한다.
    • file.renameTo(목적지 파일 객체) : file을 목적지로 지정된 파일 객체로 실제 파일을 이동한다.
    • file.getParentFile() : 파일의 상위 디렉토리를 객체로 반환받는다.
    • file.isFile() : 파일의 존재 여부를 확인한다.
package step2;

import java.io.File;
import java.io.IOException;

import common.DirInfo;

// java.io.File class의 기능을 테스트하는 예제
public class TestFile1 {
	public static void main(String[] args) {
		String dirPath = DirInfo.OUTPUT_DIR+"fileExam";
		System.out.println(dirPath);
		
		File dir = new File(dirPath); //경로가 적힌 문자열을 객체로 담아서 처리하기 위해 넣는다.
		System.out.println("존재유무:"+dir.exists()); // 폴더(디렉토리) 존재하면 true, 아니면 false
		if(dir.exists() == false) // 존재하지 않으면
			System.out.println("디렉토리 생성:"+dir.mkdirs()); //디렉토리를 생성한다. 생성되면 true, 아니면 false (mkdir() : 디렉토리 하나만 / mkdirs() : 하위 디렉토리까지)
		
		System.out.println(dir.getPath()); // 경로를 반환한다.
		//파일을 생성해본다.
		//정상 흐름 테스트
		//File file = new File(dir.getPath()+File.separator+"note1.txt");
		//예외 흐름 테스트 : 잘못된 경로를 입력시킨다. => IOExceptio이 전달되어 catch 구문이 실행된다.
		File file = new File("우하하"+dir.getPath()+File.separator+"note1.txt");
		//System.out.println(dirPath);
		try {
			boolean result = file.createNewFile();
			System.out.println(file.getName()+"파일 생성:"+result); // 새로 생성 true, 기존 파일이 존재하여 만들지 않을 떄는 false 반환
		} catch (IOException e) {
			//아래는 예외 발생 메시지와 경로를 보여준다.
			e.printStackTrace();
		}
	}

}
  • 디렉토리 내 파일 및 폴더 확인하기
package step2;

import java.io.File;

import common.DirInfo;

//
public class TestFile5 {
	public static void main(String[] args) {
		File destFile = new File(DirInfo.OUTPUT_DIR + "fileExam" + File.separator + "memo7.txt");
//		// destFile 과 같은 위치에 movie라는 폴더(또는 디렉토리)를 만들고 싶다.
//		// destFile.getParent() : String ==> java.io.File class의 mkdirs()메소드를 사용하기 위해서는 반환되는 경로 문자열을 이용해 File 객체를 또다시 만들어야 한다.
//		// getParentFile() : File ==> 위와 달리 파일 객체로 반환하므로 바로 File class 메소드를 사용할 수 있다.
//		File parentDir = destFile.getParentFile();
//		//디렉토리 생성
//		File dir = new File(parentDir.getPath()+ File.separator+"movie");
//		System.out.println("movie 폴더 생성: "+dir.mkdirs());

		// 위의 destFile 과 같은 위치에 있는 파일 또는 디렉토리의 정보를 알고자 한다.
		// loop 반복문을 이용해 파일과 폴더의 모든 이름을 출력하고 싶다
		// File의 getParentFile()과 list() : String [] 을 적절히 이용해서 for loop를 이용해 파일과 폴더명을
		// 모두 출력해본다.
		String fa[] = destFile.getParentFile().list();
		for (int i = 0; i < fa.length; i++) {
			System.out.println(fa[i]);
		}
		System.out.println("==============================");
		// listFiles() : File [] 를 이용해 디렉토리 내에 있는 정보가 파일(isFile())이면 file:파일명
		// 디렉토리이면 directory:디렉토리명이 출력되도록 for loop를 구현해본다.
		// isDirectory()
		File farray[] = destFile.getParentFile().listFiles();
		for (int i = 0; i < farray.length; i++) {
			if (farray[i].isDirectory()) {
				System.out.println("directory: " + farray[i].getName());
			}
			else if (farray[i].isFile()) {
				System.out.println("file: " + farray[i].getName());
			} 
		}//for
		System.out.println("==============================");
		for (File file:farray) {
			if(file.isDirectory())
				System.out.println("directory: "+file.getName());
			else if (file.isFile()){
				System.out.println("file: "+file.getName());
			}
		}
	}//main
}//class

3-4. NodeStream 계열과 ProcessingStream 계열

  NodeStream ProcessingStream
의미 필수적인 스트림이며, 직접 장치에 연결된다. 부가적인 스트림이며 반드시 NodeStream 을 필요로 한다. 다양한 기능을 위해 존재한다.
FileReader, FileOutputStream BufferedReader, PrintWriter
  • FileWriter과 PrintWriter을 이용한 선수 명단 txt 파일 생성 예제
package step6;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import common.DirInfo;
//간단한 문자열 입력스트림 테스트를 위한 예제 - main 에서 모두 작성해본다.
// 입력 시 while loop를 이용해 선수명단을 입력받아 출력한다 readLine() 이용 => 변경 ready()를 이용
public class TestReader3 {
	public static void main(String[] args) {
		String path = DirInfo.OUTPUT_DIR+"선수명단.txt";
		try {
			// 파일에 연결되는 입력 스트림 (NodeStream)
			FileReader fr = new FileReader(path);
			// 버퍼 입력 기능을 제공하는 입력 스트림 ( ProcessingStream )
			BufferedReader br = new BufferedReader(fr); // composition relationship 
			
			//while 반복문을 이용해 한번에 선수 명단을 모두 출력한다.
			//ready() 을 이용, 더 이상 입력받을 내용이 없으면 false
			
			while(br.ready())
				System.out.println(br.readLine());
			br.close();
			
			
		} catch (FileNotFoundException e) {
		
			e.printStackTrace();
			
		} catch (IOException e) {

			e.printStackTrace();
		}
	}

}
  • FileReader 과 BufferedReader을 이용한 선수 명단 txt 파일의 내용을 읽어오는 예제
    • Buffer : 완충 역할을 담당한다. 많은 데이터들을 일일이 전달하면 오래 걸리고 오류 발생 가능성이 높아지므로, Buffer 라는 바구니에 담아 데이터를 전달하여 시스템 부하를 줄이고 성능을 향상시킨다.
package step6;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import common.DirInfo;
//간단한 문자열 입력스트림 테스트를 위한 예제 - main 에서 모두 작성해본다.
// 입력 시 while loop를 이용해 선수명단을 입력받아 출력한다 readLine() 이용
public class TestReader2 {
	public static void main(String[] args) {
		String path = DirInfo.OUTPUT_DIR+"선수명단.txt";
		try {
			// 파일에 연결되는 입력 스트림 (NodeStream)
			FileReader fr = new FileReader(path);
			// 버퍼 입력 기능을 제공하는 입력 스트림 ( ProcessingStream )
			BufferedReader br = new BufferedReader(fr); // composition relationship 
			
			//while 반복문을 이용해 한번에 선수 명단을 모두 출력한다.
			//readLine() 을 이용, 더 이상 입력받을 내용이 없으면 null
			
			// //내 코드
//			while(true) {
//				String s = br.readLine();
//				if (s == null){
//					br.close();
//					break;
//				}
//				System.out.println(s);
//			}
			
			String data = br.readLine();
			while(data != null) {
				System.out.println(data);
				data = br.readLine();
			}
			br.close();
			
			
			
		} catch (FileNotFoundException e) {
		
			e.printStackTrace();
			
		} catch (IOException e) {

			e.printStackTrace();
		}
	}

}
  • 파일을 복사하는 예제
package step9;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

public class CopyAndPasteMenuService {
	private String orgFilePath;
	private String destDirPath;

	public CopyAndPasteMenuService(String orgFilePath, String destDirPath) {
		this.orgFilePath = orgFilePath;
		this.destDirPath = destDirPath;
	}

	/**
	 * 1. 복붙할 디렉토리 존재 유무를 확인, 없으면 생성한다 
	 * 2. orgFilePath 에 해당하는 파일을 입력받을 스트림을 생성한다. 
	 * 3. destDirPath 의 디렉토리를 생성했으므로 그 디렉토리에 저장할 파일명을 받아와서 출력 스트림을 생성한다. 
	 * 4. 데이터가 존재할때까지 반복하면서 입력받아 출력한다. 
	 * 5. 스트림 닫는다. (close) 이때 닫기 전에 버퍼에 있는 정보가 출력된다. flush
	 * 
	 * @throws IOException
	 */

	public void execute() throws IOException {
		// 1. 디렉토리 존재 유무 확인, 없으면 생성
		File destDir = new File(destDirPath);
		if (destDir.isDirectory() == false)
			destDir.mkdirs();
		BufferedReader br = null;
		PrintWriter pw = null;
		try {
			// 파일명을 입력받기 (memo.txt 그대로 복사하니까)
			// file 객체로 만들었으므로 orgFile.getName()으로 파일명을 가져올 수 있음
			File orgFile = new File(orgFilePath);
			// 입력받는 파이프라인(관)  2. orgFilePath 에 해당하는 파일을 입력받을 스트림을 생성한다.
			br = new BufferedReader(new FileReader(orgFile));
			pw = new PrintWriter(new FileWriter(destDir + File.separator + orgFile.getName()));
			//파일 내용 읽기
			while (br.ready()) {
				String data = br.readLine();
				pw.println(data);
			}
			
			//4. 데이터가 존재할때까지 반복하면서 입력받아 출력한다. 
			
			

		} finally {
			// 5. 스트림 닫는다.
			if (br != null)
				br.close();

		} // finally

	}// execute


}

3-4. GUI

  • Nested Class
package step10;

import javax.swing.JFrame;

public class TestSwingGUI1 {
	private JFrame frame;
	public void showUI() {
		frame = new JFrame("gui test");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(300, 100);
		frame.setLocation(300, 200);
		frame.setVisible(true);
	}
	public static void main(String[] args) {
		new TestSwingGUI1().showUI();
	}
}
package step10;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

public class TestSwingGUI2 {
	private JFrame frame;
	private JButton button;
	
	public void showUI() {
		frame = new JFrame("gui test");
		frame.setLayout(new FlowLayout());
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		button = new JButton("눌러봐");
		//버튼 이벤트 처리자 등록 ( Event Handling ) 
		button.addActionListener(new ButtonHandler());
		frame.add(button);
		frame.setSize(300, 100);
		frame.setLocation(300, 200);
		frame.setVisible(true);
	}
	//Nested class ( Inner Class )
	//private 멤버에 바로 접근 가능
	//버튼 이벤트 담당자
	class ButtonHandler implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			System.out.println("버튼 눌렀구나");
			//다른 클래스인 바깥 클래스(Outer class)의 private member 접근 가능
			button.setText("오늘 수고하셨습니다 즐퇴근");
		}
		
	}
	
	public static void main(String[] args) {
		new TestSwingGUI2().showUI();
	}
}
  • 이미지 파일을 복사하는 예제
    • TestImageService.java
package step1;

import java.io.File;
import java.io.IOException;

import common.DirInfo;

public class TestImageService {
public static void main(String[] args) {
	ImageService service = new ImageService();
	String orgImgFilePath = DirInfo.OUTPUT_DIR+"image.jpg";
	//이미지 파일이 존재하는지 확인 (isFile)
	System.out.println(new File(orgImgFilePath).isFile());
	String destImgDir = DirInfo.OUTPUT_DIR+"cp";
	try {
		service.copyAndPaste(orgImgFilePath, destImgDir);
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
}
  • ImageService.java
package step1;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

public class ImageService {

	/**
	 * 특정 위치에 있는 이미지 파일(orgImgFilePath)을 복사해서 (입력받아) <br>
	 * 특정 위치(destImgDir)로 붙여넣기 (출력하는) 하는 기능을 제공하는 메소드 <br>
	 * 1. destImgDir 디렉토리가 존재하는 지 확인해서 존재하지 않으면 생성 <br>
	 * 2. 파일에서 입력받을 스트림 생성 <br>
	 * 	  NodeStream 계열 : FileInputStream <br>
	 *    ProcessingStream 계열 : BufferedInputStream <br>
	 * 3. 파일을 출력할 스트림 생성
	 *    NodeStream 계열 : FileOutputStream <br>
	 *    ProcessingStream 계열 : BufferedOutputStream <br>
	 * 4. orgImgFilePath 로부터 입력받아 destImgDir에 파일을 생성해 출력한다. <br>
	 * 5. 스트림을 닫는다. (close -> flush 와 close)
	 * @param orgImgFilePath
	 * @param destImgDir
	 * @throws IOException 
	 */
	public void copyAndPaste(String orgImgFilePath, String destImgDir) throws IOException {
		//1. destImgDir 디렉토리가 존재하는 지 확인해서 존재하지 않으면 생성
		File destDir = new File(destImgDir);
		if(destDir.isDirectory() == false) {
			destDir.mkdirs();
		}
		String destFileName = new File(orgImgFilePath).getName();
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		try {
			bis = new BufferedInputStream(new FileInputStream(orgImgFilePath));
			bos = new BufferedOutputStream(new FileOutputStream(destImgDir+File.separator+destFileName));
			int data = bis.read();
			while(data != -1) {
				bos.write(data);
				data = bis.read();
			}
		
		}finally {
			if(bis != null)
				bis.close();
			if(bos != null)
				bos.close();
			System.out.println("파일 복붙 완료");
		}
		
	}//method

}//class

4. 객체 직렬화

  • 메모리(heap) 상에 있는 자바 객체를 외부로 전송할 수 있는 상태로 만드는 것이다.
  • java 객체 상태를 바이트 단위 Stream 으로 전송할 수 있도록 만드는 과정
  • 객체를 생성하기 위한 class가 Serializable interface의 하위 클래스로 설계되어야 한다.
  • ex) public class Car extends Vehicle implements Serializable { }  
  • 만약 직렬화 대상 클래스가 implements Serializable 하지 않으면 java.io.NotSerializableException 이 발생한다. 
    • 객체 역직렬화 (Object Deerialization) : 직렬화하여 저장된 객체의 정보를 다시 heap 영역의 메모리로 복원하는 것을 말한다.
    • 마샬링 : 객체나 특정 형태의 데이터를 저장 및 전송 가능한 데이터 형태로 변환하는 과정이다.
    • transient keyword : 직렬화 대상에서 제외시키고자 할 때 사용하는 자바 예약어
  • 객체 직렬화, 역직렬화 시 사용되는 Stream
객체 직렬화 스트림 객체 역직렬화 스트림
ObjectOutputStream ObjectInputStream
writeObject() readObject() : Object
  • 객체 직렬화/역직렬화 예제
package step2;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectSerialization1Service {
	
	//객체 직렬화 기능의 메소드
	public void executeOutput(String filePath, Car car) throws IOException {
		//상위 디렉토리 생성
		//File getParentFile() , isDirectory() , mkdirs()
		File file = new File(filePath);
		File dir = file.getParentFile();
		//System.out.println(dir.isDirectory());
		if (!dir.isDirectory()) {
			dir.mkdirs();
		}
		FileOutputStream fos = null;// 장치 (파일)에 연결되는 노드스트림
		ObjectOutputStream oos = null; // 객체 직렬화 기능을 지원하는 프로세스 스트림
		try {
			//checkedException => throws 해주자
			fos = new FileOutputStream(file);
			oos = new ObjectOutputStream(fos);
			oos.writeObject(car);
		}finally {
			if(fos != null)
				fos.close();
			if(oos != null)
				oos.close();
		}
	}//method

	//객체 역직렬화 메소드 : 파일에 저장된 객체 정보를 역직렬화하여 java memory heap에 복원
	public Car executeInput(String filePath) throws IOException, ClassNotFoundException {
		FileInputStream fis = null;
		ObjectInputStream ois = null;
		try {
			fis = new FileInputStream(filePath);
			ois = new ObjectInputStream(fis);
			Car car = (Car)ois.readObject(); // Object Down Casting : Object 타입으로 반환되므로 형변환 필요
			return car;
		}finally {
			if(fis != null)
				fis.close();
			if(ois != null)
				ois.close();
		}
		
	}
}//class

4-1. SerialVersionUID

  • 직렬화된 객체의 클래스 버전을 식별하는 고유한 아이디이다.
  • 직접 명시하지 않으면 jvm이 자동 생성하나, 직접 명시하는 것을 권장하는 이유는 클래스가 이후에 업데이트 되면 자동 생성되는 UID는 역직렬화시 InvalidClassException 가 발생한다. 
  • 클래스 업데이트 시에도 유연하게 객체 역직렬화를 지원하기 위해 serialVersionUID 를 직접 명시하도록 권장한다. = 호환성 보장

5. Thread

5-1. Thread 란?

  • 사전적 의미 : 실(thread)이 여러 개 (multi thread) 모여 옷(process)이 된다.
  • 프로세스 내부의 세부적 실행 단위이다.
    • 프로세스(process) : 현재 실행 중인 프로그램을 말한다.
      • multi-processing의 예) 컴퓨터 사용자가 코딩하며 youtube로 음악을 틀고 카톡으로 대화한다. 
        • multi process = 코딩 프로그램, youtube, 카카오톡 
  • 프로그램 실행 중에는 반드시 스레드가 하나 이상 수행되어야 한다. (=main)
    • multi-thread의 예) youtube 라는 프로그램을 실행시키면 영상(thread 1), 소리(thread 2), 자막(thread 3)이 동시에 실행된다.
    • 따라서 동시에 서비스하기 위해서는 Multi Threading 이 필요하다.
      • 서버 측면에서는 동시에 여러 명의 고객(클라이언트)에게 서비스하기 위해 멀티 스레드가 필요하다.
      • 클라이언트 측면에서는 입력받고 출력하는 각각의 스레드가 있어야 동시적으로 메시지를 주고받을 수 있다.
    • 스레드는 개발자가 직접 실행할 수는 없다. (실행 가능 상태로 변경하고, 이후 JVM과 OS가 스케줄링을 시작한다.)

5-2. Thread 생성 방법

  1. Thread class를 상속받는 방법 (extends Thread)
  2. Runnable Interface를 구현하는 방법 
  • 위의 두 방법 중 자바는 단일 상속만을 지원하므로 2번의 방법이 좋다. (해당 클래스가 다른 것을 상속받아야 하는 경우도 생기므로)
  • Thread 동작 원리
    1. Thread 생성 
    2. start() : 스레드를 실행 가능(Runnable)로 보낸다. 이후 jvm과 OS가 스케줄링을 한다.
    3. run() : 스케줄링을 받은 실행되는 스레드 동작 내용이다. 이 run() 메소드가 실행을 마치면 Thread가 종료된다.
      • 참고 : java thread 의 속성에서 1부터 10까지 우선 순위가 존재하며, 기본은 5이다. (우선 순위 적용 여부는 OS별로 상이하다.)
    4. 종료

  • 단일 스레드 환경
package step1;
/**
 * TestThread1 : 단일 스레드 환경 (main thread)
 * 
 * Thread step1 : 현재까지는 main thread 라는 하나의 스레드만으로 동작시켰다. 
 * 				  그래서 아래의 코드를 여러번 실행해도 변하지 않는 결과는 
 * 				  main method 내의 모든 작업이 다 수행된 후에 main thread 종료라는 메시지가 출력되고 
 * 				  main thread 가 종료된다. => 동기적이다
 */
class Worker{
	public void work() {
		for (int i= 0; i<5; i++) {
			System.out.println("현재 스레드명: "+Thread.currentThread().getName()+" i:"+i);
		}
	}
	
}
public class TestThread1 {
public static void main(String[] args) {
	System.out.println("main thread 시작");
	Worker worker = new Worker();
	worker.work();
	worker.work();
	worker.work();
	System.out.println("main thread 종료");
}
}
  • 멀티 스레드 환경- extends Thread 사용
package step1;

/**
 * TestThread2 : 멀티 스레드 환경 (main thread + video thread)
 * 											Thread 상속받는 방식으로 생성
 * 
 * TestThread1 단일 스레드(main Thread) 환경에서 실행
 * ==> TestThread2 Multi Thread 환경에서 실행
 * 			 main Thread , VideoWorker Thread 두 개가 함께 실행되도록 한다. 
 * 스레드 생성 방법 중 extends Thread로 스레드 생성, 실행(JVM) 되도록 start 한다
 * 
 * ==> 비동기 처리된 결과를 확인한다
 * main thread 는 자신의 역할 (영상 일꾼 스레드 생성 후 start)을 수행한 후 종료된다.
 * videoWorker 영상일꾼스레드는 start되면 Runnable 상태에서 대기하다가 스케줄링을 받으면 jvm에 의해 run이 호출되어 실행된 후 종료된다.
 * 
 */
class VideoWorker extends Thread{
	//VideoWorker Thread 의 실행 내용을 정의하는 메소드 run()
	//JVM이 스케줄링을 하면 호출되어 실행된다. 
	public VideoWorker (String name) {
		super(name); //부모 Thread 클래스의 생성자를 호출해 name을 할당한다.
	}
	
	public void run() {
		videoService();
	}
	public void videoService() {
		for (int i = 0; i<10;i++) {
			System.out.println(Thread.currentThread().getName() + " thread 영상 서비스");
			
			try {
				Thread.sleep(10); //0.03초간 현재 스레드 중단 후 재개하게 한다.
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
	}
}
}



public class TestThread2 {
	public static void main(String[] args) {
		System.out.println(Thread.currentThread().getName() + " thread 시작");
		VideoWorker videoWorker = new VideoWorker("영상일꾼"); //스레드를 생성 : VideoWorker은 Thread 를 상속받았음 
		videoWorker.start();//실행 가능 상태로 보낸다.
		System.out.println(Thread.currentThread().getName() + "VideoWorker thread를 실행 가능 상태로 보내기 위해 start 한다.");
		
		System.out.println(Thread.currentThread().getName() + " thread 종료");
	}
}
  • 멀티 스레드 환경 - implements Runnable 사용
package step1;

/**
 * TestThread3 : 멀티 스레드 환경 (main thread + audio thread)
 * 											Runnable interface 를 구현하는 방식으로 생성 
 * 
 * TestThread1 단일 스레드(main Thread) 환경에서 실행
 * ==> TestThread2 Multi Thread 환경에서 실행
 * 			 main Thread , AudioWorker Thread 두 개가 함께 실행되도록 한다. 
 * 스레드 생성 방법 중 extends Thread로 스레드 생성, 실행(JVM) 되도록 start 한다
 * 
 * ===> TestThread3 
 * 스레드 생성 방법 중 implements Runnable로 스레드 생성, 실행(JVM) 되도록 start 한다.
 * 
 * ==> 비동기 처리된 결과를 확인한다
 * main thread 는 자신의 역할 (영상 일꾼 스레드 생성 후 start)을 수행한 후 종료된다.
 * AudioWorker 영상일꾼스레드는 start되면 Runnable 상태에서 대기하다가 스케줄링을 받으면 jvm에 의해 run이 호출되어 실행된 후 종료된다.
 * 
 */
class AudioWorker implements Runnable {
	//AudioWorker Thread 의 실행 내용을 정의하는 메소드 run()
	//JVM이 스케줄링을 하면 호출되어 실행된다. 

	public void run() {
		AudioService();
	}
	
	public void AudioService() {
		for (int i = 0; i<10;i++) {
			System.out.println(Thread.currentThread().getName() + " thread 음향 서비스");
			
			try {
				Thread.sleep(10); //0.03초간 현재 스레드 중단 후 재개하게 한다.
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
	}
}
}

public class TestThread3 {
	public static void main(String[] args) {
		System.out.println(Thread.currentThread().getName() + " thread 시작");
		AudioWorker audioWorker = new AudioWorker(); //스레드를 생성 : AudioWorker은 Thread 를 상속받았음 
		Thread thread = new Thread(audioWorker, "음향스레드일꾼");
		thread.start();//실행 가능 상태로 보낸다.
		System.out.println(Thread.currentThread().getName() + "AudioWorker thread를 실행 가능 상태로 보내기 위해 start 한다.");
		
		System.out.println(Thread.currentThread().getName() + " thread 종료");
	}
}
  • Thread 제어
join() setDaemon(true)
특정 스레드가 수행 완료할 때까지 현재 스레드를 대기시킨다. 자신을 실행시킨 스레드가 종료되면 함께 종료한다.
  • 자바 메모리와 Thread
    • 스레드별로 독립적으로 생성되는 메모리 영역은 Stack, PC register
    • 멀티 스레드 환경에서 공유되는 메모리 영역은 MetaSpace, Heap
스레드 별로 독립적으로 생성되는 메모리 영역 멀티 스레드 환경에서 공유되는 메모리 영역
- Stack : 지역 변수가 저장되는 공간
- PC Register : Program Counter Register로 프로그램이 어느 지점까지 실행했는지에 대한 위치를 저장하는 공간
- Native Method Stack : 타 언어의 코드가 실행되기 위한 스택 영역
- MetaSpace : 모든 스레드가 공유하며, JVM이 클래스 정보를 관리하는 저장소 -> 클래스명, 상속 관계, 메소드 정보, 접근 제어자 등에 대한 정보를 스레드가 객체 생성하거나 메소드를 호출할 때 MetaSpace 의 정보를 참조한다.
- Heap : 객체(배열 포함) 정보가 저장되는 영역

 

'교육' 카테고리의 다른 글

9주차 - Web  (0) 2025.06.29
6, 7, 8주차 - DB와 JDBC  (0) 2025.06.14
5주차 - Java 객체 지향 프로그래밍 (끝)  (0) 2025.05.25
2주차 - Java 객체 지향 프로그래밍  (0) 2025.05.05
1주차 - Java 객체 지향 프로그래밍  (0) 2025.04.27