일상
3, 4주차 - Java 객체 지향 프로그래밍 본문
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, 카카오톡
- multi-processing의 예) 컴퓨터 사용자가 코딩하며 youtube로 음악을 틀고 카톡으로 대화한다.
- 프로세스(process) : 현재 실행 중인 프로그램을 말한다.
- 프로그램 실행 중에는 반드시 스레드가 하나 이상 수행되어야 한다. (=main)
- multi-thread의 예) youtube 라는 프로그램을 실행시키면 영상(thread 1), 소리(thread 2), 자막(thread 3)이 동시에 실행된다.
- 따라서 동시에 서비스하기 위해서는 Multi Threading 이 필요하다.
- 서버 측면에서는 동시에 여러 명의 고객(클라이언트)에게 서비스하기 위해 멀티 스레드가 필요하다.
- 클라이언트 측면에서는 입력받고 출력하는 각각의 스레드가 있어야 동시적으로 메시지를 주고받을 수 있다.
- 스레드는 개발자가 직접 실행할 수는 없다. (실행 가능 상태로 변경하고, 이후 JVM과 OS가 스케줄링을 시작한다.)
5-2. Thread 생성 방법
- Thread class를 상속받는 방법 (extends Thread)
- Runnable Interface를 구현하는 방법
- 위의 두 방법 중 자바는 단일 상속만을 지원하므로 2번의 방법이 좋다. (해당 클래스가 다른 것을 상속받아야 하는 경우도 생기므로)
- Thread 동작 원리
- Thread 생성
- start() : 스레드를 실행 가능(Runnable)로 보낸다. 이후 jvm과 OS가 스케줄링을 한다.
- run() : 스케줄링을 받은 실행되는 스레드 동작 내용이다. 이 run() 메소드가 실행을 마치면 Thread가 종료된다.
- 참고 : java thread 의 속성에서 1부터 10까지 우선 순위가 존재하며, 기본은 5이다. (우선 순위 적용 여부는 OS별로 상이하다.)
- 종료

- 단일 스레드 환경
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 |