본문 바로가기

JAVA/혼공자

[혼공자] Chapter 10. 예외처리

10-1 예외 클래스


▶ 자바에서의 에러와 예외
에러 (Erorr) : 개발자가 해결할 수 없는 치명적인 오류
Ex. 하드웨어 고장으로 인한 응용프로그램 실행 오류
예외 (Exception) : 개발자가 해결할 수 있는 그나마 덜 심각한 오류  
Ex. 잘못된 코딩으로 인해 발생하는 오류
예외가 발생하면 프로그램은 곧바로 종료되지만, 예외 처리를 통해 정상 실행 상태가 유지되게 할 수 있다.

예외 처리의 목적 : 프로그램의 비정상 종료를 막고 정상적인 실행 상태를 유지하는 것

 

▶ 예외의 종류

일반 예외 (컴파일러 체크 예외)
컴파일러가 예외처리 코드 유무를 검사한다.
예외처리 코드가 없다면 컴파일 오류가 발생하여 프로그램이 실행되지 않는다.
cf. 이클립스는 일반 예외가 발생할 수 있는 코드를 작성하면 빨간 밑줄을 그어 예외처리 코드의 필요성을 알려준다.
➡️ 컴파일 시 발생하는 에러

실행 예외 (컴파일러 넌 체크 예외)
컴파일러가 예외처리 코드 유무를 검사하지 않는다.
따라서 예외처리를 하지 않아서 프로그램이 실행되지 않는 일은 없지만,
실행 도중 예외가 발생하면 프로그램은 바로 종료된다.
➡️ 실행 시 발생하는 에러


▶ 예외 클래스
예외가 발생하면, JVM은 해당 예외 클래스의 객체를 생성하여 예외 처리 코드로 넘겨준다.
모든 예외는 Exception 클래스를 상속한다.
일반 예외와 실행 예외는 RuntimeException을 상속하는지에 따라 구별할 수 있는데, 
실행 예외는 RuntimeException을 상속하지만, 일반 예외는 상속하지 않는다. 

출처 : https://itmining.tistory.com/9

 

  일반 예외 (Checked Exception) 실행 예외 (Unchecked Exception)
확인 시점 컴파일 단계 실행 단계
처리 여부 반드시 예외 처리 해야함  명시적 처리 강제하지 않음
클래스 Exception 클래스의 하위 클래스 중
RuntimeException 을 상속하지 않는 모든 클래스
RuntimeException 클래스의 하위 클래스
대표적 예외
- IOException
- SqlException
- NullPointerException
- ClassCastException
- IndexOutOfBoundException
- SystemException

출처 : https://itmining.tistory.com/9

 

▶ 실행 예외 예시
- NullPointerException
null 값을 갖는 참조 변수에 객체 접근 연산자인 도트(.)를 사용하는 경우
- ArrayIndexOutOfBoundsException
배열에서 인덱스 범위를 초과할 경우 발생하는 예외
- NumberFormatException
문자를 숫자로 변환할 때, 숫자로 변환될 수 없는 문자가 포함되어 있는 경우
Ex. Integer.parseInt("1200원");
- ClassCastException
'(자식) 부모' OR '(구현클래스) 인터페이스'로 강제 형변환할 때, 알맞은 자동 형변환이 선행되지 않은 경우
방지 : if (부모 instanceof 자식) 조건식으로 자동 형변환을 조사하면 된다.

*예외가 발생하면, 콘솔창에서 예외가 발생한 클래스와 함께 예외가 발생한 line을 :와 함께 알려준다.


10-2 예외 처리


▶ try - catch - finally 블록
정상 실행되었을 때 : try 블록 실행 후 finally 블록 실행
예외가 발생되었을 때 : try 블록 실행 중 예외가 발생하면 이후 코드를 실행하지 않고 catch 블록으로 넘어가고 이후 finally 블록을 실행한다.

