웹_프론트_백엔드/JAVA프레임윅기반_풀스택

2020.03.25

shine94 2020. 3. 25. 08:58

1. 예외를 처리를 Exception Handling이라고 한다.

 

 

2. java.lang.Exception  : 복구 가능
      |
      |__ java.lang.RuntimeException : 꼭 핸들링 해 줄 필요 없다(즉, 코드 상으로 반드시 해 줄 필요 없다)
      |    |__ ArithmeticException, NullPointerException, ...
      |
      |__ IOException, ParseException ... : 반드시 핸들링 해줘야 하는 에러(코드 상으로 반드시 해줘야 할 필요가 있다)

 

[실습코드]

 

1. [과제] 카드 발급 회사

** CardCompanyMain 클래스

package practice.cardcompany;
/* 
 * 카드회사가 있습니다. 카드회사는 '유일한 객체' 이고, 
 * 이 회사에서는 카드를 발급하면 항상 '고유번호' 가 자동으로 생성됩니다.
 * 10001 부터 시작하여 카드가 생성될때마다 10002, 10003 식으로 증가됩니다.
 * 다음 코드가 수행되도록 Card클래스와 Company 클래스를 구현하세요
 */
public class CardCompanyMain {

	// ★ 아래 main() 코드는 손대지 마세요 ★
	public static void main(String[] args) {

		Company com = Company.getInstance();  // 싱글톤 패턴
		Company company = Company.getInstance();  // 여러번 호출해도 동일 객체
				
		// ↓ '동일객체' 라고 결과가 나와야 한다.
		System.out.println((com == company) ? "동일객체" : "다른객체");
		
		Card myCard = company.createCard();  // 첫번째 카드생성 고유번호 10001 부여 
		Card yourCard = company.createCard();
		
		System.out.println(myCard.getCardNumber());  // 10001 출력
		System.out.println(yourCard.getCardNumber()); // 10002출력			 
		
	} // end main

} // end class

 

** Card 클래스

package practice.cardcompany;

public class Card{
	
	// 필요한 변수, 메소드, 생성자 정의하기
	public static int num = 10000;
	private int cardNumber = 1000;
	
	// 기본생성자
	private Card() {
		cardNumber = num;
		System.out.println("카드 잘 발급되고 저장됨" + cardNumber);
	}
	
	
	private static Card instance = null;
	public static Card getInstance() {
		// 카드번호 1증가
		num++;

		// 인스턴스 생성!
		instance = new Card();	
		
		return instance;
	}

	// getter, setter
	public int getCardNumber() {return cardNumber;}
	public void setCardNumber(int cardNumber) {this.cardNumber = cardNumber;}
	
} // end class

 

** Company 클래스

package practice.cardcompany;

public class Company {
	
	// 기본 생성자
	private Company() {	}
	
	private static Company instance = null;
	public static Company getInstance() {
		if(instance == null) {
			instance = new Company();	// 인스턴스 생성
		}
		return instance;
	}
	
	// 이때 카드 발급..!!
	public Card createCard() {
		return Card.getInstance();
	}
	
}


-> 과제 결과는 잘 나왔으나 쌤이 원하신 건 카드번호를 company가 관리하는 것인데 나는 Card에서 관리함

    아래의 코드는 쌤이 의도한 대로 과제를 잘 해온 학생의 코드

** CardCompanyMain 클래스

package practice.cardcompany;
/* 
 * 카드회사가 있습니다. 카드회사는 '유일한 객체' 이고, 
 * 이 회사에서는 카드를 발급하면 항상 '고유번호' 가 자동으로 생성됩니다.
 * 10001 부터 시작하여 카드가 생성될때마다 10002, 10003 식으로 증가됩니다.
 * 다음 코드가 수행되도록 Card클래스와 Company 클래스를 구현하세요
 */
public class CardCompanyMain {

	// ★ 아래 main() 코드는 손대지 마세요 ★
	public static void main(String[] args) {
		Company com = Company.getInstance();  // 싱글톤 패턴
		Company company = Company.getInstance();  // 여러번 호출해도 동일 객체
				
		// ↓ '동일객체' 라고 결과가 나와야 한다.
		System.out.println((com == company) ? "동일객체" : "다른객체");
		
		Card myCard = company.createCard();  // 첫번째 카드생성 고유번호 10001 부여 
		Card yourCard = company.createCard();
		
		System.out.println(myCard.getCardNumber());  // 10001 출력
		System.out.println(yourCard.getCardNumber()); // 10002출력			 
	} // end main

} // end class

 

** Card 클래스

package practice.cardcompany;

public class Card {

	// 카드 고유 번호
	private int cardNumber;
	
	// 생성자
	Card(int cardNumber) {
		this.cardNumber = cardNumber;
	}

	public int getCardNumber() {
		return cardNumber;
	}

	protected void setCardNumber(int cardNumber) {
		this.cardNumber = cardNumber;
	}
	
} // end class

 

** Company 클래스

package practice.cardcompany;

public class Company {

	// 필요한 변수, 메소드, 생성자 정의하기
	// 카드번호
	private int cardNumber = 10000;
	
	// default Constructor
	private Company() {}
	
	// instance 주소
	private static Company instance = null;
	// instance 생성, 중복 확인
	static Company getInstance() {
		if(instance == null) {
			instance = new Company();
		}
		return instance;
	}

	// 카드 발급
	public Card createCard() {
		cardNumber++;
		return new Card(cardNumber);
	}
	
} // end class

 

 

2. 전화번호부 1.0 (어제 만든거 최종 완성시킴) : unique id, regDate 추가
** PhonebookMain 클래스

package phonebook01.class01;

import java.util.Scanner;

public class PhonebookMain {

	Scanner sc;
	PhonebookModel[] bookData = new PhonebookModel[5]; 
	
	public static void main(String[] args) {
		PhonebookMain app = new PhonebookMain();
		app.init();	// 초기화
		app.run();	// 실행
		app.exit();	// 종료
	} // end main()
	
	// 응용프로그램을 초기화 
	public void init() {
		sc = new Scanner(System.in);
	}
	
	// 응용프로그램 구동
	public void run() {
		System.out.println("전화번호부 v1.0");
		
		while(true) {
			showMenu();  // 메뉴 표시
			
			int menu = sc.nextInt(); // 메뉴 입력
			sc.nextLine();
			
			switch(menu) {
			case 1:
				System.out.println("전화번호부를 입력합니다");
				insertPhoneBook();
				break;
			case 2:
				System.out.println("전화번호부 출력(열람)");
				showPhoneBook();
				break;
			case 3:
				System.out.println("프로그램을 종료합니다");
				return;
			default:
				System.out.println("잘못 입력하셨습니다");
			}
			
		} // end while	
	} // end run()
	
	// 응용프로그램 종료
	public void exit() {
		sc.close();
	}	
	
	// 전화번호부 입력
	public void insertPhoneBook() {
		
		// 전화번호부가 다 찼는지 체크
		int i;
		for(i = 0; i < bookData.length; i++) {
			if(bookData[i] == null) {break;}
		}
		// 다 찼으면 입력불가 처리
		if(i == bookData.length) {
			System.out.println("전화번호부가 다 찼습니다");
			return;
		}
		
		// 이름, 전화번호, 이메일 입력
		System.out.print("이름 입력:");
		String name = sc.nextLine();

		System.out.print("전화번호 입력:");
		String phoneNum = sc.nextLine();
		
		System.out.print("이메일 입력:");
		String email = sc.nextLine();
		
		// --> PhonebookModel 인스턴스 생성
		PhonebookModel pb = new PhonebookModel(name, phoneNum, email);
		pb.setUid(i);
		
		// 배열에 추가
		bookData[i] = pb;
		System.out.println((i + 1) + "번째 전화번호부 추가 성공");
	}
	
