예외처리(exception handling)
프로그램 오류
- 컴파일 에러 : 컴파일할 때 발생하는 에러. 프로그램 실행 안됨
- 런타임 에러 : 문법에는 맞지만 실행 중 발생하는 에러. 프로그램 종료됨
- 논리적 에러 : 작성 의도와 다르게 동작. 프로그램은 실행됨
- 런타임 에러
에러는 어쩔 수 없지만 예외는 처리하자
- 에러 : 코드에 의해 수습될 수 없는 심각한 오류(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예외로 변환
}
}
'Back-end > JAVA' 카테고리의 다른 글
[JAVA] 날짜와 시간 & 형식화 (1) | 2023.10.14 |
---|---|
[JAVA] java.lang 패키지와 유용한 클래스 - Object, String, Math, wrapper 클래스 (0) | 2023.10.07 |
[JAVA] 객체지향 프로그래밍(2) - 상속, 오버라이딩, Package와 import, 제어자, 다형성, 추상클래스, 인터페이스 (1) | 2023.09.16 |
[JAVA] 객체지향 프로그래밍(1) - 클래스와 객체, 변수와 메서드, 오버로딩, 생성자, 초기화 (0) | 2023.09.09 |
[JAVA] 조건문, 반복문 (0) | 2023.02.28 |