try - catch - finally 블록은 일반 예외와 실행 예외 모두를 처리할 수 있다. 
일반 예외는 컴파일러가 예외 처리문을 검사하므로 반드시 해줘야 하는 것이다. 
또 이클립스에서도 빨간 줄로 예외를 알려주므로 예외 처리가 상대적으로 쉽다. 
하지만, 실행 예외는 실행 전에는 어떤 예외가 발생할지 모르는 상태이므로 개발자의 경험에 의해 예외처리를 해줘야 한다.

앞서 배웠던 것처럼, 예외가 발생하면 JVM은 해당 예외 클래스의 객체를 생성하여 예외 처리 코드로 넘겨준다. 
따라서 catch문은 매개변수처럼 해당 예외 객체를 받아 처리한다.

// 문법
try {
} catch (예외클래스 e) {
} finally {
}

 


* 이때 예외 클래스의 자리에 Exception을 적어주면 모든 예외를 받을 수 있다. 
앞에서 배웠듯이, 모든 예외 객체는 Exception 클래스를 상속하기 때문에 자동 형변환으로 Exception은 모든 예외를 받을 수 있는 것이다.


▶ 다중 catch 블록
하나의 try 안에서 다양한 종류의 예외가 발생할 수 있을 때, 여러 catch를 만들어줄 수 있다. 
이때 실행되는 catch는 하나 뿐이다. 
(try 실행 중 예외가 발생하면 그 즉시 해당 catch 블록으로 가고, try 블록에서는 동시다발적인 예외가 발생하지 않으므로) 
하나의 catch 블록에서 or 연산자 || 를 이용해 두 종류 이상의 예외를 잡을 수도 있다.

실행할 catch 블록은 위에서부터 차례로 검색된다. 
따라서 상위 예외 클래스와 하위 예외 클래스가 있을 때, 
상위 예외 클래스를 위에 적으면 자동 형변환 때문에 하위 예외 클래스가 무의미해지므로, 
상위 예외 클래스는 하위 예외 클래스보다 아래에 위치해야 한다.
Ex. catch (Exception e) { ... } 블록이 맨 위에 있다면, 
어떤 예외든 Exception 클래스의 하위 클래스이므로 아래 catch 블록들은 실행되지 않는 문제가 발생한다.
따라서 일반적인 경우 catch (Exception e) { ... } 블록을 catch 블록 가장 아래에 위치하게 하여,
위에서 잡히지 않은 모든 예외를 받게 한다.

전달된 예외 객체는 변수 e로 받아지는데, 이때 아래와 같은 메소드를 이용해 예외 객체를 사용할 수 있다.



▶ throws 키워드
사용하는 이유 : 메소드 안에서 발생한 예외를 내부에서 처리하지 않고 외부로 넘기기 위해
기능 : 메소드 안에서 예외가 발생하면, 예외 객체를 생성해 메소드를 호출한 try 블록으로 전달한다. 
try 블록에서는 예외가 발생한 즉시 실행을 멈추고 catch 블록으로 예외 객체를 전달한다. 
이처럼 메소드를 호출한 부분에서 try-catch로 예외를 처리해도 되지만, 
다시 throws 키워드를 이용해 상위 코드로 예외 객체를 전달하는 방법도 있다.
문법 : 메소드 선언부에서 throws 키워드로 발생할 수 있는 예외 클래스를 선언한다. 콤마를 이용해 여러 예외 클래스를 선언할 수 있다.

👉🏻 정리 : 메소드 안에서 발생한 예외를 / 메소드를 선언한 try 문으로 던져서 / 곧바로 catch 문에서 처리하게 한다.

*프로그램을 실행하는 main( ) 함수에서도 throws를 선언할 수 있다. 
이때 JVM이 최종적으로 예외 객체를 받게 되는데, JVM은 예외의 내용을 콘솔에 출력하는 것으로 예외를 처리한다. 
하지만, 사용자 입장에서 알 수 없는 내용이 출력되고 프로그램이 종료되는 것은 좋지 않으므로, 
main( ) 함수 안에서 try-catch 블록으로 최종 예외 처리를 하는 것이 바람직하다.