	// 전화번호부 출력
	public void showPhoneBook() {
		// 배열안에 저장된 전화번호부들 출력.
		int i;
		for(i = 0; i < bookData.length; i++) {
			PhonebookModel pb = bookData[i];
			if(pb == null) {break;}
			
			System.out.println(pb);
		}
		// i가 0으로 시작되는데 1을 더해줘야 하는 것이 아닌가...?
		// 아니다!! 더해주지 않아도 된다!! 
		// 왜? bookData.length가 4라고 하자..
		// 위의 for문은 i가 0부터 3까지 실행되고 
		// i가 4가 되는 순간 for 문을 탈출한다
		// 굳이 1 증가 해주지 않아도 이미 원하는 데이터 출력이 가능함..!!
		System.out.println(i + " 개의 전화번호부 출력");
	}
	

	public void showMenu() {
		System.out.println();
		System.out.println("전화번호부 프로그램");
		System.out.println("------------------");
		System.out.println("[1] 입력");
		System.out.println("[2] 열람");
		System.out.println("[3] 종료");
		System.out.println("------------------");
		System.out.print("선택: ");
	}

} // end class

 

** PhonebookModel 클래스

package phonebook01.class01;

import java.text.SimpleDateFormat;
import java.util.Date;

public class PhonebookModel {
	// 멤버 변수
	private int uid;	// unique id
	private String name;	// 이름
	private String phoneNum;	// 전화번호
	private String email;	// 이메일
	private Date regDate;	// 등록일시
	
	// 기본 생성자
	public PhonebookModel() {
		this.name = "";
		this.phoneNum = "";
		this.email = "";
		this.regDate = new Date();	// 생성되는 현재시간.
	}

	//매개변수 생성자
	public PhonebookModel(String name, String phoneNum, String email) {
		this();
		this.name = name;
		this.phoneNum = phoneNum;
		this.email = email;
	}
	
	public PhonebookModel(int uid, String name, String phoneNum, String email, Date regDate) {
		super();
		this.uid = uid;
		this.name = name;
		this.phoneNum = phoneNum;
		this.email = email;
		this.regDate = regDate;
	}

	// getter & setter
	public int getUid() {return uid;}
	public void setUid(int uid) {this.uid = uid;}

	public String getName() {return name;}
	public void setName(String name) {this.name = name;}

	public String getPhoneNum() {return phoneNum;}
	public void setPhoneNum(String phoneNum) {this.phoneNum = phoneNum;}

	public String getEmail() {return email;}
	public void setEmail(String email) {this.email = email;}

	public Date getRegDate() {return regDate;}
	public void setRegDate(Date regDate) {this.regDate = regDate;}

	
	@Override
	public String toString() {
		String str = String.format("%3d|%s|%s|%s|%20s", uid, name, phoneNum, email, 
					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(regDate));
		return str;
	}
	
}

 

 

3. 전화번호부 2.0 : 어제 만든 전화번호부를 MVC(Model View Controller) 적용해보기

   [MVC 장점] 유지보수에 용이함
   [MVC 단점] 복잡함

   [중요!] 데이터를 다룰때는 CRUD (Create Read Update Delete) 바탕으로 만들기!

** PhonebookMain 클래스

package phonebook02.mvc;

import java.util.Scanner;

// VIEW 객체
// 사용자와의 입출력 담당 (UI, ...)
public class PhonebookMain {

	private Scanner sc;
	private PhonebookManager pbCtrl;   // CONTROLLER 객체
	
    
	public static void main(String[] args) {
		PhonebookMain app = new PhonebookMain();
		app.init();	// 초기화
		app.run();	// 실행
		app.exit();	// 종료
	} // end main()
	
    
	// 응용프로그램을 초기화 
	public void init() {
		sc = new Scanner(System.in);
		pbCtrl = PhonebookManager.getInstance();	// CONTROLLER 생성
	}
	
	// 응용프로그램 구동
	public void run() {
		System.out.println(PhonebookController.VERSION);
		
		while(true) {
			showMenu();	// 메뉴 표시
			
			int menu = sc.nextInt();	// 메뉴 입력
			sc.nextLine();
			
			switch(menu) {
			case Menu.MENU_INSERT:
				insertPhoneBook();
				break;
			case Menu.MENU_LIST:
				listPhonebook();
				break;
			case Menu.MENU_DELETE:
				deletePhonebook();
				break;
			case Menu.MENU_UPDATE:
				updatePhonebook();
				break;
			case Menu.MENU_QUIT:
				System.out.println("프로그램을 종료합니다.");
				// return의 의미가 메소드를 끝낸다는 뜻이고
				// run 메소드는 리턴 값이 void
				// void가 return을 안쓴다는 의미가 아니라
				// 아래와 같이 작성할 수 있음!
				// 즉, return에 값이 없다는 뜻이라고 생각하면 됨!!
				return;
			default:	
				System.out.println("잘못 입력하셨습니다!");
			} // end switch
		} // end while	
	} // end run()
	
	// 응용프로그램 종료
	public void exit() {
		sc.close();
	}	
	
	// 전화번호부 입력
	public void insertPhoneBook() {
		// VIEW 역할 : 사용자 입출력
		System.out.println("-- 입력 메뉴 --");
		
		// 이름, 전화번호, 메모 입력
		System.out.print("이름 입력:");
		String name = sc.nextLine();

		System.out.print("전화번호 입력:");
		String phoneNum = sc.nextLine();
		
		System.out.print("메모 입력:");
		String memo = sc.nextLine();
		
		// CONTROLLER에 연결
		int result = pbCtrl.insert(name, phoneNum, memo);
		if(result == PhonebookController.QUERY_FAIL) {
			System.out.println("입력 실패");
		} else {
			System.out.println(result + " 개의 전화번호 입력 성공");
		}
	} // end insertPhoneBook()
	
	// 전화번호부 열람 (전체)
	public void listPhonebook() {
		// CONTROLLER 연결
		PhonebookModel[] data = pbCtrl.selectAll();
		
		// VIEW 역할 : 사용자 입출력
		System.out.println("총 " + data.length + " 명의 전화번호 출력");
		
		for(PhonebookModel e : data) {
			System.out.println(e);
		}
	} // end listPhonebook() 
	
	// 전화번호부 수정
	public void updatePhonebook() {
		// VIEW : 사용자 입출력
		System.out.println("--- 수정 메뉴 ---");
		System.out.println("수정할 번호 입력 : ");
		int uid = sc.nextInt();
		sc.nextLine();		// 버퍼 비우기
		
		// CONTROLLER에 연결
		if(pbCtrl.selectByUid(uid) == null) {
			System.out.println("존재하지 않는 uid : " + uid);
			return;
		}
		
		// VIEW : 이름, 전화번호, 메모 입력
		System.out.print("이름 입력:");
		String name = sc.nextLine();

		System.out.print("전화번호 입력:");
		String phoneNum = sc.nextLine();
		
		System.out.print("메모 입력:");
		String memo = sc.nextLine();
		
		// CONTROLLER에 연결
		int result = pbCtrl.updateByUid(uid, name, phoneNum, memo);
		if(result == PhonebookController.QUERY_FAIL) {
			System.out.println("수정 실패");
		} else {
			System.out.println(result + " 개의 전화번호 수정 성공");
		}
	} // end updatePhonebook()
	
	// 전화번호 삭제
	public void deletePhonebook() {
		// VIEW : 사용자 입출력
		System.out.println("--- 삭제 메뉴 ---");
		
		System.out.println("삭제할 번호 입력 : ");
		int uid = sc.nextInt();
		sc.nextLine();	// 버퍼 지우기
		
		// CONTROLLER
		if(pbCtrl.selectByUid(uid) == null) {
			System.out.println("존재하지 않는 uid : " + uid);
			return;
		}
		
		int result = pbCtrl.deleteByUid(uid);
		if(result == PhonebookController.QUERY_FAIL) {
			System.out.println("삭제 실패");
		} else {
			System.out.println(result + " 개의 전화번호 삭제 성공");
		}
	} // end deletePhonebook()
	
