자바의 정석 8장 (22일차) - 사용자 정의 예외처리 & 예외 되던지기 & 연결된 예외 처리(Chained Exception)

728x90

사용자 정의 예외처리 

요즘 유행하는 비트코인을 가져와서 간단한 사용자 정의 예외처리 예시를 만들었다.

 

입금된 비트코인 잔고보다 인출되는 비트코인 잔고가 더 많으면 throws로 지정된 오류 클래스로 보내기로 했고

오류 클래스에서 지정된 연산 수행 후에 toString 클래스로 오류전용 지정 문구를 출력하도록 만들었다.

class CoinAccount extends BalanceInsufficientException{
	private double BTCaccount;
	private double ETHaccount;
	
	public CoinAccount(double bTCaccount, double eTHaccount) {
		super();
		BTCaccount = bTCaccount;
		ETHaccount = eTHaccount;
	}
	public CoinAccount() {
	}
	public void BTCdeposit(double BTC) {
		BTCaccount+=BTC;
	}
	public void BTCwithdraw(double BTC, Bank b) throws BalanceInsufficientException{
		if(BTCaccount<BTC) {
			throw new BalanceInsufficientException(BTC - BTCaccount);
		}
		else {
			BTCaccount -= BTC;
			b.setBTCBank(BTC);
			System.out.println("정상적으로 인출되었습니다. 현재잔고 : " + BTCaccount);
		}
	}
	
	public void ETHdeposit(double ETH) {
		ETHaccount+=ETH;
	}
	
	public double getBTCaccount() {
		return BTCaccount;
	}
	public double getETHaccount() {
		return ETHaccount;
	}

	public String toString() {
		return "코인잔고  BTC: " + BTCaccount + " ETH: " + ETHaccount;
	}
}


class BalanceInsufficientException extends RuntimeException{
	double differ;
	
	public BalanceInsufficientException() {
		super(); // RuntimException객체의 생성자를 호출한다
	}
	
	public BalanceInsufficientException(double differ) {
		super(); // RuntimException객체의 생성자를 호출한다
		this.differ = differ;
	}

	@Override
	public String toString() {
		return "현재 잔고가 " + differ + "만큼 부족하여 인출할 수 없습니다";
	}
}

 

아래의 main클래스를 예외활용하면 아래와 같은 문구가 출력된다.

 

출력문:

현재 잔고가 0.001만큼 부족하여 인출할 수 없습니다

	public static void main(String[] args) {
		
		CoinAccount ca = new CoinAccount();
		try {
			ca.BTCdeposit(0.004);
			
			ca.BTCwithdraw(0.005);
//		코인 withdraw을 할 때 잔고가 부족하면 오류를 발생시켜 프로그램 종료
		} 	
		catch (BalanceInsufficientException e) {
			System.out.println(e);
		}
	}
}

예외 되던지기

예외를 처리한 후에 다시 예외를 발생시키는 행동

호출한 메서드와 호출받은 메서드에서 예외를 두번 처리한다.

 

예시로 아래의 코드를 참조하자.

  • 아빠가 딸에게 심부름을 시켰지만 딸이 돈이 없는 경우 try에서 새로운 에러를 생성하고 catch에서 생성된 오류를 다시 집어던진다.
  • 아빠가 받은 에러를 다시 재처리하여 출력한다.
  • 아래는 아래코드의 최종 출력문

 

출력문:

아빠가 딸에게 심부름을 시킴
아빠의 심부름, 하지만 돈이 없음
결국 아빠가 딸 심부름 대신 처리

public class ReThrow {
	public static void main(String[] args) throws Exception{
		System.out.println("아빠가 딸에게 심부름을 시킴");
		father();
	}
	static void father() throws Exception{
		try {
			daughter(); //딸 메서드를 호출
		}
		catch (Exception e) {
			System.out.println("결국 아빠가 딸 심부름 대신 처리");
//			다시 되돌려 받은 에러를 아빠메서드에서 대신 처리
		}
	}
	static void daughter() throws Exception{
		try {
			System.out.println("아빠의 심부름, 하지만 돈이 없음");
			throw new Exception(); //호출받았지만 돈이 없는 관계로 새로운 exception오류 생성
		}
		catch (Exception e) {
			throw e; //만들어진 오류를 다시 호출한 메서드(father)로 집어던짐
		}
	}
}

