-개발자가 의도하지 않은 상황에서 발생하는 문제
-예외 발생 시 프로그램이 비정상적으로 종료된다.
-오류는 시스템적인 문제 발생으로 개발자가 해결 불가능한 영역이지만 예외는 오류와 달리 심각도가 낮고,
예외 처리를 통해 프로그램의 비정상적인 종료를 막을 수 있다.
=> 예외가 발생하지 않도록 예외 발생 코드를 수정하는 것은 예외 처리가 아니며,
예외가 발생한다고 가정했을 때 해당 상화에서 수행할 작업을 기술하는 것이 예외 처리
-컴파일(번역) 시점에서 예외 발생 여부를 미리 파악하고 예외 처리를 검사하는 Compile checked Exception 계열이라고 함.
=> 이 계열의 경우 예외가 발생할 것으로 예상되는 코드에 예외 처리가 되어 있지 않으면 컴파일러에 의해
예외 처리 요청에 해당하는 오류를 표시하게 됨.
-컴파일 시점에서는 예외 발생 여부를 알 수 없고, 실행 시점에서 예외가 발생하며, 예외 처리 여부를 검사하지 않는
예외를 complie unchecked Exception 계열이라고 함.
(ex. RuntimeException 계열 - NumberFormatException, ArithmeticException 등)
int num1 = 3, num2 = 0;
// System.out.println(num1 / num2); // 예외 발생
// => 나눗셈의 두번째 피연산자가 0 일 경우 ArithmeticException 예외 발생
// Exception in thread "main" java.lang.ArithmeticException: / by zero
// at Ex3.main(Ex3.java:22)
// => 0 에 의한 나눗셈으로 인해 ArithmeticException 예외가 발생했으며
// 예외 발생 위치가 Ex3 클래스의 main() 메서드 내에 22번 라인 코드
// => 예외가 발생하면 프로그램은 비정상적으로 종료됨
int[] arr = {1, 2, 3}; // 0 ~ 2번 인덱스까지 부여됨
// System.out.println(arr[3]); // 예외 발생
// => 배열 범위를 벗어난 인덱스 접근 시 예외 발생
// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
// at Ex3.main(Ex3.java:33)
String str = null;
// System.out.println(str.length()); // 예외 발생
// => 참조 변수값이 null 일 경우 변수 접근 시 예외 발생
// Exception in thread "main" java.lang.NullPointerException
// at Ex3.main(Ex3.java:40)
String strNum = "30.5";
// int iNum = Integer.parseInt(strNum); // 예외 발생
// => 변환 불가능한 수치데이터를 변환 시 예외 발생
// Exception in thread "main" java.lang.NumberFormatException: For input string: "30.5"
// at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
// at java.lang.Integer.parseInt(Integer.java:580)
// at java.lang.Integer.parseInt(Integer.java:615)
// at Ex3.main(Ex3.java:47)
예외 처리 (Exception Handling)
- 예외가 발생하더라도 프로그램이 정상적으로 종료될 수 있도록 예외 상황에 대한 추가적인 작업(해결책)을 수행하는 것
- try ~ catch 블록을 사용하여 예외 처리 작업 수행
=> try 블록 내에서 예외 발생 가능성이 있는 코드들을 기술하고 예외가 발생하면 JVM에 의해
해당 예외 객체가 생성되어 전달되며 catch 블록 중 일치하는 예외 타입을 찾아 해당 블록을 실행하게 됨
=> 만약, 일치하는 catch 블록이 없을 경우 프로그램은 비정상 종료된다
- 하나의 try 블록에서 catch 블록은 여러개를 가질 수 있으며 예외 발생시 위에서부터 차례대로 catch 블록을 검사함
- 예외클래스를 각각의 catch 블록으로 구분해도 되고, 업캐스팅을 활용하여 상위 예외 타입으로 결합하여 처리해도 된다
(ex. ArithmeticException & NullPointerException 을 결합하여 RuntimeException 이나 Exception 타입으로 처리 가능)
< 기본 문법 >
try {
// 예외가 발생할 것으로 예상되는 범위의 코드들...
} catch(예외클래스명1 변수명) {
// 예외클래스명1 에 해당하는 예외 발생 시 실행할 코드들...
} catch(예외클래스명2 변수명) {
// 예외클래스명2 에 해당하는 예외 발생 시 실행할 코드들...
} catch(Exception e) {
// 위의 예외클래스 외의 모든 예외 처리가 가능한 블록
// 단, 다른 예외클래스보다 위에 있을 경우
// 다른 예외클래스가 catch 되지 못하므로 문제 발생!
}
finally 블록 (try ~ catch ~ finally)
- 예외 발생 여부와 관계없이 무조건 수행해야하는 문장을 기술
ex) 데이터베이스 자원 반환(close()) 하는 작업 등
- 심지어 실행 코드(try 또는 catch 블록) 내에서 return 문을 만나더라도
finally 블록 내의 코드는 무조건 실행 된 후 return 문이 실행된다.
(메서드를 빠져나가기 전 finally 블록 실행 후 빠져나감)
public void exceptionMethod() {
System.out.println("메서드 시작!");
try {
System.out.println("try 블록 시작!");
String str = null;
System.out.println(str.length()); // NullPointerException 예외 발생
System.out.println("try 블록 끝!");
return; // 현재 메서드 종료 후 호출한 곳으로 돌아감
// => 예외 발생 시 실행되지 못하는 문장
} catch(NullPointerException e) {
System.out.println("NullPointerException 예외 발생 시 처리할 코드");
}
// 예외 발생 여부와 관계없이 실행되는 문장이지만
// try 블록 또는 catch 블록 등에서 return 문을 만나면
// 메서드가 종료되는데 이 때 try 블록 밖의 문장은 실행되지 않고
// 바로 메서드를 빠져나가게 된다!
System.out.println("메서드 종료!");
}
public void exceptionFinallyMethod() {
System.out.println("메서드 시작!");
try {
System.out.println("try 블록 시작!");
String str = null;
// System.out.println(str.length()); // NullPointerException 예외 발생
System.out.println("try 블록 끝!");
return; // 현재 메서드 종료 후 호출한 곳으로 돌아감
// => 예외 발생 시 실행되지 못하는 문장
} catch(NullPointerException e) {
System.out.println("NullPointerException 예외 발생 시 처리할 코드");
} finally {
// 예외 발생 여부와 관계없이 실행되어야하는 코드를 기술
// try 또는 catch 문에서 return 문을 만나더라도
// 메서드를 종료하고 빠져나가기 전 finally 블록의 문장을 실행한 후
// 메서드를 종료하고 빠져나감
System.out.println("예외 발생 여부와 관계없이 실행되는 코드");
}
// 예외 발생 여부와 관계없이 실행되는 문장이지만
// try 블록 또는 catch 블록 등에서 return 문을 만나면
// 메서드가 종료되는데 이 때 try 블록 밖의 문장은 실행되지 않고
// 바로 메서드를 빠져나가게 된다!
System.out.println("메서드 종료!");
}
}
예외 처리에 대한 위임
- 자신의 위치에서 예외가 발생했을 때 자신이 직접 처리하지 않고 자신의 메서드를 호출한 곳으로 예외를 위임(떠넘기기=전달)
- throws 키워드를 사용하여 예외 처리를 위임(전달)
- 예외처리클래스를 지정할 떄 각각의 예외를 따로 지정하거나 상위 타입 예외 클래스를 사용하여 통합으로 관리도 가능함
ex) ArithmeticException + NumberFormatException = RuntimeException
RuntimeException + ClassNotFoundException = Exception
- 예외 처리를 위임받은 곳에서는 예외 처리에 대한 책임이 발생하여 직접 처리하거나 또 다시 위임 가능
(폭탄을 전달받았으면 폭탄을 해체하거나 다른 사람한테 넘기는 것 처럼)
- 최종적으로 main() 메서드까지 위임될 경우 main() 메서드에서 처리 필수!
<기본 문법>
메서드선언부() throws 위임할예외클래스명1, ..., 예외클래스명n{
//예외발생코드
//예외 발생 시 throws 키워드 뒤에 명시된 클래스와 일치하는 클래스가 있으면 해당 예외는 메서드 호출한 곳으로 던진다.
}
최종적으로 main 메서드까지 위임되면 메서드 내에서도 메서드 호출 코드에 대한 책임이 발생함
main에서도 throws 키워드를 사용할 수는 있으나 최종적으로 처리할 곳을 확인 불가능하므로
main 메서드 내에서 try ~ catch를 통해 직접 처리해야 함.
try {
부장();
} catch (Exception e) {
System.out.println("main() 메서드가 모든 예외를 처리!");
}
}
public static void 부장() throws Exception {
// 1. 위임받은 2개(or 3개)의 예외를 직접 처리할 경우
// => ClassNotFoundException 은 컴파일 시 체크되므로 예외 표시됨
// try {
// 과장();
// } catch (ClassNotFoundException e) {
// System.out.println("부장이 ClassNotFoundException 처리");
// } catch (RuntimeException e) {
// System.out.println("부장이 RuntimeException 처리");
// }
// 2. 위임받은 예외를 다시 위임할 경우
// => 모든 예외를 하나의 묶음으로 다루려면 Exception 클래스 사용 가능
과장();
}
public static void 과장() throws ClassNotFoundException, RuntimeException {
// 1. 위임받은 2개의 예외를 직접 처리할 경우
// try {
// System.out.println("대리로부터 ArithmeticException, NumberFormatException 위임됨");
// 대리();
// } catch (RuntimeException e) {
// // ArithmeticException 과 NumberFormatException 을 따로 처리하거나
// // 상위 타입으로 묶어서 처리 가능함
// System.out.println("위임받은 예외를 RuntimeException 타입으로 처리");
// }
// 2. 위임받은 예외를 다시 부장에게 위임할 경우
System.out.println("대리로부터 ArithmeticException, NumberFormatException 위임됨");
대리();
Class.forName("클래스명"); // ClassNotFoundException 예상 코드
}
public static void 대리() throws ArithmeticException, NumberFormatException {
// 사원으로부터 위임된 예외를 전달받을 경우
// 위임된 예외 클래스에 대한 예외 처리를 수행해야한다.
// => 만약, Compile Checked Exception 일 경우 메서드 호출 코드에
// 예외 처리 책임에 따른 오류가 표시되지만
// Compile Unchecked Exception 일 경우 메서드 호출 코드에
// 오류가 표시되지 않는다.
// 1. 대리가 사원으로부터 위임된 예외를 직접 처리할 경우
// try {
// System.out.println("사원으로부터 ArithmeticException 위임됨");
// 사원();
// } catch (ArithmeticException e) {
// System.out.println("대리가 ArithmeticException 직접 처리!");
// }
// 2. 예외를 직접 처리하지 않고 호출한 곳으로 위임할 경우
Integer.parseInt("숫자가아닌데이터"); // NumberFormatException 발생 코드
사원(); // ArithmeticException 예외가 위임되는 코드
// => 현재 위치에서 발생할 것으로 예상되는 예외 : 2개
// ArithmeticException 과 NumberFormatException 모두 위임하거나
// 선택적으로 처리하고 나머지만 위임 가능
}
public static void 사원() throws ArithmeticException {
// try {
// System.out.println("사원에서 예외 발생");
// System.out.println(3 / 0); // ArithmeticException 발생 예상 코드
// } catch (ArithmeticException e) {
// System.out.println("사원이 직접 예외 처리");
// }
// 예외를 호출한 곳(대리)으로 위임할 경우
System.out.println("사원에서 예외 발생");
System.out.println(3 / 0); // ArithmeticException 발생 예상 코드
// => 직접 try~catch 블록을 통해 예외를 처리하지 않고 위임할 경우
// 메서드 선언부 마지막 부분에 throws 키워드를 쓰고
// 뒷부분에 위임할 예외클래스 이름을 명시함
// (1개 또는 복수개의 클래스 지정 가능)
}
개발자에 의한 예외 발생(예외를 직접 발생시키기)
- 자바 문법에서 정해놓은 규칙에 따른 예외가 아닌 개발자 입장에서의 논리적인 예외 상황이 발생했을 때 예외를 발생시킬 수 있음.
ex) 점수를 int형 score 변수에 입력받았을 때 100보다 클 경우 예외
=> 문법적으로는 맞지만, 논리적으로 틀렸을 때 예외 발생
- throw 키워드를 사용하여 발생시킬 예외 클래스의 객체 생성하여 발생
< 기본 문법 >
예외 발생 상황에서(ex. if문, while문 등의 조건에서)
throw new 예외클래스명("예외메세지");
// inputScore() 메서드 호출 시 Exception 예외가 throws 되므로
// 메서드 호출 시점에서 예외 발생 가능성을 파악할 수 있고
// 호출 코드에서 try~catch 블록을 사용하여 예외를 처리해야한다!
try {
inputScore(150);
} catch (Exception e) {
e.printStackTrace();
System.out.println("예외 발생 메세지 : " + e.getMessage());
}
}
public static void inputScore(int score) throws Exception {
// 성적 처리를 위해 점수를 입력받은 메서드에서
// 0 ~ 100 사이의 점수만 입력받아 처리한다고 가정
// => int형 변수는 문법적으로 0 ~ 100 범위 외의 정수도 무관함
// 이 때, 0 ~ 100 범위가 아닌 정수 입력 시 예외를 발생시킨 후
// 해당 예외를 외부로 던져서 예외 처리를 강제로 수행시킬 수 있다.
// if문을 사용하여 0 ~ 100 사이 범위가 아닌 점수가 입력됐을 때 판별
if(score < 0 || score > 100) {
// 문법적로는 올바른 문법이지만 논리적으로 문제가 있으므로
// 개발자가 직접 예외를 발생시켜야 한다!
throw new Exception("0 ~ 100 사이의 점수만 입력하세요!");
// => 개발자가 예외 상황을 정의하고 직접 예외를 발생시킴
// => 이 때, 발생한 예외를 직접 처리(try~catch)해도 되지만
// 주로, 발생한 예외를 외부로 던지기(위임하기)위해 throws 사용
}
}
'develop > Java' 카테고리의 다른 글
Wrapper 클래스 (0) | 2021.04.18 |
---|---|
enum 타입 (0) | 2021.04.12 |
중첩 클래스 (0) | 2021.04.12 |
제네릭(Generic, 일반화) (1) | 2021.04.05 |
Collection Framework(컬렉션 프레임워크) (0) | 2021.04.04 |