	public void showMenu() {
		System.out.println();
		System.out.println("전화번호부 프로그램");
		System.out.println("------------------");
		System.out.println(" [0] 종료");
		System.out.println(" [1] 입력");
		System.out.println(" [2] 열람");
		System.out.println(" [3] 수정");
		System.out.println(" [4] 삭제");
		System.out.println("------------------");
		System.out.print("선택: ");
	}

} // end class

 

** PhonebookModel 클래스

package phonebook02.mvc;

import java.text.SimpleDateFormat;
import java.util.Date;

// MODEL 객체
// 데이터를 표현 객체
// 명심! 데이터를 표현하는 객체이지 저장하는 객체가 아니다!!!
public class PhonebookModel {
	// 멤버 변수
	private int uid;		// unique id
	private String name;		// 이름
	private String phoneNum;	// 전화번호
	private String memo;		// 메모
	private Date regDate;		// 등록일시
	
    
	// 기본 생성자
	public PhonebookModel() {
		this.name = "";
		this.phoneNum = "";
		this.memo = "";
		this.regDate = new Date();	// 생성되는 현재시간.
	}

	//매개변수 생성자
	public PhonebookModel(String name, String phoneNum, String email) {
		this();
		this.name = name;
		this.phoneNum = phoneNum;
		this.memo = email;
	}
	
	public PhonebookModel(int uid, String name, String phoneNum, String email, Date regDate) {
		super();
		this.uid = uid;
		this.name = name;
		this.phoneNum = phoneNum;
		this.memo = email;
		this.regDate = regDate;
	}


	// getter & setter
	public int getUid() {return uid;}
	public void setUid(int uid) {this.uid = uid;}

	public String getName() {return name;}
	public void setName(String name) {this.name = name;}

	public String getPhoneNum() {return phoneNum;}
	public void setPhoneNum(String phoneNum) {this.phoneNum = phoneNum;}

	public String getMemo() {return memo;}
	public void setMemo(String memo) {this.memo = memo;}

	public Date getRegDate() {return regDate;}
	public void setRegDate(Date regDate) {this.regDate = regDate;}

	
	@Override
	public String toString() {
		String str = String.format("%3d|%s|%s|%s|%20s", uid, name, phoneNum, memo, 
					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(regDate));
		return str;
	}

}

 

** PhonebookManager 클래스

package phonebook02.mvc;

import java.util.Arrays;

// CONTROLLER 객체
//	어플리케이션의 동작, 데이터 처리, (Business logic 담당)
public class PhonebookManager implements PhonebookController {
	
	public static final int MAX_DATA = 3;	// 최대 데이터 개수
	
	// 배열로 구현
	private PhonebookModel[] pbList = new PhonebookModel[MAX_DATA];
	
	// 몇 개의 데이터가 저장되었는지
	private int count = 0;
	
	
	// singleton 적용
	private PhonebookManager() {}
	private static PhonebookManager instance = null;
	public static PhonebookManager getInstance() {
		if(instance == null) {
			instance = new PhonebookManager();
		}
		return instance;
	} // end getInstance()
	
    
	// 전화번호부 생성 등록
	@Override
	public int insert(String name, String phoneNum, String memo) {
		
		int result = PhonebookController.QUERY_FAIL;
		
		// 매개변수 검증 : 이름 필수
		if(name == null || name.trim().length() == 0) {
			return result;
		}
		
		// 만약 배열이 다 차있으면, MAX_DATA 만큼 추가된 새 배열 작성
		if(count == pbList.length) {
			pbList = Arrays.copyOf(pbList, pbList.length + MAX_DATA);
		}
		
		pbList[count] = new PhonebookModel(name, phoneNum, memo);
		pbList[count].setUid(getMaxUid() + 1);	// 기존의 최대 uid 값보다 1 증가한 값(unique한 값 보장)
		// uid와 배열 index는 다르다!!
		count++;
		
		return 1;
	}

	@Override
	public PhonebookModel[] selectAll() {
		return Arrays.copyOfRange(pbList, 0, count);
	}

	// 특정 uid 의 데이터 검색 리턴
	// 못찾으면 null 리턴
	@Override
	public PhonebookModel selectByUid(int uid) {
		
		for(int index = 0; index < count; index++) {
			if(pbList[index].getUid() == uid) {
				return pbList[index];	// uid값 발견하면 리턴
			}
		}
		
		return null;	// 못찾으면 null 리턴
	} // end selectByUid()

	@Override
	public int updateByUid(int uid, String name, String phoneNum, String memo) {
		int result = PhonebookController.QUERY_FAIL;
		
		// 매개변수 검증
		if(uid < 1) {return result;}
		if(name == null || name.trim().length() == 0) {return result;}	// 이름 필수
		
		// 특정 uid 값을 가진 데이터의 배열 인덱스 찾기
		int index = findIndexByUid(uid);
		if(index < 0) {return result;}
		
		pbList[index].setName(name);
		pbList[index].setPhoneNum(phoneNum);
		pbList[index].setMemo(memo);
		result = 1;	// 수정 성공
		
		return result;
	}

	@Override
	public int deleteByUid(int uid) {
		int result = PhonebookController.QUERY_FAIL;
		
		// 매개변수 검증
		if(uid < 1) {return result;}
		
		int index = findIndexByUid(uid);
		if(index < 0) {return result;}
		
		// 배열 뒤의 원소들을 앞으로 이동
		for(int i = index, j = index + 1; i < count; i++, j++) {
			pbList[i] = pbList[j];
		}
		
		// count 1 감소
		count--;
		result = 1;
		
		return result;
	}
	
	// 현재 데이터 중 가능 큰 uid값을 찾아서 리턴
	private int getMaxUid() {
		int maxUid = 0;
		
		for(int index = 0; index < count; index++) {
			if(maxUid < pbList[index].getUid()) {
				maxUid = pbList[index].getUid();
			}
		}
		return maxUid;
	}

	// 특정 uid 값을 가진 데이터의 배열 인덱스 찾기
	// 못찾으면 -1 리턴
	private int findIndexByUid(int uid) {
		for(int index = 0; index < count; index++) {
			if(pbList[index].getUid() == uid) {
				return index;
			}
		}
		return -1;
	}
	
}

 

** PhonebookController 인터페이스

package phonebook02.mvc;

// Controller 인터페이스
//	동작 정의하기 전에 '동작 설계'
public interface PhonebookController {
	
	public static final String VERSION = "전화번호부 2.0";
	
	// 만들고자 하는 응용프로그램의 
	// '동작' 을 설계하는 것이 인터페이스다.
	// 이 인터페이스는  View와 Controller의 연결고리 역할을 하여. 모듈단위 유지관리를 용이하게 해준다. 
	
	// [동작 설계]
	// 1. 이름(name)과 전화번호(phoneNum)과 (memo)값이 주어지면 전화번호 데이터(PhonebookModel)를   
	//		생성하여 저장, 날짜(regDate)는 생성한 날짜로, uid 값은 자동 증가 값으로
	//		성공하면 1, 실패하면 0 리턴
	// 2. 현재 전화번호부에 저장된 전화번호 데이터(PhonebookModel)들을 전부 불러들여서 
	//		리턴(PhonebookModel 배열로)
	// 3. 특정 uid 값을 가진 전화번호 데이터(PhonebookModel)을 찾아서 리턴, 없으면 null 리턴
	// 4. 특정 uid 값을 가진 전화번호 데이터(PhonebookModel)을 찾아서 
	//		주어진 이름(name)과 전화번호(phoneNum)과 (memo)값으로 변경.  
	//		성공하면 1, 실패하면 0 리턴
	// 5. 특정 uid 값을 가진 전화번호 데이터(PhonebookModel)을 찾아서 삭제.
	//		성공하면 1, 실패하면 0 리턴
	
	
	public abstract int insert(String name, String phoneNum, String memo);	/* 1 */
	public abstract PhonebookModel[] selectAll();	/* 2 */
	public abstract PhonebookModel selectByUid(int uid);	/* 3 */
	public abstract int updateByUid(int uid, String name, String phoneNum, String meno);	/* 4 */
	public abstract int deleteByUid(int uid);	/* 5 */

	
	public static final int QUERY_FAIL = 0;

}

 

