본문 바로가기

웹_프론트_백엔드/JAVA

2020.09.24(JAVA_심화반)

1. HaspMap을 이용하여 equals(), hashCode() 재정의

** com.lec.java.hashmap > Student.java

package com.lec.java.hashmap;

public class Student {
	int num;
	String name;
	
	public Student() {;}
	public Student(int num, String name) {
		super();
		this.num = num;
		this.name = name;
	}

	@Override
	public boolean equals(Object obj) {
		if(obj instanceof Student) {
			Student std = (Student)obj;
			if(std.hashCode() == this.hashCode()) {
				return true;
			}
		}
		return false;
	}
	
	@Override
	public int hashCode() {
		return this.num;
	}
}

 

** com.lec.java.hashmap > Task.java

package com.lec.java.hashmap;

import java.util.HashMap;

public class Task {
	public static void main(String[] args) {
		Student shine = new Student(1, "샤인");
		HashMap<Student, Integer> stdMap = new HashMap<>();
		
		stdMap.put(shine, 88);
		
		System.out.println(stdMap.get(new Student(1, "샤인")));
	}
}

 

 

2. ATM

1) 멀티쓰레드를 이용하여 ATM을 만든다.

** com.lec.java.atm > ATM.java

package com.lec.java.atm;

// 멀티 쓰레드를 이용하여 ATM을 만든다!
// 출금이란 기능이 쓰레드에서 접근하는 자원이 될 수 있다
public class ATM implements Runnable {
	int money = 10000;

	@Override
	public void run() {
		for(int i = 0; i < 5; i++) {
			withdraw(1000);
			try {Thread.sleep(500);} catch (InterruptedException e) {;}
		}
	}
	
	public void withdraw(int money) {
		this.money -= money;
		
		// Thread.currentThread().getName() : 접근한 쓰레드의 이름을 가져온다.
		System.out.println(Thread.currentThread().getName() + "이(가) " + money + "원 출금");
		System.out.println("현재 잔액 : " + this.money + "원");
	}
}

 

** com.lec.java.atm > CU.java

package com.lec.java.atm;

public class CU {
	public static void main(String[] args) {
		// 자원은 하나이고 여러 개에서 접근하려고 할 때,
		// 여러 개의 쓰레드(엄마, 아들)가 자원(ATM) 하나를 공유한다.
		// 즉, 자원이 하나에 여러 개의 쓰레드가 번갈아가면서 빠른 속도로 접근한다
		ATM atm = new ATM();
		
		// 생성자 Thread(Runnable target, String name)
		Thread mon_thread = new Thread(atm, "엄마");
		Thread son_thread = new Thread(atm, "아들");
		
		mon_thread.start();
		son_thread.start();
	}
}

 

2) 만들고 보니 아래의 사진과 같은 멀티 쓰레드의 자원 공유의 문제 발생했다.

   어떻게 해결해야할까?

 

3) 이를 극복하기 위해서는 동기화를 이용한다.

** com.lec.java.atm > ATM.java

package com.lec.java.atm;

// 멀티 쓰레드를 이용하여 ATM을 만든다!
// 출금이란 기능이 쓰레드에서 접근하는 자원이 될 수 있다
public class ATM implements Runnable {
	int money = 10000;

	// synchronized 키워드를 잘못 붙이면 박살난다!
	// run()에 synchronized 키워드를 붙이게 된다면 단일 쓰레드가 된다!
	// 그렇기 때문에 synchronized 붙일때에는 절대 run()에다가 붙이는 것이 아니라
	// 해당 요소에만 붙이면 된다.
	@Override
	public void run() {
		for(int i = 0; i < 5; i++) {
			withdraw(1000);
			try {Thread.sleep(500);} catch (InterruptedException e) {;}
		}
	}
	