연결된 예외(Chained Exception)

한 예외가(A) 다른 예외(B)를 발생시킬 수 있다

A예외가 B예외를 발생시키면 A는 B의 원인예외 (Cause Exception)이라고 부른다.

두개의 예외를 연결시키는 것이 연결된 예외이다.

 

 

 

사용목적의 이유는 아래와 같다

1. 여러 예외를 하나도 묶어 다루기 위해서

  • 여러의 catch블럭으로 일일이 써주면 코드의 가독성이 떨어져서 하나의 예외로 묶은 후에 예외를 한번에 처리한다

 

2. Checked Exception(필수처리)을 Unchecked Exception(선택처리)으로 변경하기 위해서

  • 어떤 필수처리(Exception의 자손 클래스)를 선택처리로 변경할 때 RuntimeException으로 변경하면 되지만, 만약 오류 클래스들이 다른 클래스에 많이 사용되고 있다면 일일이 변경하기가 매우 번거로울 수 있다.
  • 따라서 RuntimeException(선택처리) 오류클래스를 만들고 'initCause()' 함수를 사용하여 필수처리의 예외를 선택처리 오류 클래스에 집어넣으면 일일이 변경하지 않고 필수처리 오류들을 선택처리로 한번에 위장변경 시킬 수 있다. 

아래의 예시는 만약 편의점에 심부름을 간다고 할 경우, 편의점이 존재하는가 혹은 차가 있는가를 예외처리하여 연결된 예외로 묶어준 경우이다.

  • 기본 멤버변수를 차, 편의점을 boolean 타입으로 준 후 false면 AllErrorException으로 한번에 묶어 출력하도록 하였다.
  • 오류를 묶을 때 initCause를 사용해 오류가 발생할 시에 AllErrorException으로 통합시켜 다시 throw로 재돌려주는 형식

출력문:

차타고 출발
Lecture.AllErrorException: 편의점의 존재여부
at Lecture.daughter.errand(CauseException.java:53)
at Lecture.CauseException.main(CauseException.java:19)
Caused by: Lecture.ExistException: 주변에 편의점이 없습니다
at Lecture.daughter.gotoConvStore(CauseException.java:68)
at Lecture.daughter.errand(CauseException.java:43)
... 1 more

public class CauseException {
	public static void main(String[] args){
		daughter d = new daughter();
		Mart m = new Mart();
		
		try {
			d.errand();
		}
		catch (AllErrorException e) {
			e.printStackTrace();
		}
	}
}

class daughter{
	Boolean Car = true;
	Boolean ConvStore = false;
	
	public daughter() {
	}

	public daughter(int fathermoney) {
		this.money = fathermoney;
	}
	
	void errand() throws AllErrorException{
		try {
			gobyCar();
			gotoConvStore();
		}
		catch (CarErrorException e) {
//			allerrorexception 클래스에 통합시켜주기위해 isitcause를 사용하여 오류 묶어 실행
//			각각의 다른 오류들을 카테고리화 시켜 통합했다.
			AllErrorException ea = new AllErrorException("이동수단 없음");
			ea.initCause(e);
			throw ea;
		}
		catch (ExistException e) {
			AllErrorException ea = new AllErrorException("편의점의 존재여부");
			ea.initCause(e);
			throw ea;
		}
	}
	void gobyCar() throws CarErrorException{
		if(!Car) {
			throw new CarErrorException("차가 없어서 못갑니다");
		}
		else
			System.out.println("차타고 출발");
	}
	void gotoConvStore() throws ExistException{
		if(!ConvStore) {
			throw new ExistException("주변에 편의점이 없습니다");
		}
		else
			System.out.println("편의점 출발");
	}


class AllErrorException extends Exception{
	public AllErrorException(String msg) {
		super(msg);
	}
}
class CarErrorException extends Exception{
	public CarErrorException(String msg) {
		super(msg);
	}
}
class ExistException extends Exception{
	public ExistException (String msg) {
		super(msg);
	}
}
728x90