** Menu 인터페이스

package phonebook02.mvc;

public interface Menu {
	public static final int MENU_QUIT = 0;
	public static final int MENU_INSERT = 1;
	public static final int MENU_LIST = 2;
	public static final int MENU_UPDATE = 3;
	public static final int MENU_DELETE = 4;
}

 

 

4. Lec18_Exception
1) com.lec.java.exception01 패키지, Exception01Main 클래스

package com.lec.java.exception01;

import java.util.Scanner;
/* 예외(Exception)
	
	컴파일 에러 : 문법상 오류
	예외(Exception) : 문법상의 오류 가 아닌 '실행중' 에 발생되는 오류상황
	  
	(기본적으로) 예외 가 발생되면, 예외 관련 메세지 출력하고 프로그램이 종료 됩니다.  
 */
public class Exception01Main {

	public static void main(String[] args) {
		System.out.println("예외(Exception)");
		
		System.out.println("[1] ArithmeticException");
		int num1 = 123;
		int num2 = 10;

		System.out.println("num1 / num2 = " + (num1 / num2));
		//System.out.println("num1 / num2 = " + (num1 / 0)); // 오류 : java.lang.ArithmeticException

		
		System.out.println("[2] ArrayIndexOutOfBoundsException");
		int[] numbers = new int[10];
		
		//numbers[10] = 100;			// 오류 : java.lang.ArrayIndexOutOfBoundsException
		
		
		System.out.println("[3] NegativeArraySizeException");
		int size = -1;
		
		//int[] number2 = new int[size];	// 오류 : java.lang.NegativeArraySizeException
		
		
		System.out.println("[4] NullPointerException");
		String str = null;
		
		// null에다가 .을 붙일 수 없음
		// .앞을 의심 해봐야함..!!
		//System.out.println("문자열 길이: " + str.length()); // 오류 : java.lang.NullPointerException
		
		
		System.out.println("[5] InputMismatchException");  
		Scanner sc = new Scanner(System.in);
	
		// int 값을 입력해야하는데 int 이외의 다른 값을 입력하면 해당 오류 발생
		// 실행 과정에서 오류 발생함.
		sc.nextInt();
		
		
		sc.close();
	
		System.out.println("\n프로그램 종료");
	} // end main()

} // enc class Exception01Main


2) com.lec.java.exception02 패키지, Exception02Main 클래스

package com.lec.java.exception02;
/* 예외처리 : try~catch를 사용하는 이유
	 1. if 문은 예외 처리 이외의 용도로 사용되기 때문에 프로그램 코드상에서 
	    예외처리 부분을 구분하기가 쉽지 않다
	 2. try {}  블럭은 '일반적인 흐름'을   ,catch {} 블럭을 '예외처리' 
	    블럭으로 만듦으로 코드 분석이 훨씬 용이.
*/
public class Exception02Main {

	public static void main(String[] args) {
		System.out.println("예외(Exception) 처리");
		
		int num1 = 123;
		int num2 = 0;
		int result = 0;
		
		// if 문을 사용한 처리
		if (num2 != 0) {
			result = num1 / num2;
		} else {
			System.out.println("0으로 나눌 수 없습니다... ");
		} // end if
		
		System.out.println("결과: " + result);
		
		
		System.out.println("\ntry ~ catch 사용");
		// 위의 코드를 try~catch 로 만들어 처리

		try {
			// 수행 블럭
			result = num1 / num2;
			System.out.println("결과 : " + result);
		} catch(ArithmeticException ex) {
			// 예외 처리 블록 : 예외 발생시 수행코드
			System.out.println("0으로 나누는 Exception 발생");
			System.out.println(ex.getMessage());
			ex.printStackTrace();
			// 예외가 처리되면(handling) 프로그램 종료되지 않고 계속 진행
		}
		
		
		System.out.println();
		System.out.println("프로그램 종료...");

	} // end main()

	/* 
		특히 시스템 자원(resource), HW (파일, DB..) 등을 사용하는 프로그래밍에선
		'예외' 가 언제든지 발생할수 있으므로 try ~ catch 가 필수적이다.
		 
		try {
			// 1. DB connect
			// 2. DB 테이블 접속/쿼리
			// 3. SQL 문장 실행
			// 4. 결과 출력
		} catch (Exception e) {
			// Exception 처리
		}
		
		if (DB connect 성공) {
			if (DB table 쿼리 성공) {
				if (SQL 문장 실행) {
					System.out.println("결과"));
				} else {
					// SQL 문장 실패 처리
				}
			} else {
				// DB 테이블 쿼리 실패 처리
			}
		} else {
			// DB connect 실패 처리
		}
	 */
	
} // end class Exception02Main

 

[추가] ex.getMassage() : 에러 이벤트와 함께 들어오는 메세지를 출력
         ex.printStackTrace() : 에러 메세지의 발생 근원지를 찾아서 단계별로 에러를 출력

3) com.lec.java.exception03 Exception03Main

package com.lec.java.exception03;
/*  예외 처리: try ~ catch
  	연습 01패키지에서 발생하던 예외들을 try~catch로 잡아보자 
  	
 	 주의! : try 블럭 안에서 선언된 변수는 try 블럭안에서만 사용되는 지역변수가 된다.
	     catch 블럭등 다른 블럭에서도 사용 가능하게 하려면 try 바깥에서 선언해야 한다
 */
public class Exception03Main {

	public static void main(String[] args) {
		System.out.println("예외 처리: try ~ catch");
		
		System.out.println();
		System.out.println("[1] ArithmeticException");
		int num1 = 0;
		int num2 = 0;
		
		// try ~ catch 로 감싸기
		// 주의! try 블럭 안에서 선언된 변수는 try 블럭 안에서만 사용되는 지역변수가 된다.
		//      catch 블럭 등 다른 블럭에서도 사용 가능하게 하려면 try 바깥에서 선언해야 한다.
		try {
			num1 = 123;
			num2 = 0;
			System.out.println("num1 / num2 = " + (num1 / num2));
		} catch(ArithmeticException ex) {
			System.out.println(num1 + "은 " + num2 + "로 나눌 수 없다.");
			System.out.println(ex.getMessage());
			ex.printStackTrace();
		}
		
		
		System.out.println();
		System.out.println("[2] ArrayIndexOutOfBoundsException");

		// try ~ catch 로 감싸기
		try {
			int[] numbers = new int[10];
			numbers[100] = 111;
		
		} catch (ArrayIndexOutOfBoundsException ex) {
			System.out.println("예외 메시지: " + ex.getMessage());
			ex.printStackTrace();
		} // end catch
		
		
		System.out.println();
		System.out.println("[4] NullPointerException");
		try {
			String str = null;
			System.out.println("스트링 길이: " + str.length());
			
		} catch (NullPointerException ex) {
			System.out.println("예외 메시지: " + ex.getMessage());
			
		} // end catch
		
		System.out.println();
		try {
			Object obj = new int[10];
			String str = (String)obj;
            
		} catch (ClassCastException ex) {
			System.out.println("예외 메시지: " + ex.getMessage());
			// 배열도 Object 클래스를 상속하므로 배열 인스턴스는 Object 의 참조변수로 참조가 가능하다
            
		} // end catch

		
		System.out.println("\n프로그램 종료");
	} // end main

} // end class Exception03Main

 

[추가] ex.getMassage() : 에러 이벤트와 함께 들어오는 메세지를 출력

[추가] ex.printStackTrace() : 에러 메세지의 발생 근원지를 찾아서 단계별로 에러를 출력

