[JAVA] 예외처리 - try-catch문, 예외 선언, 예외 되던지기

예외처리(exception handling)


프로그램 오류

  1. 컴파일 에러 : 컴파일할 때 발생하는 에러. 프로그램 실행 안됨
  2. 런타임 에러 : 문법에는 맞지만 실행 중 발생하는 에러. 프로그램 종료됨
  3. 논리적 에러 : 작성 의도와 다르게 동작. 프로그램은 실행됨

 

  • 런타임 에러
에러는 어쩔 수 없지만 예외는 처리하자

 - 에러 : 코드에 의해 수습될 수 없는 심각한 오류(OOME:Out Of Memory Error)
 - 예외 : 코드에 의해 수습될 수 있는 미약한 오류(Exception, RuntimeException 등)

 

  • 예외

- Exception예외 클래스들(checked예외) : 컴파일러가 예외 처리 여부 체크. 예외 처리 필수

- RuntimeException예외 클래스들(unchecked예외) : 컴파일러가 예외 처리 여부 체크 안함. 예외 처리 선택

 

 

 

예외 처리 방법1 - try-catch문

: 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성

: 프로그램의 비정상 종료를 막기 위해 사용

: 중괄호 생략 불가

: catch 블럭 내에 try-catch문이 포함된 경우, 같은 이름의 참조변수 사용 불가

Exception은 모든 예외의 최고 조상이므로, 모든 예외 처리 가능
-> 맨 마지막에 와야 함
try {
	// 예외가 발생할 가능성이 있는 문장들
} catch (Exception e1) {
	// Exception1이 발생했을 경우, 이를 처리하기 위한 문장들
} catch (Exception e2) {
	// Exception2이 발생했을 경우, 이를 처리하기 위한 문장들
} catch (Exception e3) {
	// Exception3이 발생했을 경우, 이를 처리하기 위한 문장들
}

 

 

 

try-catch문에서의 흐름

  • try블럭 내에서 예외가 발생한 경우

1. 발생한 예외와 일치하는 catch블럭 찾기

2-1. catch블럭 찾으면, catch블럭 수행하고 전체 try-catch문 탈출

2-2. catch블럭 찾지 못하면, 예외 처리 불가 -> 프로그램 비정상 종료

public class ExceptionEx4 {
	public static void main(String[] args) {
		System.out.println(1);
		System.out.println(2);
		try {
			System.out.println(3);
			System.out.println(0/0);
			System.out.println(4); // 실행되지 않음
		} catch (ArithmeticException ae) {
			System.out.println(5);
		}
		System.out.println(6);
	}
}
1
2
3
5
6

 

  •  try블럭 내에서 예외가 발생하지 않은 경우

- catch블럭 거치지 않고 전체 try-catch문 탈출

public class ExceptionEx5 {
	public static void main(String[] args) {
		System.out.println(1);
		System.out.println(2);
		try {
			System.out.println(3);
			System.out.println(4);
		} catch (Exception e) {
			System.out.println(5); // 실행되지 않음
		}
		System.out.println(6);
	}
}
1
2
3
4
6

 

 

 

예외의 발생과 catch블럭

  • void printStackTrace()

: 예외발생 당시의 호출스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력

 

  • String getMessage()

: 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있음

public class ExceptionEx8 {
	public static void main(String[] args) {
		System.out.println(1);
		System.out.println(2);
		try {
			System.out.println(3);
			System.out.println(0/0); // ArithmeticException 발생
			System.out.println(4); // 실행 안됨
		} catch (ArithmeticException ae) {
			ae.printStackTrace();
			System.out.println("예외메시지 : "+ae.getMessage());
		}
		System.out.println(6);
	}
}
1
2
3
java.lang.ArithmeticException: / by zero
at ExceptionEx8.main(ExceptionEx8.java:9)  // printStackTrace() 실행
예외메시지 : / by zero  // getMessage() 실행
6

 

  • 멀티 catch블럭(jdk 1.7부터)

: 내용이 같은 catch블럭을 하나로 합친 것

: 멀티 catch블럭의 예외가 조상-자손 관계라면 컴파일 에러

: 멀티 catch블럭 중 하나에서만 선언된 메서드 호출 불가

더보기

try {

    ...

} catch (ExceptionA e) {

    e.printStackTrace();

} catch(ExceptionB e2) {

    e2.printStackTrace();

}

try {
	...
} catch(ExceptionA | ExceptionB e) {
	e.printStackTrace();
}

 

 

 

예외 발생시키기

1. 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체 생성