	// synchronized 키워드를 붙이게 되면 자원 전체에 동기화를 사용한다.
	//public synchronized void withdraw(int money) {
	public void withdraw(int money) {
		//this.money -= money;
		
		// 동기화 블럭은 원하는 부분만 동기화를 하기 위해 사용한다.
		// mutex에 run메소드가 재정의된 객체를 적어준다.
		synchronized(this) {
			this.money -= money;
		}
		
		// Thread.currentThread().getName() : 접근한 쓰레드의 이름을 가져온다.
		System.out.println(Thread.currentThread().getName() + "이(가) " + money + "원 출금");
		System.out.println("현재 잔액 : " + this.money + "원");
	}
}

 

** com.lec.java.atm > CU.java

package com.lec.java.atm;

public class CU {
	public static void main(String[] args) {
		// 자원은 하나이고 여러 개에서 접근하려고 할 때,
		// 여러 개의 쓰레드(엄마, 아들)가 자원(ATM) 하나를 공유한다.
		// 즉, 자원이 하나에 여러 개의 쓰레드가 번갈아가면서 빠른 속도로 접근한다
		ATM atm = new ATM();
		
		// 생성자 Thread(Runnable target, String name)
		Thread mon_thread = new Thread(atm, "엄마");
		Thread son_thread = new Thread(atm, "아들");
		
		mon_thread.start();
		son_thread.start();
	}
}

 


3. 동기화(synchronized)
 : 멀티 쓰레드 내에서 자원의 공유 문제 발생시

   해당 영역 혹은 해당 자원에 동기화 키워드 및 동기화 블록을 작성하여 하나씩 접근하도록 하는 기법.
   각 쓰레드를 제어할 수 있게 된다.

   [예] 자바스크립트는 동기식, Ajax는 비동기식

 

   근데 왜 단일쓰레드를 쓰지 비동기식을 쓸까?

   다 멀티쓰레드인데 딱 한군데만 하나씩 접근해야할 때 사용한다.

   [예] 은행에서 금액 조회는 여러 명에서 한꺼번에 해도 되나,

         입금과 출금은 한 명씩 차례대로 접근해야 함

 

 

4. 단축키

   try ~ catch : 영역 잡고 Alt + Shift + Z
   synchronized : 영역 잡고 Alt + Shift + Z + 6

 

 

5. 빵집

   빵집은 총 20개의 빵을 만들 수 있다.

   손님에게 갓만든 빵을 대접해야 하기 때문에...! 빵이 10개 되는 순간 만드는 순간 멈출 것이다.

   그리고 손님이 빵을 사서 빵이 10개 미만되는 순간...! 다시 10개가 되기 전까지 빵을 만들 것이다.

   

   현재 아래의 코드로 실행을 하면 사진과 같은 에러가 발생한다.

   정장적으로 작동할 수 있도록 코드를 수정해보자!

** com.lec.java.bakery > BreadPlate.java

package com.lec.java.bakery;

public class BreadPlate {
	
	int breadCnt;
	int eatCnt;
	
	// 빵 만들기
	public synchronized void makeBread() {
		if(breadCnt > 9) {
			System.out.println("빵이 가득 찼습니다.");
			try {wait();} catch (InterruptedException e) {;}
		}
		breadCnt++;
		System.out.println("빵을 1개 만들었습니다. 총 : " + breadCnt + "개");
	}
	
	// 빵 먹기
	public synchronized void eatBread() {
		if(eatCnt == 20) {
			System.out.println("빵이 다 떨어졌습니다.");
		} else if(breadCnt < 1) {
			System.out.println("빵이 없습니다. 만들 때까지 기다려주세요.");
		} else {
			breadCnt--;
			eatCnt++;
			System.out.println("빵을 1개 먹었습니다. 총 : " + breadCnt + "개");
			notify();
		}
	}
}

 

** com.lec.java.bakery > BreadMaker.java

package com.lec.java.bakery;

public class BreadMaker implements Runnable {