-> ex.printStackTrace() 사용전

-> ex.printStackTrace() 사용후

4) com.lec.java.exception04 Exception04Main

package com.lec.java.exception04;
/*	예외 클래스의 상속 관계 (★필수 암기★)
	java.lang.Object
	|__ java.lang.Throwable
	  |
	  |__ java.lang.Exception  : 복구 가능
	  |    |
	  |    |__ java.lang.RuntimeException
	  |    |    |__ ArithmeticException, NullPointerException, ...
	  |    |
	  |    |__ IOException, ParseException ...
	  |
	  |__ java.lang.Error    : 복구 불가
	         ...   OutOfMemoryError, StackOverFlowError ...  
	
	 getMessage(), printStackTrace() 등의 메소드는 Throwable 클래스에 정의
	 자식 클래스들은 Throwable에 정의된 메소드들을 사용할 수 있음
	 
	catch문은 여러개를 사용할 수는 있지만, 
	 상위 예외 클래스를 하위 예외 클래스보다 먼저 사용할 수는 없다. (컴파일 에러) 
*/
public class Exception04Main {

	
	public static void main(String[] args) {
		System.out.println("try ~ catch ~ catch ");
		
		int num1 = 123, num2 = 10, result = 0;
		String str1 = null;
		String str = "abcd";
		int[] numbers = new int[10];

		// try ~ catch ~ catch .. 사용
		try {
			result = num1 / num2;
			System.out.println("나눈 결과: " + result);
			
			int length = str1.length();
			System.out.println("스트링 길이: " + length);
			
			str.charAt(20);	// java.lang.StringIndexOutOfBoundsException
							// catch 에서 걸리는 게 없으면 프로그램은 죽는다...!
			
			numbers[10] = 11111;
			System.out.println("numbers: " + numbers[10]);
			
		} catch(ArithmeticException ex) {
			System.out.println("산술 연산 예외 : " + ex.getMessage());
			
		} catch(ArrayIndexOutOfBoundsException ex) {
			System.out.println("배열 인덱스 예외 : " + ex.getMessage());
			
		} catch(NullPointerException ex) {
			System.out.println("Null 포인터 : " + ex.getMessage());
			
		} 
			// 저 위에 있는 catch에 하나도 안걸리면 프로그램은 죽는다..!!
			// 프로그램 죽는 것을 방지하기 위해 저 위 처리되지 않은 나머지들은 너가 다 처리하라는 방법이 있다!
			// Exception....!!
			catch(Exception e) {
				// Exception은 모~든 Exception 들(상속된 예외 객체들까지도) catch 할 수 있다.
				// (다형성!)
				System.out.println("Exception 발생");
			}
		
        
		System.out.println();
		System.out.println("프로그램 종료");

	} // end main()

} // end class Exception04Main

 

[추가] catch 에서 걸리는 게 없으면 프로그램은 죽는다...!

5) com.lec.java.exception05 Exception05Main

package com.lec.java.exception05;

import java.util.InputMismatchException;
import java.util.Scanner;
/*	multi-catch
		Java 7부터 하나의 catch문에서 여러개의 예외를 처리할 수 있는 방법을 제공
		절대로 같은 상속레벨의 exception 들만 multi-catch 하기.
	 
	finally
		 예외(exception) 발생 여부와 상관없이 항상 실행되어야 할
		 코드들은 finally 블록 안에서 작성.
		 즉, finally 블록 안에 있는 코드들은 항상 실행이 됨.
		 예외가 발생하지 않을 때에는 try 블록 안의 코드들이
		 모두 실행된 후에 finally 블록의 코드들이 실행
		 예외가 발생할 때는, 해당 catch 블록의 코드들이
		 실행된 후에 finally 블록의 코드들이 실행
		 
		(주의) 
		 try 블록이나 catch 블록 안에 return이 있더라도,
		 finally 블록 안의 코드들이 다 실행된 이후에
		 return이 실행되게 됨.

		(주의)
		 try블럭, catch블럭, finally 블럭등에서 두루두루
		  사용할 변수는 try블럭 전에 선언하고, 초기화 까지 하자.
		 
		  보통은 자원반납과 같은 것들을 할때 finally 활용
		  자원 : 키보드, 파일, 데이터베이스, 네트워크 ...

 */
public class Exception05Main {

	public static void main(String[] args) {
		System.out.println("multi-catch");
		// Java 7부터 하나의 catch문에서 여러개의 예외를 처리할 수 있는 방법을 제공
		// 절대로 같은 상속레벨의 exception 들만 multi-catch 하기.
		
		
		
		// 46번째 줄 str.length(); 문장에서 에러가 발생했고
		// 발생하자마자 catch로 넘거 갔기 때문에
		// 밑에 있는 47번째 줄 int n = 123 / 0; 에러는 실행하지 않는다..!!
		// multi-catch 사용하기
		try {
			String str = null;
			str.length();
			int n = 123 / 0;
		} catch(ArithmeticException 
				| NullPointerException 
				| ArrayIndexOutOfBoundsException ex) {
			System.out.println(ex.getClass());
			System.out.println(ex.getMessage());
			
		}
		
		
		System.out.println();
		System.out.println("finally");
		

		// finally 사용하기
		// 1)
		System.out.println("\n1) finally는 예외가 발생하지 않아도 실행된다는 것을 증명");
		System.out.println("#1 try{} 직전");
		try {
			System.out.println("#2 try{} 시작");
			int[] numbers = new int[10];
			numbers[0] = 123;
			System.out.println("#3  try{} 종료");
			
		} catch(ArrayIndexOutOfBoundsException ex) {
			System.out.println("#4 catch{}");
			System.out.println("예외메시지 : " + ex.getMessage());
		} finally {
			System.out.println("#5 finally {}");
		}
		System.out.println("#6 try{} 종료후");
		
		
		// 2)
		System.out.println("\n2) finally는 예외가 발생해도 실행된다는 것을 증명");
		System.out.println("#1 try{} 직전");
		try {
			System.out.println("#2 try{} 시작");
			int[] numbers = new int[10];
			numbers[10] = 123;
			System.out.println("#3  try{} 종료");
			
		} catch(ArrayIndexOutOfBoundsException ex) {
			System.out.println("#4 catch{}");
			System.out.println("예외메시지 : " + ex.getMessage());
		} finally {
			System.out.println("#5 finally {}");
		}
		System.out.println("#6 try{} 종료후");
		
		
		// 3)
		System.out.println("\n3) finally는 실행 중간에 return이 있어도 실행된다는 것을 증명");
		System.out.println("#1 try{} 직전");
		try {
			System.out.println("#2 try{} 시작");
			int[] numbers = new int[10];
			numbers[10] = 123;
			System.out.println("#3  try{} 종료");
			
		} catch(ArrayIndexOutOfBoundsException ex) {
			System.out.println("#4 catch{}");
			System.out.println("예외메시지 : " + ex.getMessage());
			
			return;
			
		} finally {
			System.out.println("#5 finally {}");
		}
		System.out.println("#6 try{} 종료후");
		
		
		// 4)
		System.out.println("\n4) NullPointerException 에러인데..!! catch에서 해당 에러는 잡지를 못함");
		System.out.println("결국 catch에서 에러를 잡지 못했기 때문에 프로그램이 죽었다...!!");
		System.out.println("#1 try{} 직전");
		try {
			System.out.println("#2 try{} 시작");
			String str = null;  str.length();
			int[] numbers = new int[10];
			numbers[10] = 123;
			System.out.println("#3 try{} 종료");
		} catch(ArrayIndexOutOfBoundsException ex) {
			System.out.println("#4 catch{}");
			System.out.println("예외메세지 : " + ex.getMessage());
		} finally {
			System.out.println("#5 finally {}");
		}
		System.out.println("#6 try {} 종료후");
		
		
		
		// try블럭, catch블럭, finally 블럭등에서 두루두루
		// 사용할 변수는 try블럭 전에 선언하고, 초기화 까지 하자.

		// 보통은 자원반납과 같은 것들을 할때 finally 활용
		// 자원 : 키보드, 파일, 데이터베이스, 네트워크 ...

//		Scanner sc = new Scanner(System.in);
//
//		try {
//			System.out.println("정수 입력하세요");
//			sc.nextInt();
//			System.out.println("try블록 종료");
//		}catch(InputMismatchException ex) {
//			System.out.println("예외 메시지: " + ex.getMessage());
//			return;  // 설사 리턴하더라도
//		}finally {
//			System.out.println("finally 수행");
//			sc.close();  // 자원 반납하는 부분은 반드시 finally{} 에서 처리
//		}
		
		System.out.println();
		System.out.println("\n프로그램 종료...");

	} // end main

} // end class Exception05Main

 