Exception e = new Exception("고의로 발생시킴");

2. 키워드 throw를 이용해서 예외 발생시킴

throw e;
public class ExceptionEx9 {

	public static void main(String[] args) {
		try {
			Exception e = new Exception("고의로 발생시킴");
			throw e;
			// throw new Exception("고의로 발생시킴");
		} catch (Exception e) {
			System.out.println("에러 메시지 : "+e.getMessage());
			e.printStackTrace();
		}
		
		System.out.println("프로그램이 정상 종료됨");
	}
}
에러 메시지 : 고의로 발생시킴
java.lang.Exception: 고의로 발생시킴
at ExceptionEx10.main(ExceptionEx10.java:6)
프로그램이 정상 종료됨

 

 

 

예외 처리 방법2 - 메서드에 예외 선언하기

: 메서드 호출 시 발생가능한 예외를 호출하는 쪽에게 떠넘기기(알리기)

: checked 예외만 적는 것이 정석

: 예외 선언만으로 예외가 처리되는 것이 아니라 try-catch문이 있어야 함!

void method() throws Exception1, Exception2, ... ExceptionN {
	// 메서드의 내용
}
public class ExceptionEx12 {
	public static void main(String[] args) throws Exception {
		method1();
	}
	
	static void method1() throws Exception {
		method2();
	}
	
	static void method2() throws Exception {
		throw new Exception();
	}
}
Exception in thread "main" java.lang.Exception
at ExceptionEx12.method2(ExceptionEx12.java:11)
at ExceptionEx12.method1(ExceptionEx12.java:7)
at ExceptionEx12.main(ExceptionEx12.java:3)

 

 

 

finally블럭

: 예외 발생여부와 관계없이 수행되어야 하는 코드 포함

: try-catch문의 맨 마지막에 위치해야 함

try {
	// 예외 발생 가능성이 있는 문장
} catch(Exception1 e1) {
	// 예외처리를 위한 문장
} finally {
	// 예외 발생 여부와 관계없이 항상 수행되어야 하는 문장
}
public class FinallyTest3 {
	public static void main(String[] args) {
		FinallyTest3.method1();
		System.out.println("method1()의 수행을 마치고 main메서드로 돌아왔습니다.");
	}
	
	static void method1() {
		try {
			System.out.println("method1(0이 호출되었습니다.");
			return; // 현재 실행중인 메서드 종료
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("method1()의 finally블럭이 실행되었습니다.");
		}
	}
}
method1(0이 호출되었습니다.
method1()의 finally블럭이 실행되었습니다.
method1()의 수행을 마치고 main메서드로 돌아왔습니다.

 

 

 

사용자 정의 예외 만들기

: 프로그래머가 직접 예외 클래스 정의

: 조상은 Exception과 RuntimeException 중 선택

: Exception은 try-catch문이 필수이므로 주로 RuntimeException을 조상으로 사용

class MyException extends Exception {
	MyException(String msg) { // 문자열을 매개변수로 받는 생성자. 가능하면 작성할 것
    	super(msg); // 조상인 Exception클래스의 생성자 호출
    }
}

 

 

 

예외 처리 방법3 - 예외 되던지기(exception re-throwing)

: 예외를 처리한 후 다시 예외를 발생시키는 것

: 호출된 메서드와 호출된 메서드 양쪽 모두에서 예외처리함(분담처리)

public class ExceptionEx17 {
	public static void main(String[] args) {
		try {
			method1();
		} catch (Exception e) {
			System.out.println("main메서드에서 예외가 처리되었습니다.");
		}
	}
	
	static void method1() throws Exception {
		try {
			throw new Exception();
		} catch (Exception e) {
			System.out.println("method1메서드에서 예외가 처리되었습니다.");
			throw e;
		}
	}
}
method1메서드에서 예외가 처리되었습니다.
main메서드에서 예외가 처리되었습니다.

 

 

 

연결된 예외(chained exception)

: 한 예외가 다른 예외를 발생시킬 수 있음 -> 이 두 예외를 연결하는 것이 연결된 예외

: 예외A가 예외B를 발생시키면, A는 B의 원인 예외(cause exception)

Throwable initCause(Throwable cause) // 지정된 예외를 원인 예외로 등록
Throwable getCause() // 원인 예외 반환

 

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

public static void main(String[] args) {
	try {
		install();
	} catch (InstallException e) { // SpaceException과 MemoryException을 하나로 다룸
		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();
	}
}

 

2. 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(MemoryException("메모리가 부족합니다.")); // unchecked예외로 변환
	}
}