사용자 정의 예외(CustomException)
사용자가 직접 예외 클래스를 정의할 수 있다.
- 사용자 정의 예외는 JVM에서 예외를 발생시켜 주지 않기 때문에 직접 예외를 생성해야 함
- 사용자 정의 예외 클래스 이름은 Exception으로 끝나는 것이 좋음
- 조상은 Exception과 RuntimeException중에서 선택함
-> Exception은 필수처리 예외라서 try-catch문을 꼭 사용해야 하기 때문에 가급적으로 선택처리인 RuntimeException을 선택!!(필요한 경우에만 필수처리로!)
- 사용자 정의 예외 클래스도 필드, 생성자, 메서드 선언을 포함할 수 있지만, 대부분 생성자 선언만 포함!
- 생성자는 예외 발생 원인(예외 메시지)를 전달하기 위해 String타입의 매개변수를 갖는다!
- 예외 메시지의 용도 -> catch{ } 블록의 예외처리코드에서 이용하기 위해서
class MyException extends Exception { // Exception 선택
MyException(String msg) { // 문자열을 매개변수로 받는 생성자
super(msg); // 조상인 Exception클래스의 생성자를 호출한다 == Exception(String msg)
}
}
-> Exception클래스로부터 상속받아서 MyException클래스를 생성! 필요하면 멤버변수나 메서드를 추가할 수 있음
class MyException extends RuntimeException { // RuntimeException 선택
MyException(String msg) { // 문자열을 매개변수로 받는 생성자
super(msg); // 조상인 Exception클래스의 생성자를 호출한다
}
}
예제1) 은행 예금보다 더 많은 금액을 인출하려고 하는 경우, 예외 문구를 사용자에게 출력
public class CustomException {
public static void main(String[] args) {
Account account = new Account(20000); // Account 객체 생성
System.out.println("이 계좌의 주인은 " + account.getName() + "씨입니다.");
System.out.println("이 계좌의 잔액은 " + account.currentMoney + "원 입니다.");
try{
account.withdraw(19000); // 출금하려는 가격 (withdraw 메서드 호출)
}catch(BankingException e) { // 예외처리!!
System.out.println(e.getMessage());
}
}
}
class BankingException extends Exception { // BankingException 클래스 정의
BankingException() {} // 생성자 정의(매개변수 없는)
public BankingException(String msg) { // 메시지를 넘기기 위해서 String으로 매개변수를
super(msg);
}
}
class Account { // Account 클래스 생성
private String name = "이지향"; // name 인스턴스 변수를 private으로
int currentMoney; // 현재 금액
public void setName(String name) { // setName으로 이름 가져오기
this.name = name;
}
public String getName() { // getName으로 이름 반환하기
return name;
}
Account(int current) { // 매개변수가 있는 Account의 생성자
this.setName(name);
this.currentMoney = current;
}
void withdraw(int money) throws BankingException {
if(money>currentMoney) // 츨금 금액이 원금보다 크면
throw new BankingException("잔액이 부족합니다."); // 예외발생!
else // 출금 금액이 원금보다 작으면
currentMoney -= money; // 원금에서 출금 금액 빼고
System.out.println("현재 남은 금액: " + currentMoney + "원"); // 현재 남은 금액 출력
}
}
예외 되던지기(exception re-throwing)
- 예외를 처리한 후에 다시 예외를 발생시키는 것(예외를 처리하고 다시 예외를 발생시킴)
- 호출한 메서드와 호출된 메서드 양쪽 모두에서 예외처리하는 것
예제) 호출한 main메서드와 호출 받은 method1 모두 예외처리하는 프로그램
public class ExceptionEx15 {
public static void main(String[] args) {
try {
method1(); // method1 호출
} catch (Exception e) { // throw e 에서 넘어온 예외를 처리
System.out.println("main에서 예외처리 되었습니다.");
}
} // main메서드의 끝
static void method1() throws Exception { //예외선언 때문에 throws Exception 추가!
try {
throw new Exception(); // 예외 발생
} catch (Exception e) {
System.out.println("method1에서 예외처리 되었습니다.");
throw e; // 다시 예외를 발생시킨다.
}
} // method1의 끝
} // class의 끝
예외 처리 방법
1) 호출 받은 쪽에서(method1) try-catch구문으로 처리하는 방법
2) 호출한 쪽에서(main) try-catch구문으로 처리하는 방법
3) 양쪽에서 try-catch구문으로 처리하는 방법
연결된 예외(chained exception)
- 한 예외가 다른 예외를 발생시킬 수 있다.
- 예외 A가 예외 B를 발생시키면, A는 B의 원인 예외(cause exception)
- 연결된예외: 어떤 예외를 다른 예외로 감싸는 것(세부적인 예외들을 포괄적인 예외들로 감싼다)
Throwable initCause(Throwable cause) | 지정한 예외를 원인 예외로 등록 |
Throwable getCause( ) | 원인 예외를 반환 |
-> Throwable cause에 원인 예외 A를 넣음
-> initCause메서드는 Exception클래스와 RuntimeException의 조상인 ThrowAble클래스에 정의되어 있어서 모든 예외에서 사용가능
예제) SpaceException(A)을 원인 예외로 하는 InstallException(B)을 발생시키는 방법
void install() throws InstallException {
try {
starInstall(); // SpaceException 발생(저장공간부족)
copyFiles();
} catch (SpaceException e ) {
InstallException ie = new InstallException("설치중 예외발생") // 예외생성
ie.initCasue(e) // InstallException의 원인예외를 SpaceExceptionm으로 지정
throw.ie; // InstallException을 발생시킨다.
} catch (MemoryException me) {
...
} ...
- SpaceException e -> 예외 A, InstallException -> 예외 B
- initCause 메서드를 사용해서 A를 B에 포함!
-> 먼저 InstallException을 생성(1)한 후에, initCause()로 SpaceException(A)를 InstallException(B)의 원인예외로 등록(2)하고 throw로 이 예외(InstallException)를 던진다(3)!!!!
-> 생성된 예외가 InstallException이어서 메서드 선언부에 SpaceException이 아닌 InstallException이 옴!
(1) InstallException ie = new InstallException( );
(2) ie.initCause(e)
(3) throw.ie;
[연결된 예외를 사용하는 이유1] 여러 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해서
- 프로그램을 설치하다가 SpaceException 예외가 발생되어서 결과 -> 설치할 공간이 부족합니다라는 에러메세지가 뜸!
- 하지만 설치하다가 발생할 수 있는 에러가 더 생길 수도 있음!(MemoryException이나 SpaceException 말고도..!
-> 그러면 install이라는 메서드를 쓸 때 마다 Catch 블록이 너무 많이 사용하게 됨
-> 그럴 때 연결된 예외를 사용해서 많은 에러를 => InstallException 하나로 줄여서 다룰 수 있음!
-> 이 코드처럼 install( )메서드 안으로 Catch블록들(발생할 예외들)을 집어넣으면 됨
-> 그리고 startInstall( ); 이 시작되면서 예외가 발생하게 되면 그 예외를 InstallException안에 넣어주게 됨
-> 세부적인 예외처리는 install( )메서드 안에 감출 수 있고 InstallException만 던지도록 선언!(그래서 throws 옆에 InstallExcepton)
-> install( )내부에서는 예외처리를 한 게 아니라 두 예외를 연결하는 작업을 함
- install( ); 메서드를 호출했을 때 SpaceException이나 MemoryException이 아니라 InstallException이 발생하게 됨!
- install( ) 선언부의 InstallException을 catch 블록에 넣으면 됨! catch(InstallException e)
- 결과: 발생한 예외인 InstallException으로 대략정보(설치 중 예외발생)와 원인 예외(실제로 발생한 예외) 인 SpaceException으로 세부정보(설치할 공간이 부족합니다)를 알 수 있다!
[이유2] checked예외를 unchecked예외로 변경하려 할 때(필수처리 -> 선택처리)
checked예외: Exception클래스 자손, 필수처리 / unchecked예외: RuntimeException클래스, 선택처리
- 필수예외를 선택예외로 바꾸는 이유? 필수처리예외는 try-catch문을 꼭 써야하기 때문에 제약이 생김
static void StartInstall() throws SpaceException, MemmoryException {
if(!enoughSpace()) // 충분한 설치공간이 없으면..
throw new SpaceException("설치할 공간이 부족합니다.");
if(!enoughMemory()) // 충분한 메모리가 없으면..
throw new MemoryException("메모리가 부족합니다.");
-> spaceException과 MemoryException 둘다 필수예외처리!
-> 만약 MemoryException을 선택처리로 바꾸고 싶다면 위 코드를 아래코드로 변경
static void StartInstall() throws SpaceException {
if(!enoughSpace()) // 충분한 설치공간이 없으면..
throw new SpaceException("설치할 공간이 부족합니다.");
if(!enoughMemory()) // 충분한 메모리가 없으면..
throw new RuntimeException(new MemoryException("메모리가 부족합니다."));
}
-> MemoryException은 Exception의 자손이므로 반드시 예외처리를 해야하는데 이것을 RuntimeException으로 감싸버려서 unchecked예외(선택처리)가 되었다. 그래서 startInstall( )선언부에 MemoryException을 선언하지 않아도 된다.!
-> 원래 선언부에 Space와 Memory가 있었지만 Memory가 필수에서 선택으로 바뀌게 되면서 선언부에 Memory를 생략!
-> RuntimeException을 만들고 안에다가 집어넣으면 됨! 필수예외를 선택예외로 감싸는 것
-> RuntimeException으로 감싸면서 RuntimeException인 것처럼 위장하기!
-> MemoryException을 원인 예외로 등록!(앞예제에서는 initCause로 원인예외로 등록했고 지금은 생성자로 원인예외로 등록함!)
'멀티캠퍼스 풀스택 과정 > Java의 정석' 카테고리의 다른 글
자바의 정석6-2 String 클래스, Stringbuffer(StringBuilder) (0) | 2022.01.07 |
---|---|
자바의 정석6-1 Java.lang패키지, eqauls, hasecode, toString (0) | 2022.01.07 |
자바의 정석5-2 예외 발생, 예외 처리, Finally블럭 (0) | 2022.01.06 |
자바의 정석5-1 예외처리(exception handling) (0) | 2022.01.06 |
자바의 정석4-3 내부클래스와 익명클래스 (0) | 2022.01.06 |