[추가] getClass() : 객체가 속하는 클래스의 정보를 알아내는 메소드

6) com.lec.java.exception06 Exception06Main TestClass

** Exception06Main 클래스 

package com.lec.java.exception06;
/* throws
	 메소드 설계를 할 때 예외 처리를 직접 하지 않는 경우:
	 메소드 이름 뒤에 throws Exception을 추가하면,
	 예외가 발생한 경우에는 메소드를 호출한 곳으로 exception이 던져짐.
	 'Exception' 및 이를 '직접 상속받은' Exception 을 throws 하는 메소드의 경우,
	 이 메소를 호출하는 쪽에서 반.드.시 예외 처리 (handling) 해야 한다. 안하면 에러!
	 
	 
	반면 'RuntimeException' 및 이를 상속받은 예외를 throws 하는 메소드는
	굳이 호출하는 쪽에서 매번 예외 처리 할 필요는 없다
 */
public class Exception06Main {

	public static void main(String[] args)   throws Exception   {
		System.out.println("throws");
		
		System.out.println();
		TestClass test = new TestClass();
		int result = test.divide(123, 0);
		System.out.println("result = " + result);
		
		
		System.out.println();
		//test.divide2(123, 0);  	// 오류 
		// Unhandled exception type Exception
		// 아래와 같이 선언하면 사용 가능함
		try {
			test.divide2(123, 0);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
		// 내가 예외처리하고 싶지 않아요
		// 나를 호출한 곳으로 떠넘길래...!!
		// 메인 메소드에 throws Exception를 작성하면 떠넘기기 가능함!
		// main으로 떠넘겨지고...!! 
		// 메인은 JVM으로 떠넘겨지고...!!
		// 결국 프로그램은 죽는다...!! ㅜㅜㅜ
		//test.divide2(111, 0);
		//test.divide3(222, 0);
		
		
		// 반드시 try ~ catch 처리 : 2000ms 지연 
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} 	
		
		
		System.out.println("프로그램 종료...");

	} // end main()

} // end class Exception06Main

 

** TestClass 클래스

package com.lec.java.exception06;

public class TestClass {
	
	// 메소드를 설계를 할 때
	// 예외 처리를 메소드 내부에서 (try ~ catch) 로 처리
	public int divide(int x, int y) {
		int result = 0;
		try {			
			result = x / y;
		} catch(ArithmeticException e) {
			System.out.println(e.getMessage());
		}

		return result;
	} // end divide()
	
	
	// 메소드 설계를 할 때 예외 처리를 직접 하지 않는 경우:
	// 메소드 이름 뒤에 throws Exception을 추가하면,
	// 예외가 발생한 경우에는 메소드를 호출한 곳으로 exception이 던져짐.
	// Exception 및 이를 '직접 상속받은' Exception 을 throws 하는 메소드의 경우,
	// 이 메소를 호출하는 쪽에서 반.드.시 예외 처리 (handling) 해야 한다. 안하면 에러!
	
	
	// throws 사용하기
	public int divide2(int x, int y) throws Exception {
		return x / y;
	} // end divide2()
	
	
	// 반면 'RuntimeException' 및 이를 상속받은 예외를 throws 하는 메소드는
	// 굳이 호출하는 쪽에서 매번 예외 처리 할 필요는 없다
	
	// throws RuntimeException 사용하기
	public int divide3(int x, int y) throws RuntimeException {
		return x / y;
	} // end divide3()

	
} // end class TestClass

 

[추가] divide() : 예외 처리를 메소드 내부에서 (try ~ catch) 로 처리

[추가] divide2() : throws 사용하기, 메인 메소드에 throws Exception 추가해야 함!!

[추가] Thread.sleep(2000) : 반드시 try ~ catch 처리, 2000ms 지연

7) com.lec.java.exception07 패키지, Exception07Main, ScoreException 클래스
** Exception07Main 클래스

package com.lec.java.exception07;

import java.util.InputMismatchException;
import java.util.Scanner;
/* Exception 클래스 만들어 사용하기  & throw
 	Exception 또는 RuntimeException 클래스를 상속 받아서 만듬
 */
public class Exception07Main {

	static Scanner sc = new Scanner(System.in);
	
    
	// ScoreException 을 throws 하는 메소드 만들기
	public static int inputScore() throws ScoreException{
		int score = sc.nextInt();

		if(score < 0 || score > 100) {
			//ScoreException ex = new ScoreException();
			ScoreException ex = new ScoreException(score + " 값은 유효한 점수가 아닙니다.");
			throw ex;	// 예외객체 throw!
		}
		
		return score;
	} // end inputScore()
	
	
	public static void main(String[] args) {
		System.out.println("예외 클래스 만들기, throw");
		
		System.out.println();

		// ScoreException 을 catch 해보자
		int i = 0;
		while(i < 5) {
			try{
				System.out.println("국어 점수 입력:");
				int kor = inputScore();
				System.out.println("kor = " + kor);
				
//				System.out.println("영어 점수 입력:");
//				int eng = inputScore();
//				System.out.println("eng = " + eng);
				i++;
				
			} catch(ScoreException ex) {
				System.out.println(ex.getMessage());
			} catch(InputMismatchException ex) {
				System.out.println("적절하지 않는 입력값입니다.");
				sc.nextLine();	// 버퍼 제거를 하지 않으면 무한루프 돔
				
			}
			
		}

		sc.close();
			
		System.out.println("프로그램 종료");
	} // end main()

} // end class Exception07Main


** ScoreException 클래스

package com.lec.java.exception07;

// 우리가 만드는 예외 클래스
// Exception 또는 RuntimeException 클래스를 상속 받아서 만듬

// Exception 을 상속받아 예외 클래스 정의하기
public class ScoreException extends Exception {

	// 생성자
	public ScoreException() {
		super("점수 입력 오류");	// 예외 메시지 설정
								// Exception(String message) 생성자 호출
		
	}
	
	public ScoreException(String msg) {
		super(msg);
	}
	
} // end class ScoreException

 

8) com.lec.java.exception08 패키지, Exception08Main, AgeInputException 클래스
** Exception08Main 클래스

package com.lec.java.exception08;

import java.util.InputMismatchException;
import java.util.Scanner;

public class Exception08Main {

	static Scanner sc = new Scanner(System.in);
	
	
	// AgeInputException 을 throws 하는 메소드 정의
	public static int inputAge() throws AgeInputException {
		System.out.println("나이 입력:");
		int age = sc.nextInt();

		if(age < 0) {
			// 우리가 만든 예외를 발생
			// 예외 클래스의 인스턴스를 생성
			AgeInputException ex = new AgeInputException();
			
			// exception 인스턴스를 throw
			throw ex;
		}
		
		return age;
		
	} // end inputAge()
	
	
	public static void main(String[] args) {
		System.out.println("예외 클래스 만들기 2");
		
		while(true) {
			try {
				int age = inputAge();
				System.out.println("나이: " + age);
				
				break;

			} catch (AgeInputException e) {
				System.out.println(e.getMessage());
				System.out.println("다시입력하세요");
			} 
		}
		
		sc.close();
		
		System.out.println("프로그램 종료...");
		
	} // end main()

} // end class Exception08Main