	BreadPlate bread = new BreadPlate();
	
	@Override
	public void run() {
		for(int i = 0; i < 20; i++) {
			bread.makeBread();
			try {Thread.sleep(1000);} catch (InterruptedException e) {;}
		}
		System.out.println("영업 종료 / 재료 소진");
	}
}

 

** com.lec.java.bakery > Bakery.java

package com.lec.java.bakery;

import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

public class Bakery {
	public static void main(String[] args) {
		BreadPlate bread = new BreadPlate();
		BreadMaker maker = new BreadMaker();
		
		String[] arButton = {"빵 먹기", "나가기"};
		
		Thread maker_thread = new Thread(maker);
		
		maker_thread.start();
		
		ImageIcon icon = new ImageIcon("src/img/bread.gif");	// 상대경로
		
		while(true) {
			int choice = JOptionPane.showOptionDialog(null, "", "파리바게트", JOptionPane.DEFAULT_OPTION, 
					JOptionPane.PLAIN_MESSAGE, icon, arButton, null);
			
			if(choice == 0) {
				bread.eatBread();
			} else {
				break;
			}
		}
	}
}

 

1) 내가 수정한 코드

** com.lec.java.bakery.me > BreadPlate.java

package com.lec.java.bakery.me;

public class BreadPlate {
	
	int breadCnt;
	int eatCnt;
	
	// 빵 만들기
	public synchronized void makeBread() {
		if(breadCnt > 9) {
			System.out.println("빵이 가득 찼습니다.");
			try {wait();} catch (InterruptedException e) {;}
		}
		breadCnt++;
		System.out.println("빵을 1개 만들었습니다. 총 : " + breadCnt + "개");
	}
	
	// 빵 먹기
	public synchronized void eatBread() {
		if(eatCnt == 20) {
			System.out.println("빵이 다 떨어졌습니다.");
		} else if(breadCnt < 1) {
			System.out.println("빵이 없습니다. 만들 때까지 기다려주세요.");
		} else {
			breadCnt--;
			eatCnt++;
			System.out.println("빵을 1개 먹었습니다. 총 : " + breadCnt + "개");
			notify();
		}
	}
}

 

** com.lec.java.bakery.me > BreadMaker.java

package com.lec.java.bakery.me;

public class BreadMaker implements Runnable {

	BreadPlate bread = new BreadPlate();
	
	@Override
	public void run() {
		for(int i = 0; i < 20; i++) {
			bread.makeBread();
			try {Thread.sleep(1000);} catch (InterruptedException e) {;}
		}
		System.out.println("영업 종료 / 재료 소진");
	}
}

 

** com.lec.java.bakery.me > Bakery.java

package com.lec.java.bakery.me;

import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

public class Bakery {
	public static void main(String[] args) {
		BreadMaker maker = new BreadMaker();
		
		String[] arButton = {"빵 먹기", "나가기"};
		
		Thread maker_thread = new Thread(maker);
		
		ImageIcon icon = new ImageIcon("src/img/breadMe.gif");
		
		maker_thread.start();
		
		while(true) {
			int choice = JOptionPane.showOptionDialog(null, "", "파리바게트", JOptionPane.DEFAULT_OPTION,
					JOptionPane.PLAIN_MESSAGE, icon, arButton, null);
			
			if(choice == 0) {
				maker.bread.eatBread();
			} else {
				break;
			}
		}
	}
}

 

2) 강사님이 수정한 코드

** com.lec.java.bakery.teacher > BreadPlate.java

package com.lec.java.bakery.teacher;

public class BreadPlate {
	
	int breadCnt;
	int eatCnt;
	
	// 빵 만들기
	public synchronized void makeBread() {
		if(breadCnt > 9) {
			System.out.println("빵이 가득 찼습니다.");
			try {wait();} catch (InterruptedException e) {;}
		}
		breadCnt++;
		System.out.println("빵을 1개 만들었습니다. 총 : " + breadCnt + "개");
	}
	
