예외 처리는 어떻게 할까?
프로그램 오류
--
오류 종류
- 컴파일 에러 : 컴파일 시 발생하는 에러
- 런타임 에러 : 실행 시 발생하는 에러
- 논리적 에러 : 실행은 되지만, 의도와 다르게 동작하는 상황
런타임 에러의 종류
- 에러 (Error) : 프로그램 코드에 의해 수습이 불가능한 오류
- 예외 (Exception) : 프로그램 코드에 의해 수습이 가능한 오류
예외 클래스의 계층 구조
에러와 예외 역시 최종 부모 클래스가 Object 클래스다.
위 그림처럼
예외 클래스는 두 그룹으로 나눌 수 있다.
- Exception 클래스와 해당 자식 클래스들
- RuntimeException 클래스와 해당 자식 클래스들
--
예외 처리 ( try-catch, try-catch-finally문 )
--
오류가 아닌 예외는 개발자가 발생할 예외에 대해 처리를 미리 해주어야 한다.
즉, 프로그램 실행 시 발생할 수 있는 예외를 막기 위한 대비 코드를 작성하여
프로그램의 비정상 종료를 막고, 정상적인 실행 상태를 유지하기 위해 작성한다.
try-catch 기본 형식
try {
// 예외 발생할 가능성이 있는 명령어
} catch (예외타입 변수명1) {
// 해당 예외타입의 예외가 발생했을 경우, 처리할 명령어
} catch (예외타입 변수명2) {
// 해당 예외타입의 예외가 발생했을 경우, 처리할 명령어
}
try {
System.out.println(3);
System.out.println(0/0); //0으로 나눠서 ArithmeticException에러 발생
System.out.println(4);
} catch (Exception e) { // Exception은 모든 예외의 부모 클래스로 ArithmeticException예외catch가능
System.out.println(5);
}
System.out.println(6);
/* 결과
3
5
6
*/
try-catch문 진행 순서
- try문에 존재하는 명령어 수행 도중 예외 발생 ( 미발생 시 try문을 끝까지 수행 후 try-catch문 탈출)
- 발생한 예외와 관련된 예외타입(예외 클래스)이 정의된 catch문을 찾아 수행 (처음 찾은 하나의 catch문만 수행)
- catch문을 모두 수행하면 try-catch문 탈출하고 정상적으로 이어서 동작
try-catch문 규칙
- try에서 예외 발생 시 순서대로 catch문을 탐색하여 처음으로 예외 타입과 일치한 catch문을 찾으면 해당 catch문 하나만 실행 (나머지 catch문은 무시)
- 발생한 예외와 관련된 catch문이 없다면 예외처리를 못해 프로그램이 비정상 종료
- catch문에서 예외처리 완료 후 바로 try-catch문을 탈출하여 그 다음 명령어를 수행 (try문으로 돌아가지 않음)
- 이중 for문처럼 try-catch문 또한 중첩이 가능
- Exception은 모든 예외를 포함하므로 마지막 catch문에 사용하는 것이 좋다. (if-else문에서 else와 같은 느낌으로)
멀티 catch문
catch문 하나에 '|' 기호를 사용하여 여러 개의 예외타입을 적용할 수도 있다.
(정의된 예외타입 중에서 하나만 해당해도 catch문이 실행된다.)
catch (예외타입A | 예외타입B e) { ... }
주의
하나의 catch문에 정의한 예외타입(클래스)이 서로 부모/자식 관계라면 "컴파일 에러"가 발생한다.
(부모 클래스 하나만 정의해도 해당 자식 클래스인 예외는 모두 포함하므로 하나만 작성해도 된다.)
try-catch-finally 기본 형식
try {
// 예외 발생할 가능성이 있는 명령어
} catch (예외타입 변수명1) {
// 해당 예외타입의 예외가 발생했을 경우, 처리할 명령어
} catch (예외타입 변수명2) {
// 해당 예외타입의 예외가 발생했을 경우, 처리할 명령어
} finally {
// 예외 발생 여부와 상관없이 무조건 마지막에 실행되는 명령어
}
try {
System.out.println(3);
System.out.println(0/0); //0으로 나눠서 ArithmeticException에러 발생
System.out.println(4);
} catch (Exception e) { // Exception은 모든 예외의 부모 클래스로 ArithmeticException예외catch가능
System.out.println(5);
} finally {
System.out.println("finally");
}
System.out.println(6);
/* 결과
3
5
finally
6
*/
finally문은
try문에서 예외가 발생하든 발생하지 않든 항상 실행되는 블록으로
try-catch문을 모두 수행하고 마지막에 수행된다.
일반적으로 파일, DB 연결, 네트워크 소켓과 같은 시스템 자원을 다시 해제하거나 닫을 때 주로 사용된다.
--
강제 예외 발생시키기 (throw)
--
throw 키워드를 사용하면
개발자가 고의로 예외를 발생시킬 수 있다.
throw 기본 형식
예외타입 객체명 = new 예외생성자("예외 메시지");
throw 강제로 발생시킬 예외 객체명;
try {
Exception e = new Exception("고의로 발생시킴"); // 예외 선언(생성)
throw e; // 예외 발생시킴
} catch (Exception e) {
System.out.println("에러 메세지 : " + e.getMessage());
e.printStackTrace();
}
System.out.println("프로그램 종료됨");
/* 결과
에러 메세지 : 고의로 발생시킴
java.lang.Exception: 고의로 발생시킴
at ExceptionEx9.main(ExceptionEx9.java:4)
프로그램 종료됨
*/
--
메서드에 예외 처리 떠넘기기 (throws)
--
메서드에 throws 키워드를 사용하면
메서드 내부에서 예외가 발생할 명령어를 즉시 try-catch문으로 예외를 처리하지 않고
해당 메서드를 호출하는 곳에서 try-catch문으로 예외를 처리하도록 떠넘길 수 있다.
throws 기본 형식
반환타입 메서드명() throws 발생할예외1, 발생할예외2, ... {
// 예외발생할 가능성이 있는 명령어 존재
}
// 기존 방식 (바로 try-catch문으로 예외 처리하기)
public void test1() {
try {
System.out.println(3);
System.out.println(0/0);
System.out.println(4);
} catch (Exception e) {
System.out.println(5);
} finally {
System.out.println("finally");
}
System.out.println(6);
}
// throws 키워드 사용하기
public void test1() throws Exception { // 해당 메서드를 호출하는 곳으로 예외 처리 떠넘기기
System.out.println(3);
System.out.println(0/0); // 예외 발생할 명령어
System.out.println(4);
System.out.println("finally");
System.out.println(6);
}
public void test2() throws Exception { // 또 떠넘기기 가능
test1();
}
public void test3() { // 최종적으로 예외 처리하기
try {
test2();
} catch (Exception e) {
System.out.println(5);
} finally {
System.out.println("finally");
System.out.println(6);
}
}
/* 결과
3
5
finally
6
*/
// catch만 미구현하여 떠넘기기도 가능
public void test1() throws Exception {
try {
System.out.println(3);
System.out.println(0 / 0); // 예외 발생
System.out.println(4); // 이 코드는 실행되지 않음
} finally {
System.out.println("finally"); // 예외가 발생해도 finally는 항상 실행
}
System.out.println(6); // 예외 발생 시 이 코드는 실행되지 않음
}
public void test2() {
try {
test1(); // test1() 호출, 여기서 예외 발생 가능
} catch (Exception e) {
System.out.println(5); // test1()에서 발생한 예외를 처리
}
}
/* 결과
3
finally
5
*/
throws는
해당 메서드에서 이러한 예외가 발생할 수 있다고
해당 메서드를 호출하는 곳에게 알려주는 역할이다.
즉, "이 메서드에서는 이러한 예외가 발생할 수 있으니 호출하게 되면 해당 예외 처리를 해줘야해!!"
라고 말하는 것이다.
호출하는 곳마다 throws 키워드를 사용하면 계속 예외 처리를 떠넘길 수 있지만
끝까지 떠넘기기만 하면 결국 예외 처리를 하지 않아 에러가 발생한다.
무조건 어느 한 곳에서는 예외처리를 해줘야 한다.
--
사용자 정의 예외 만들기 ( 커스텀 예외 클래스 )
--
기본으로 제공하는 예외 클래스들 중에서
필요한 예외 클래스를 상속 받아 새로운 예외 클래스를 만들고 사용할 수 있다.
예시
class MyException extends Exception { // MyException예외 클래스 생성
MyException(String msg) { //문자열을 매개변수로 받는 생성자
super(msg); //부모인 Exception의 생성자 호출
}
}
위 코드는 아주 간단하게만 작성한 것이며
부모 클래스인 Exception를 오버라이딩하여 재정의할 수도 있고
새로운 메서드도 추가할 수 있다.
--
'Language > Java' 카테고리의 다른 글
[java.lang] Object 클래스 (객체 관련 클래스) (0) | 2024.10.31 |
---|---|
java.lang 패키지 (1) | 2024.10.29 |
내부 클래스 (+ 익명 클래스) (0) | 2024.10.23 |
인터페이스 (1) | 2024.10.22 |
다형성 (예시 : A opt = new B()) (0) | 2024.10.21 |