** AgeInputException 클래스

package com.lec.java.exception08;

// Exceptio 상속받은 예외 클래스 만들기
public class AgeInputException extends Exception{

	// 생성자
	public AgeInputException() {
		super("나이 입력 오류");
		
	}
	
} // end class AgeInputException

 

 

5. 전화번호부 3.0 : 예외를 이용하여 에러메시지 출력하기
 : 사용자 입장에서 에러메시지 없이 튕기거나 사용이 안되는 프로그램을 사용하고 싶지 않다!!
   프로그램이 작동하지 않으면 이유를 알고 싶어한다.
   즉, 예외를 이용한 에러메시지를 구현함으로써 신뢰성 있는 소프트웨어를 만들 수 있다.

** PhonebookMain 클래스

package phonebook03.ex;

import java.util.InputMismatchException;
import java.util.Scanner;

// VIEW 객체
// 사용자와의 입출력 담당 (UI,..) 
public class PhonebookMain {

	private Scanner sc;
	private PhonebookManager pbCtrl;   // CONTROLLER 객체
	
    
	public static void main(String[] args) {
		PhonebookMain app = new PhonebookMain();
		app.init();	// 초기화
		app.run();	// 실행
		app.exit();	// 종료
	} // end main()
	
    
	// 응용프로그램을 초기화 
	public void init() {
		sc = new Scanner(System.in);
		pbCtrl = PhonebookManager.getInstance();  // CONTROLLER 생성
	}
	
	// 응용프로그램 구동
	public void run() {
		System.out.println(Pb.VERSION);
		
		int menu;
		
		while(true) {
			showMenu();  // 메뉴 표시
			
			try {
				menu = sc.nextInt();	// 메뉴 입력
				sc.nextLine();		// 버퍼 비우기
				
				switch(menu) {
				case Menu.MENU_INSERT:
					insertPhoneBook();
					break;
				case Menu.MENU_LIST:
					listPhonebook();
					break;
				case Menu.MENU_DELETE:
					deletePhonebook();
					break;
				case Menu.MENU_UPDATE:
					updatePhonebook();
					break;
				case Menu.MENU_QUIT:
					System.out.println("프로그램을 종료합니다.");
					return;
				default:	
					System.out.println("잘못 입력하셨습니다!");
				} // end switch
			} catch(PhonebookException ex) {
				System.out.println(ex.getMessage());
			} catch (InputMismatchException ex) {
				System.out.println("잘못된 입력입니다.");
				sc.nextLine();
			}
		} // end while	
	} // end run()
	
	// 응용프로그램 종료
	public void exit() {
		sc.close();
	}	
	
	// 전화번호부 입력
	public void insertPhoneBook() {
		// VIEW 역할 : 사용자 입출력
		System.out.println("-- 입력 메뉴 --");
		
		// 이름, 전화번호, 메모 입력
		System.out.print("이름 입력:");
		String name = sc.nextLine();

		System.out.print("전화번호 입력:");
		String phoneNum = sc.nextLine();
		
		System.out.print("메모 입력:");
		String memo = sc.nextLine();
		
		// CONTROLLER에 연결
		int result = pbCtrl.insert(name, phoneNum, memo);
		System.out.println(result + " 개의 전화번호 입력 성공");
	} // end insertPhoneBook()

	// 전화번호부 열람 (전체)
	public void listPhonebook() {
		// CONTROLLER 연결
		PhonebookModel[] data = pbCtrl.selectAll();
		
		// VIEW 역할 : 사용자 입출력
		System.out.println("총 " + data.length + " 명의 전화번호 출력");
		
		for(PhonebookModel e : data) {
			System.out.println(e);
		}
	} // end listPhonebook() 
	
	// 전화번호부 수정
	public void updatePhonebook() {
		// VIEW : 사용자 입출력
		System.out.println("--- 수정 메뉴 ---");
		System.out.println("수정할 번호 입력 : ");
		int uid = sc.nextInt();
		sc.nextLine();		// 버퍼 비우기
		
		if(pbCtrl.selectByUid(uid) == null) {
			System.out.println("존재하지 않는 uid : " + uid);
			return;
		}
		
		// VIEW : 이름, 전화번호, 메모 입력
		System.out.print("이름 입력:");
		String name = sc.nextLine();

		System.out.print("전화번호 입력:");
		String phoneNum = sc.nextLine();
		
		System.out.print("메모 입력:");
		String memo = sc.nextLine();
		
		// CONTROLLER에 연결
		int result = pbCtrl.updateByUid(uid, name, phoneNum, memo);
		System.out.println(result + " 개의 전화번호 수정 성공");
        
	} // end updatePhonebook()
	
	// 전화번호 삭제
	public void deletePhonebook() {
		// VIEW : 사용자 입출력
		System.out.println("--- 삭제 메뉴 ---");
		
		System.out.println("삭제할 번호 입력 : ");
		int uid = sc.nextInt();
		sc.nextLine();	// 버퍼 지우기

		// CONTROLLER
		int result = pbCtrl.deleteByUid(uid);
		System.out.println(result + " 개의 전화번호 삭제 성공");

	} // end deletePhonebook()
	
	public void showMenu() {
		System.out.println();
		System.out.println("전화번호부 프로그램");
		System.out.println("------------------");
		System.out.println(" [0] 종료");
		System.out.println(" [1] 입력");
		System.out.println(" [2] 열람");
		System.out.println(" [3] 수정");
		System.out.println(" [4] 삭제");
		System.out.println("------------------");
		System.out.print("선택: ");
	}

} // end class


** PhonebookModel 클래스

package phonebook03.ex;

import java.text.SimpleDateFormat;
import java.util.Date;

// MODEL 객체
// 데이터를 표현 객체
// 명심! 데이터를 표현하는 객체이지 저장하는 객체가 아니다!!!
public class PhonebookModel {
	// 멤버 변수
	private int uid;	// unique id
	private String name;	// 이름
	private String phoneNum;// 전화번호
	private String memo;	// 메모
	private Date regDate;	// 등록일시
	
    
	// 기본 생성자
	public PhonebookModel() {
		this.name = "";
		this.phoneNum = "";
		this.memo = "";
		this.regDate = new Date();	// 생성되는 현재시간.
	}

	//매개변수 생성자
	public PhonebookModel(String name, String phoneNum, String email) {
		this();
		this.name = name;
		this.phoneNum = phoneNum;
		this.memo = email;
	}
	
	public PhonebookModel(int uid, String name, String phoneNum, String email, Date regDate) {
		super();
		this.uid = uid;
		this.name = name;
		this.phoneNum = phoneNum;
		this.memo = email;
		this.regDate = regDate;
	}


	// getter & setter
	public int getUid() {return uid;}
	public void setUid(int uid) {this.uid = uid;}

	public String getName() {return name;}
	public void setName(String name) {this.name = name;}

	public String getPhoneNum() {return phoneNum;}
	public void setPhoneNum(String phoneNum) {this.phoneNum = phoneNum;}

	public String getMemo() {return memo;}
	public void setMemo(String memo) {this.memo = memo;}

	public Date getRegDate() {return regDate;}
	public void setRegDate(Date regDate) {this.regDate = regDate;}

	
	@Override
	public String toString() {
		String str = String.format("%3d|%s|%s|%s|%20s", uid, name, phoneNum, memo, 
					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(regDate));
		return str;
	}
	
}

 

** PhonebookManager 클래스

package phonebook03.ex;

import java.util.Arrays;

// CONTROLLER 객체
//	어플리케이션의 동작, 데이터 처리, (Business logic 담당)
public class PhonebookManager implements Pb {
	
	public static final int MAX_DATA = 3;	// 최대 데이터 개수
	
	// 배열로 구현
	private PhonebookModel[] pbList = new PhonebookModel[MAX_DATA];
	
