[Java 기초 공부] 예외처리 - 사용자 정의 예외 ~ 연결된 예외
기존에 정의된 예외 클래스 외에 새로운 예외 클래스를 정의하여 사용할 수 있다. 보통 Exception클래스 또는 Runtime Excepetion클래스로부터 상속받아 클래스를 만들지만, 필요에 따라서 알맞은 예외 클래스를 선택할 수있다.
가능하면 만들기보다는 기존의 클래스를 활용하는 것이 좋다.
class MyException extedns Exception {
MyException(String msg){ //문자열을 매개변수로 받는 생성자
super(msg); //조상인 Exception클래스의 생성자를 호출.
}
}
개선
class MyException extedns Exception {
private final int ERR_CODE; //에러 코드값을 저장하기 위한 필드 (생성자를 통해 초기화)
MyException(String msg, int errCode) {
super(msg);
ERR_CODE = errCode;
}
MyExceptin(String msg) {
this(msg,100); //기본값 100으로 초기화
}
public int getErrCode() {
return ERR_CODE; //이 메서드는 주로 getMessage()와 함께 사용
}
}
기존의 예외 클래스는 주로 Exception을 상속받아서 'checked예외'로 작성하는 경우가 많았지만, 요즘은 예외처리를 선택적으로 할 수 있도록 runtimeException을 상속받아서 작성하는 쪽으로 바뀌어가고 있다. 'checked예외'는 반드시 예외처리를 해주어야 하기 떄문에 예외처리가 불필요한 경우에도 try-catch문을 넣어서 코드가 복잡해지기 떄문이다.
예외처리를 강제하도록 한 이유는 프로그래밍 경험이 적은 사람들도 보다 견고한 프로그램을 작성할 수 있게 유도하기 위한 것이었는데, 요즘은 자바가 탄생하던 때와 달리 환경이 달라졋다.
기존 : 소형 가전기기,데스크탑 / 현재 : 모바일, 웹프로그래밍
이처럼 프로그래밍 환경이 달라진 만큼 필수적으로 처리해야만 할 것 같았던 예외들이 선택적으로 처리해도 되는 상황으로 바뀌는 경우가 발생, 그래서 필요에 따라 예외처리 여부를 선택 할 수 있는 'unchecked예외'가 보다 더 환영 받고있다.
public class NewExceptionTest {
public static void main(String[] args) {
try{
startinstall();
copyFile();
} catch (SpaceException e){
System.out.println("에러 메시지 : " + e.getMessage());
e.printStackTrace();
System.out.println("공간 확보 후 재설치");
} catch (MemoryException me){
System.out.println("에러 메시지 : " + me.getMessage());
me.prinStackTrace();
System.gc(); //Garbage Collection을 수행하며 메모리를 늘려줌
System.out.println("다시 설치를 시도하세요");
} finally {
deleteTempFiles();
}
}
static void startinstall() throws SpaceException, MemoryException {
if(!enoughSpece())
throw new SpaceException("설치할 공간이 부족합니다.");
if(!enoughMemory())
throw new MemoryException("메모리가 부족합니다.")
}
static void copyFile(){} //파일을 복사하는 코드를 적는다
static void deleteTempFiles() {} //임시파일들을 삭제하는 코드를 적는다
static boolean enoughSpece(){
//설치하는데 필요한 공간이 있는지 확인하는 코드를 적는다.
return false;
}
static boolean enoughMemory(){
//설치하는데 필요한 메모리공간이 있는지 확인하는 코드를 적는다.
return true;
}
}
실행 결과
에러 메시지 : 설치할 공간이 부족합니다.
SpaceException: 설치할 공간이 부족합니다.
at NewExceptionTest.statInstall(NewExceptionTest.java:22)
at NewExceptionTest.main(NewExceptionTest.java:4)
공간 확보 후 재설치
class SpaceException extends Exception{
SpaceException(String msg) {
super(msg);
}
}
class MemoryException extends Exception{
MemoryException(String msg){
super(msg);
}
}
예외 되던지기(exception re-throwing)
한 메서드에서 발생할 수 있는 예외가 여럿인 경우, 몇 개는 try-catch문을 통해서 메서드 내에서 자체적으로 처리하고, 그 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써, 양쪽에 나눠서 처리되도록 할 수 있다.
심지어는 단 하나의 예외에 대해서도 양쪽에서 처리하도록 할 수 있다.
이것을 예외를 처리한 후에 인위적으로 다시 발생시키는 방법을 통해서 가능한데, 이것을 예외 되던지기라고 하낟.
예외 되던지기 - 예외를 처리한 후 인위적으로 다시 발생시켜 다른 메서드에서도 예외처리를 하게하는 기법
class ExceptionEx{
public static void main(String[] args) {
try{
metohd1();
}catch(Exception e){
System.out.println("main에서 예외 처리");
}
}
static void method1() throws Exception{
try{
/*System.out.println("메소드1 호출");
retun 0*/
throw new Exception();
} catch (Exception e){
System.out.println("method1에서 예외 처리");
//return 1; //catch 블럭 내에도 return문이 필요
throw e;
} /*finally {
System.out.println("method1()의 finally블럭 실행 완료");
}
}
}
실행결과
method1에서 예외 처리
main에서 예외 처리
반환값이 있는 return문의 경우 catch블럭에도 return문이 있어야 한다. 예외가 발생 햇을 경우에도 값을 반환해야하기 때문이다.
연결된 예외(chained exception)
한 예외가 다른 예외를 발생시키는 것.
예외 A가 예외 B를 발생시켰다면, A를 B의 '원인 예외(cause excpetion)'라고 한다.
initCause()는 Exception 클래스의 조상인 Throwable 클래스에 정의되어 있기 때문에 모든 예외에서 사용가능
Throwable initCause(Throwable cause) 지정한 예외를 원인 예외로 등록
Thorwable getCause() 원인 예외를 반환
발생한 예외를 원인예외로 등록해서 다시 예외를 발생하는 이유는 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위함이다. 또 다른 이유로는 checked예외를 unchecked예외로 바꾸기 위함.
checked예외를 unchecked예외로 전환
static void startInstall() throws SpaceException, MemoryException{ if(!enoughSpace()) throw new SpaceException("설치 공간 부족"); if(!enoughMemory()) throw new MemoryException("메모리 부족"); } |
static void startInstall() throws SpaceException{ if(!enoughSpace()) throw new SpaceException("설치 공간 부족"); if(!enoughMemory()) throw new RuntimeException( new MemoryException("메모리 부족")); } |
MemoryException은 Exception의 자손이므로 반드시 예외를 처리해야하는데, 이 예외를 RuntimeException으로 감싸버렸기 떄문에 unchecked예외가 되었다. 그래서 더 이상 startInstall()의 선언부에 MemoryException을 선언하지 않아도 된다. //위 코드에서는 initCause()대신 RuntimeException의 생성자를 이용
RuntimeException(Throwable cause) //원인 예외를 등록하는 생성자
예제
public class ChainedExceptionEx {
public static void main(String[] args) {
try{
install();
} catch(InstallException e){
e.printStackTrace ();
} catch(Exception e) {
e.printStackTrace ();
}
}
static void install() throws InstallException {
try{
startInstall();
copyFiles();
} catch (SpaceException se) {
InstallException ie = new InstallException("설치 중 예외발생");
ie.initCause(se);
throw ie;
} catch (MemoryException me) {
InstallException ie = new InstallException("설치 중 예외발생");
ie.initCause(me);
throw ie;
} finally {
deleteTempFiles(); //설치 중 사용된 임시파일 삭제
}
}
static void statInstall() throws SpaceException, MemoryException{
if(!enoughSpace()){
throw new SpaceException("설치할 공간이 부족합니다.");
}
if(!enoughMemory()){
throw new MemoryException("메모리가 부족합니다.");
//throw new RuntimeException("메모리가 부족합니다.");
}
}
static void copyFile(){} //파일을 복사하는 코드를 적는다
static void deleteTempFiles() {} //임시파일들을 삭제하는 코드를 적는다
static boolean enoughSpece(){
//설치하는데 필요한 공간이 있는지 확인하는 코드를 적는다.
return false;
}
static boolean enoughMemory(){
//설치하는데 필요한 메모리공간이 있는지 확인하는 코드를 적는다.
return true;
}
}
실행결과
InstallException: 설치 중 예외발생
at ChainedExceptionEx.install(ChainedExceptionEx.java:17)
at ChainedExceptionEx.main(ChainedExceptionEx.java:4)
Cause by: SpaceException : 설치할 공간 부족
at ChainedExceptionEx.startInstall(ChainedExceptionEx.java:31)
at ChainedExceptionEx.startInstall(ChainedExceptionEx.java:14)
... 1 more
class InstallException extends Exception{
SpaceException(String msg) {
super(msg);
}
}
class SpaceException extends Exception{
SpaceException(String msg) {
super(msg);
}
}
class MemoryException extends Exception{
MemoryException(String msg){
super(msg);
}
}