예외(Execption)
- 개발자가 의도하지 않은 상황에서 발생하는 문제로 프로그램이 비정상적으로 종료됨
=> 예외가 발생한 위치부터 아래쪽의 코드들은 실행되지 못함
- 오류(Error)와 달리 심각도 낮으며, 예외 처리(Exception Handling) 를 통해 예외 발생 시 해결책을 기술하여 비정상적인 종료를 막을 수 있음
- 예외 처리를 위해 try ~ catch 문을 사용하여 처리
=> 예외가 발생할 것으로 예상되는 코드들을 try 블록 내에 기술하고, 예외가 발생하면 JVM 에 의해 해당 예외 객체들을 전달받아 catch 블록 중 일치하는 타입에 대한 블록을 실행하여 예외 처리
=> 만약, 일치하는 catch 블록이 없을 경우 프로그램은 비정상 종료됨
- Exception 클래스 및 하위 클래스를 사용하여 예외 처리
- 컴파일 시점에서 예외 발생 여부를 체크하는 Compile Checked Exception 과 실행 시점에서 예외 발생 여부를 알 수 있는 Compile Unchecked Exception 계열로 나뉨 (Compile Checked Exception 계열 : IOException, SQLException 등
Compile Unchecked Exception 계열 : RuntimeException 계열
=> ArrayIndexOutOfBoundsException, NullPointerException 등)
< 예외 처리 기본 문법 >
try {
// 예외가 발생할 것으로 예상되는 범위의 코드들...
// => 예외 발생하면 코드 아래쪽 나머지 코드는 실행되지 못함
} catch(예외클래스명1 변수명) {
// 예외클래스명1에 해당하는 예외 발생 시 처리할 코드들...
} catch(예외클래스명n 병수명) {
// 예외클래스명n에 해당하는 예외 발생 시 처리할 코드들...
} finally {
// 예외 발생 여부와 관계없이 무조건 실행할 문장들...
// ex) DB 자원 반환, I/O 자원 반환 등
}
System.out.println("프로그램 시작!");
// RuntimeException 클래스 계열들(Compile Unchecked Exception 계열)
// int num = 0;
// System.out.println(3 / num);
// => 나눗셈 연산의 피연산자가 0인 경우 ArimethicException 예외 발생
// => java.lang.ArithmeticException: / by zero
// at exception.Ex.main(Ex.java:43)
// => / by zero : 0 에 의한 나눗셈으로 인해 예외 발생했다는 메세지
// (Ex.java:43) : Ex.java 파일의 43번 라인에서 예외 발생
// int[] arr = {1, 2, 3};
// System.out.println(arr[5]);
// 배열에 존재하지 않는 인덱스 접근 시 ArrayIndexOutOfBoundsException 발생
// java.lang.ArrayIndexOutOfBoundsException: Index 5 => 5번 인덱스 문제
// at exception.Ex.main(Ex.java:51) => 51번 라인에서 예외 발생
// String str = null;
// System.out.println(str.length());
// 객체의 주소를 참조하지 않는 참조변수(null) 접근 시
// NullPointerException 예외 발생
// => java.lang.NullPointerException:
// Cannot invoke "String.length()" because "str" is null
// => at exception.Ex.main(Ex.java:57) => 57번 라인에서 예외 발생
System.out.println("프로그램 종료!");
// 예외 처리
System.out.println("프로그램 시작!");
try {
// 예외가 발생할 것으로 예상되는 코드들을 try 블록 내에 위치시킴
int num = 1;
System.out.println(3 / num); // ArithmeticException 발생 위치
// 만약, 이 지점에서 예외 발생 시 나머지 try 블록의 코드들은
// 실행되지 못하고 바로 catch 문으로 이동
int[] arr = {1, 2, 3};
System.out.println(arr[2]); // ArrayIndexOutOfBoundsException 발생 위치
// ArithmeticException 외에 다른 예외도 처리해야할 경우
// 또 다른 catch 블록을 추가할 수 있다!
// => 이 때, catch 블록은 if문처럼 순차적으로 수행됨
String str = null;
System.out.println(str.length()); // NullPointerException 발생 위치
// 예외가 복수개 일 때 각각의 예외를 별도로 처리해도 되지만
// 예외도 클래스이므로 업캐스팅이 가능하므로
// 상위 예외 타입으로 묶어서 처리 가능
// ex) NullPointerException + ArithmeticException
// = RuntimeException 또는 Exception 클래스 처리 가능
System.out.println("try 블록 끝!");
} catch(ArithmeticException e) {
// ArithemticException 예외가 발생했을 경우 처리할 코드를 기수
// 예외가 발생하지 않을 경우 이 블록은 실행되지 않음
e.printStackTrace();
// => 예외 클래스, 발생 위치, 원인 등을 자세히 출력
// e.getMessage(); // 예외 발생 원인 메세지를 문자열로 리턴
System.out.println("0으로 나눌 수 없습니다! - " + e.getMessage());
} catch(ArrayIndexOutOfBoundsException e) {
// ArrayIndexOutOfBoundsException 예외가 발생했을 경우 처리할 코드를 기술
System.out.println("배열의 인덱스가 잘못 지정되었습니다. - " + e.getMessage());
// } catch(Exception e) {
// // 위쪽의 catch 블록에서 지정되지 않은 나머지 예외들은
// // Exception 이 기술된 catch 블록에서 모두 처리 가능함
// // => 단, 각 예외에 따른 처리 방법을 구분할 수 없음.
// System.out.println("나머지 모든 예외 처리 - " + e.getMessage());
} catch(NullPointerException e) {
// => Unreachable catch block for NullPointerException.
// It is already handled by the catch block for Exception
// 주의! Exception 예외 처리 catch 블록이 존재할 경우
// 해당 예외 아래쪽의 catch 블록은 실행되지 못함
// => if문과 마찬가지로 순차적으로 처리되므로
// 하위클래스 타입부터 상위클래스 타입순으로 예외 처리 필수!
System.out.println("null 값을 참조할 수 없음! - " + e.getMessage());
} catch(Exception e) {
// 위쪽의 catch 블록에서 지정되지 않은 나머지 예외들은
// Exception 이 기술된 catch 블록에서 모두 처리 가능함
// => 단, 각 예외에 따른 처리 방법을 구분할 수 없음.
System.out.println("나머지 모든 예외 처리 - " + e.getMessage());
}
// try ~ catch 블록 밖의 코드는 예외 발생 여부와 관계없이 실행됨!
System.out.println("프로그램 종료!");
}
finally 블록
- 예외 발생 여부와 관계없이 무조건 수행해야하는 문장을 기술
- 심지어 return 문을 만나 메서드를 종료하더라도 호출한 곳으로 돌아가기 전 finally 블록을 실행한 후 돌아감
method1();
} // main() 메서드 끝
public static void method1() {
System.out.println("메서드 시작!");
try {
// String str = null;
String str = "";
System.out.println(str.length()); // NullPointerException 예외 발생
System.out.println("try 블록 끝!");
return; // 메세드 종료 후 호출한 곳으로 되돌아감
// => finally 블록이 존재할 경우 호출한 곳으로 돌아가기 전
// finally 블록을 실행한 후 돌아감
} catch(Exception e) {
e.printStackTrace();
System.out.println("NullPointerException 예외 처리!");
} finally {
System.out.println("finally - 예외 발생 여부와 관계없이 실행됨!");
}
} // method1() 메서드 끝
System.out.println("프로그램 시작!");
try {
// String str = null;
String str = "";
System.out.println(str.length()); // NullPointerException 예외 발생
System.out.println("try 블록 끝!");
} catch(Exception e) {
e.printStackTrace();
System.out.println("NullPointerException 예외 처리!");
} finally {
System.out.println("finally - 예외 발생 여부와 관계없이 실행됨!");
}
System.out.println("프로그램 종료!");
예외 처리 위임(전달)
- 예외가 발생한 곳에서 try ~ catch 블록으로 직접 처리할 수 있지만 자신이 직접 처리하지 않고, 메서드 호출한 곳으로 예외 위임 가능
- throws 키워드를 사용
- 예외 처리를 위임받은 메서드는 다시 예외 처리에 대한 책임이 발생하며 직접 처리하거나 또 다른 곳으로 위임 가능
- 최종적으로 마지막 단계의 메서드에서는 try ~ catch 블록을 사용하여 예외를 직접 처리해야 함(ex. main() 메서드)
- throws 키워드를 사용하여 지정하는 예외는 1개 또는 복수개 지정 가능
< 예외 위임 기본 문법 >
- 메서드 정의 시 메서드 선언부 마지막에 throws 키워드를 쓰고
뒤에 예외를 위임할 클래스명을 기술(복수개일 경우 콤마로 구분)
[접근제한자] 리턴타입 메서드명([파라미터...]) throws 예외클래스명... {
// 예외가 발생하는 코드...
}
public static void 팀장() throws Exception {
// 1. 대리로부터 위임받은 예외를 직접 처리하는 경우
// try {
// 대리();
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// } catch (RuntimeException e) {
// e.printStackTrace();
// }
// 2. 팀장이 위임받은 예외를 사장(main()) 에게 위임하는 경우
// => RuntimeException 과 ClassNotFoundException 을 명시하거나
// Exception 타입으로 위임 가능
대리();
}
public static void 대리() throws RuntimeException, ClassNotFoundException {
// 사원으로부터 예외를 위임받아 처리할 때
// 1. 위임받은 대리가 직접 처리하는 경우
// try {
// 사원();
// 사원2();
// } catch(ArithmeticException e) {
// System.out.println("대리가 사원의 ArithmeticException 예외 직접 처리!");
// } catch(NullPointerException e) {
// System.out.println("대리가 사원의 NullPointerException 예외 직접 처리!");
// } catch(ClassNotFoundException e) {
// System.out.println("대리가 사원2의 ClassNotFoundException 예외 직접 처리!");
// }
// 2. 예외를 위임받은 대리도 팀장에게 다시 위임하는 경우
// => 발생 가능성이 있는 예외 클래스를 모두 throws 키워드 뒤에 명시
// => 모든 클래스를 따로 명시하거나, 상위 타입으로 결합하여 명시 가능
사원(); // ArithmeticException, NullPointerException
사원2(); // ClassNotFoundException
}
public static void 사원() throws ArithmeticException, NullPointerException {
// 예외 처리 방법
// 1. 예외가 발생한 곳에서 try ~ catch 로 직접 예외 처리
try {
System.out.println("사원에서 예외 발생!");
// System.out.println(3 / 0); // ArithmeticException 발생하는 코드
// String str = null;
// System.out.println(str.length()); // NullPointerException 발생하는 코드
} catch(ArithmeticException e) {
System.out.println("사원이 직접 예외 처리!");
} catch(NullPointerException e) {
System.out.println("사원이 직접 예외 처리!");
}
// 2. 메서드를 호출한 곳으로 throws 키워드로 예외를 위임
// => 현재 사원() 메서드를 호출한 곳(대리()) 으로 예외를 던짐(throws)
// => 사원() 메서드 선언부 마지막에 throws 키워드를 쓰고
// throws 뒤에 예외 클래스를 1개 또는 복수개 명시(콤마 구분)
// System.out.println("사원에서 예외 발생!");
// System.out.println(3 / 0); // ArithmeticException 발생하는 코드
String str = null;
System.out.println(str.length()); // NullPointerException 발생하는 코드
}
public static void 사원2() throws ClassNotFoundException {
System.out.println("사원2에서 예외 발생!");
Class.forName(""); // ClassNotFoundException
// => Unhandled exception type ClassNotFoundException
// => Compile Checked Exception 이므로 예외 처리가 필수! (오류로 표시됨)
}
// 팀장으로부터 위임받을 때, 현재 메서드가 사장(main()) 일 경우
// 더 이상 위임할 수 있는 곳이 없음(문법적으로 throws 로 해결 가능)
// => 따라서, 최종적으로 사장(main()) 이 모든 예외를 처리해야 한다!
try {
팀장();
} catch (Exception e) {
System.out.println("사장이 모든 예외를 처리!");
e.printStackTrace();
}
} // main() 메서드 끝
사용자에 의한 예외 발생(throw)
- 자바 기준으로 예외가 아닌 상황에서도 개발자(사용자)의 의도대로 예외를 직접 발생시키는 것
- throw 키워드를 사용하여 발생시킬 예외 클래스 객체를 지정
=> 예외 클래스의 인스턴스를 생성하여 강제로 해당 예외 발생시킴
- 예외 클래스 인스턴스를 변수에 저장하여 발생시켜도 되지만 주로 일회성으로 예외를 발생시키고 재사용할 일이 없으므로 별도의 변수 없이 임시 객체 형태로 사용하는 경우가 많음 (new 예외클래스명() 형태로 객체를 생성하여 전달)
< 기본 문법 >
예외를 발생시킬 코드 위치에서
throw 예외객체;
사용자 정의 예외 클래스
- 기존의 예외클래스가 발생시킨 예외와 일치하지 않을 경우 Exception 클래스로 예외 처리 시 발생한 예외를 파악하기 힘들다
- 사용자가 직접 예외 클래스를 작성하여 발생시킬 예외에 맞는 객체를 만들어서 전달하면 예외 파악이 쉬워진다
- 사용자 정의 예외 클래스 정의 시 예외 클래스를 상속받아 정의 (ex. Exception 클래스를 상속받은 서브클래스 정의)
=> 주로 Exception 클래스를 상속받아 정의
- 보통, 생성자를 정의하여 슈퍼클래스에 예외메세지만 초기화
// 사용자 정의 예외 클래스 작성
// - 원하는 이름의 서브클래스 정의 후 예외클래스를 상속받음
// - 주로, Exception 클래스를 상속받아 정의함
class InvalidScoreException extends Exception {
// 생성자를 정의하여 예외 메세지를 슈퍼클래스의 생성자에 전달
public InvalidScoreException(String message) {
super(message);
}
}
try {
printScore(150);
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
System.out.println("--------------------------------");
try {
userExceptionMethod(150);
} catch (InvalidScoreException e) {
e.printStackTrace();
}
} // main() 메서드 끝
public static void printScore(int score) throws Exception {
/*
* 점수를 입력받아 학점을 계산 후 출력하는 메서드
* 단, 점수 범위를 0 ~ 100 사이의 정수로 한정
* 만약, 0 ~ 100 사이가 아닐 경우 비정상적인 점수이므로 오류 발생 필요함
* 그러나, int형 범위를 벗어나지 않으므로 자바 기준으로는 예외가 아님!
* => 따라서, 원하는 시점에 특정 조건 발생 시 강제로 예외를 발생
*/
if(score < 0 || score > 100) {
// System.out.println(score + "점 : 점수 입력 오류!");
// throw 키워드를 사용하여 해당 예외 클래스 객체 생성
// => 단, 점수 입력 오류에 대한 별도의 예외 클래스가 없으므로
// 기존의 클래스 활용(일치하는 클래스가 없으면 Exception 클래스 사용)
// Exception e = new Exception("점수 입력 오류 - " + score);
// => 예외 발생 시 표시할 예외 메세지를 생성자 파라미터로 전달
// throw 키워드 뒤에 예외 객체를 지정하여 해당 예외를 발생시킴
// throw e;
// => Unhandled exception type Exception
// 위의 방법은 Exception 객체의 변수 e 를 한 번만 사용함
// 별도의 변수 없이 바로 객체를 생성하여 예외 발생 가능 = 임시 객체
throw new Exception("점수 입력 오류 - " + score);
}
} // printScore() 메서드 끝
public static void userExceptionMethod(int score) throws InvalidScoreException {
if(score < 0 || score > 100) {
// Exception 클래스 대신 사용자 정의 예외클래스인
// InvalidScoreException 클래스를 사용하여 예외 발생시킬 수 있음
throw new InvalidScoreException("점수 입력 오류 - " + score);
}
}
} // Ex4 클래스 끝
'⛏️ > JAVA' 카테고리의 다른 글
[JAVA] 40. 중첩 클래스, 중첩 인터페이스 (0) | 2023.11.08 |
---|---|
[JAVA] 39. Scanner (0) | 2023.11.07 |
[JAVA] 37. Collection Framework(컬렉션 프레임워크) (0) | 2023.10.31 |
[JAVA] 36. 제네릭(Generic) (1) | 2023.10.31 |
[JAVA] 35. enum_type (0) | 2023.10.25 |