	// 빵 먹기
	public synchronized void eatBread() {
		if(eatCnt == 20) {
			System.out.println("빵이 다 떨어졌습니다.");
		} else if(breadCnt < 1) {
			System.out.println("빵이 없습니다. 만들 때까지 기다려주세요.");
		} else {
			breadCnt--;
			eatCnt++;
			System.out.println("빵을 1개 먹었습니다. 총 : " + breadCnt + "개");
			notify();
		}
	}
}

 

** com.lec.java.bakery.teacher > BreadMaker.java

package com.lec.java.bakery.teacher;

public class BreadMaker implements Runnable {

	//BreadPlate bread = new BreadPlate();
	BreadPlate bread = null;
	
	public BreadMaker(BreadPlate bread) {
		this.bread = bread;
	}
	
	@Override
	public void run() {
		for(int i = 0; i < 20; i++) {
			bread.makeBread();
			try {Thread.sleep(1000);} catch (InterruptedException e) {;}
		}
		System.out.println("영업 종료 / 재료 소진");
	}
}

 

** com.lec.java.bakery.teacher > Bakery.java

package com.lec.java.bakery.teacher;

import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

public class Bakery {
	public static void main(String[] args) {
		//BreadMaker maker = new BreadMaker();
		//BreadPlate bread = maker.bread;
		BreadPlate bread = new BreadPlate();
		BreadMaker maker = new BreadMaker(bread);
		
		String[] arButton = {"빵 먹기", "나가기"};
		
		Thread maker_thread = new Thread(maker);
		
		maker_thread.start();
		
		ImageIcon icon = new ImageIcon("src/img/breadTeacher.gif");	// 상대경로
		
		while(true) {
			int choice = JOptionPane.showOptionDialog(null, "", "파리바게트", JOptionPane.DEFAULT_OPTION, 
					JOptionPane.PLAIN_MESSAGE, icon, arButton, null);
			
			if(choice == 0) {
				bread.eatBread();
			} else {
				break;
			}
		}
	}
}

 

 

6. Buffer

   변수는 선언하고 사용하지 않으면 노란줄의 경고가 뜨지만 그 변수를 사용해주면 사라진다.
   하지만 Scanner는 사라지지 않을까?

   경고 메시지를 보면 never closed로 닫치지 않았다는 경고였다.
   이 경고를 해결하려면 Buffer가 뭔지 알아야 해결할 수 있고 파일입출력에 대해 이해할 수 있다.

   Buffer 임시저장소로 Scanner를 선언하는 순간 buffer가 열렸는데 그걸 닫아주지 않아서 경고가 난 것이다.
   그 경고는 close() 메소드를 통해 닫아주면 사라진다.

   하지만 이때 주의해야할 점은 버퍼는 한번 닫으면 다시 열 수 없다는 점이다.

  [만약, 메모장에 내용을 작성하려고 할 때]

          작성한 내용을 적용하려면 flush()를 사용하고,
          작성을 다했하여 필요가 없어지게 되면 close()를 통해 buffer를 닫아야 한다.

 

 

7. 파일 입출력

1) Writer(입력)

   ButteredWriter : 버퍼를 사용하여 파일 작성

   FileWriter : 경로에 있는 파일 가져오기, 파일이 없으면 생성 후 가져온다.

   OutputStreamWriter : byte 단위(이미지, 동영상)

 

2) Reader(출력)

   BufferedReader : 버퍼를 사용해서 파일 읽기

   FileReader : 경로에 있는 파일 가져오기, 파일이 없으면 오류
   InputStreamWriter : byte 단위(이미지, 동영상)

 

 

8. 파일입출력 핵심 메소드
1) 입력

   ① write("문자열 값")
      문자열 값을 해당 경로에 있는 파일에 작성한다.

   ② close()
      flush()를 사용해서 버퍼에 있는 데이터를 일괄 처리한다.
      모든 작업이 끝난 후 반드시 close()를 사용해야 한다.