	// 몇 개의 데이터가 저장되었는지
	private int count = 0;

	
	// singleton 적용
	private PhonebookManager() {	}
	private static PhonebookManager instance = null;
	public static PhonebookManager getInstance() {
		if(instance == null) {
			instance = new PhonebookManager();
		}
		return instance;
	} // end getInstance()
	

	// 전화번호부 생성 등록
	@Override
	public int insert(String name, String phoneNum, String memo) {
    
		// 매개변수 검증 : 이름 필수
		if(name == null || name.trim().length() == 0) {
			throw new PhonebookException("insert() 이름입력오류 : ", Pb.ERR_EMPTY_STRING);
		}
		
		// 만약 배열이 다 차있으면, MAX_DATA 만큼 추가된 새 배열 작성
		if(count == pbList.length) {
			pbList = Arrays.copyOf(pbList, pbList.length + MAX_DATA);
		}
		
		pbList[count] = new PhonebookModel(name, phoneNum, memo);
		pbList[count].setUid(getMaxUid() + 1);	// 기존의 최대 uid 값보다 1 증가한 값(unique 한 값 보장)
		// uid와 배열 index는 다르다!!
		count++;
		
		return 1;
	}

	@Override
	public PhonebookModel[] selectAll() {
		return Arrays.copyOfRange(pbList, 0, count);
	}

	// 특정 uid 의 데이터 검색 리턴
	// 못찾으면 null 리턴
	@Override
	public PhonebookModel selectByUid(int uid) {
		
		for(int index = 0; index < count; index++) {
			if(pbList[index].getUid() == uid) {
				return pbList[index];	// uid값 발견하면 리턴
			}
		}
		
		return null;	// 못찾으면 null 리턴
	} // end selectByUid()

	@Override
	public int updateByUid(int uid, String name, String phoneNum, String memo) {
		
		// 매개변수 검증
		if(uid < 1) {
			throw new PhonebookException("update() uid 오류 : " + uid, Pb.ERR_UID); 
		}
        
		// 이름 필수
		if(name == null || name.trim().length() == 0) {
			throw new PhonebookException("update() 이름입력 오류 : ", Pb.ERR_EMPTY_STRING);
		}	
		
		// 특정 uid 값을 가진 데이터의 배열 인덱스 찾기
		int index = findIndexByUid(uid);
		if(index < 0) {
			throw new PhonebookException("update() 없는 uid", Pb.ERR_UID);
		}
		
		pbList[index].setName(name);
		pbList[index].setPhoneNum(phoneNum);
		pbList[index].setMemo(memo);
		
		return 1;
	}

	@Override
	public int deleteByUid(int uid) {
		
		// 매개변수 검증
		if(uid < 1) {
			throw new PhonebookException("deleteByUid() uid 오류 : " + uid, Pb.ERR_UID); 
		}
		
		int index = findIndexByUid(uid);
		if(index < 0) {
			throw new PhonebookException("deleteByUid() 없는 uid", Pb.ERR_UID);
		}
		
		// 배열 뒤의 원소들을 앞으로 이동
		for(int i = index, j = index + 1; i < count; i++, j++) {
			pbList[i] = pbList[j];
		}
		
		// count 1 감소
		count--;
		
		return 1;
	}
	
	// 현재 데이터 중 가능 큰 uid값을 찾아서 리턴
	private int getMaxUid() {
		int maxUid = 0;
		
		for(int index = 0; index < count; index ++) {
			if(maxUid < pbList[index].getUid()) {
				maxUid = pbList[index].getUid();
			}
		}
		return maxUid;
	}

	// 특정 uid 값을 가진 데이터의 배열 인덱스 찾기
	// 못찾으면 -1 리턴
	private int findIndexByUid(int uid) {
		for(int index = 0; index < count; index++) {
			if(pbList[index].getUid() == uid) {
				return index;
			}
		}
		return -1;
	}
	
} // end PhonebookManager

// 예외 클래스 정의
// 예외발생하면 '에러 코드' + '에러 메시지'를 부여하여 관리하는게 좋다.
class PhonebookException extends RuntimeException{
	
	private int errCode = Pb.ERR_GENERIC;
	
	// 생성자 만들기
	public PhonebookException() {
		super("Phonebook 예외 발생");
	}
	
	public PhonebookException(String msg) {
		super(msg);
	}
	
	public PhonebookException(String msg, int errCode) {
		super(msg);
		this.errCode = errCode;
	}
	
	
	// Throwable 의 getMessage 를 오버라이딩 가능
	@Override
	public String getMessage() {
		String msg = "ERR-" + errCode + "]" +Pb.ERR_STR[errCode] 
				+ " " + super.getMessage();
		return msg;
	} 
	
} // PhonebookException

 

** PhonebookController 인터페이스 -> Pb로 이름 변경 

package phonebook03.ex;

// Controller 인터페이스
//	동작 정의하기 전에 '동작 설계'
public interface Pb {
	
	public static final String VERSION = "전화번호부 3.0";
	
	// 만들고자 하는 응용프로그램의 
	// '동작' 을 설계하는 것이 인터페이스다.
	// 이 인터페이스는  View 와 Controller 의 연결고리 역할을 하여.  모듈단위 유지관리를 용이하게 해준다. 
	
	// [동작 설계]
	// 1. 이름(name)과 전화번호(phoneNum)과 (memo) 값이 주어지면 전화번호 데이터(PhonebookModel)를   
	//		생성하여 저장, 날짜(regDate) 는 생성한 날짜로, uid 값은 자동 증가 값으로 
	//		성공하면 1, 실패하면 0 리턴
	// 2. 현재 전화번호부에 저장된 전화번호 데이터(PhonebookModel)들을 
	//		전부 불러들여서 리턴(PhonebookModel 배열로)
	// 3. 특정 uid 값을 가진 전화번호 데이터(PhonebookModel)을 찾아서 
	//		리턴, 없으면 null 리턴
	// 4. 특정 uid 값을 가진 전화번호 데이터(PhonebookModel)을 찾아서 
	//		주어진 이름(name) 과 전화번호(phoneNum) 과 (memo) 값 으로 변경.  
	//		성공하면 1, 실패하면 0 리턴
	// 5. 특정 uid 값을 가진 전화번호 데이터(PhonebookModel) 을 찾아서 삭제.  
	//		성공하면 1, 실패하면 0 리턴
	
	
	public abstract int insert(String name, String phoneNum, String memo);	/* 1 */
	public abstract PhonebookModel[] selectAll();	/* 2 */
	public abstract PhonebookModel selectByUid(int uid);	/* 3 */
	public abstract int updateByUid(int uid, String name, String phoneNum, String meno);	/* 4 */
	public abstract int deleteByUid(int uid);	/* 5 */

	
	//public static final int QUERY_FAIL = 0;
	
	
	// 에러코드 상수
	public static final int ERR_GENERIC = 0;		// 일반 오류
	public static final int ERR_INDEXOUTOFRANGE = 1;	// 인덱스 범위 벗어남
	public static final int ERR_EMPTY_STRING = 2;		// 입력문자열이 empty (혹은 null)인 경우
	public static final int ERR_UID = 3;			// uid가 없는경우
	
	// 에러문자열
	public static final String[] ERR_STR = {
			"일반오류",	// 0
			"인덱스오류",	// 1
			"문자열오류",	// 2
			"UID 오류"	// 3
	};
	
}


** Menu 인터페이스

package phonebook03.ex;

public interface Menu {
	public static final int MENU_QUIT = 0;
	public static final int MENU_INSERT = 1;
	public static final int MENU_LIST = 2;
	public static final int MENU_UPDATE = 3;
	public static final int MENU_DELETE = 4;
}

'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글

2020.03.27  (0) 2020.03.27
2020.03.26  (0) 2020.03.26
2020.03.24  (0) 2020.03.24
2020.03.23  (0) 2020.03.23
2020.03.20  (0) 2020.03.20