2) 출력
   ① readLine()
      해당 파일에 있는 내용을 한 줄 가져온다.
      만약 더 이상의 내용이 없다면 null을 리턴한다.

 

 

9. 파일입출력 정리

1) close() 메소드를 사용하지 않으면 버퍼에 남아있기 때문에 문자열이 새겨지지 않는다.

 

2) close() 메소드를 사용하니 메모장에 내용이 작성된다.

 

3) 그런데 write()를 할 때마다 기존의 내용이 유지가 안되고 덮어써진다.


4) 생성자 FileWriter(File file, boolean append)를 이용, 작성한 내용 뒤에 true를 쓰면 이어쓰기가 가능하다.

 

5) 행복, 기쁜, 슬픔을 각각 단어마다 줄바꿈하여 작성한다.

 

6) 기쁨을 사랑으로 수정하기


7) 슬픔 삭제하기

 

8) 사랑 검색


9) 슬픔 검색

 

 

10. 파일입출력 정리 코드

package com.lec.java.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class FileTest {
	// throws : 예외 던지기
	// 메소드 안에서 같은 예외가 여러 번 발생할 때 매번 예외 처리를 하기 번거롭다.
	// 따라서 메소드 중괄호 앞에 Throws Exception으로 막아준다.
	public static void main(String[] args) throws IOException {
		// 작성
		//BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"));
		//bw.write("안녕");
		//bw.close();
		//System.out.println("작성 종료");
		
		// 줄바꿈 : \n, newLine()
		//bw.write("행복\n");
		//bw.write("기쁨");
		//bw.newLine();
		//bw.write("슬픔\n");
		//bw.close();
		
		// 수정
		//BufferedReader br = new BufferedReader(new FileReader("test.txt"));
		
		//String line = null;
		
		//String temp = "";	// 임시저장소
		
		//while(true) {
		//	line = br.readLine();
		//	
		//	if(line == null) {break;}
		//	
		//	if(line.equals("기쁨")) {
		//		// 한줄을 가져올때 \n까지 가져오지 못하기 때문에 따로 추가해줘야한다.
		//		temp += "사랑" + "\n";
		//		continue;
		//	}
		//	
		//	// 한줄을 가져올때 \n까지 가져오지 못하기 때문에 따로 추가해줘야한다.
		//	temp += line + "\n";
		//}
		
		//BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"));
		//bw.write(temp);
		//bw.close();
		
		// 삭제
		//BufferedReader br = new BufferedReader(new FileReader("test.txt"));
		
		//String line = null;
		//String temp = "";
		
		//while(true) {
		//	line = br.readLine();
		//	if(line == null) {break;}
		//	if(line.equals("슬픔" )) {continue;}
		//	temp +=line + "\n";
		//}
		
		//BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"));
		//bw.write(temp);
		//bw.close();
		
		// 검색
		BufferedReader br = new BufferedReader(new FileReader("test.txt"));
		
		String line = null;
		boolean check = false;
		
		while(true) {
			line = br.readLine();
			if(line == null) {break;}
			//if(line.equals("사랑")) {
			if(line.equals("슬픔")) {
				System.out.println("검색 성공");
				check = true;
			}
		}
		if(!check) {
			System.out.println("검색 실패");
		}
	}
}

 

 

11. 다음 수업 내용 : MVC 모델

'웹_프론트_백엔드 > JAVA' 카테고리의 다른 글

2020.10.06(JAVA_심화반)  (0) 2020.10.07
2020.09.29(JAVA_심화반)  (0) 2020.10.01
2020.09.22(JAVA_심화반)  (0) 2020.09.25
2020.09.18(JAVA_심화반)  (0) 2020.09.21
2020.09.17(JAVA_심화반)  (0) 2020.09.18