랩터
[Java] 예외 처리 본문
학습 목표
- 프로그래밍에서 예외 처리가 무엇인지 이해할 수 있다.
- 컴파일 에러와 런타임 에러의 차이를 이해하고 설명할 수 있다.
- 자바 예외 클래스의 상속 계층도를 통해 클래스 간 관계를 이해할 수 있다.
- 자바의 핵심적인 예외 처리 방법인 try-catch 문과 예외 전가에 대해 이해하고 설명할 수 있다.
- throws 키워드와 throw 키워드의 차이에 관해 설명할 수 있다.
예외 처리란?
에러가 발생하는 원인은 수없이 다양하지만, 다음의 몇 가지 예를 들 수 있습니다.
- 사용자의 입력 오류
- 네트워크 연결 끊김
- 디스크 메모리 공간 부족 등 물리적 한계
- 개발자의 코드 에러
- 존재하지(유효하지) 않는 파일 불러오기
위의 원인을 다시 크게 내부적인 요인과 외부적인 요인으로 구분할 수 있습니다.
package polymorphism_example.Exception;
import java.io.BufferedReader;
import java.io.FileReader;
public class ErrorTest {
public static void main(String[] args) {
BufferedReader notExist = new BufferedReader(new FileReader("없는 파일"));
notExist.readLine();
notExist.close();
}
}
다음 코드를 실행하면 다음과 유사한 에러메시지를 확인할 수 있습니다.
java: unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
FileNotFoundException 이라는 이름에서 확인할 수 있는 것처럼, 실제로 존재하지 않는 파일을 불러오려 시도할 때 발생하는 예외입니다.
public class ErrorTest {
public static void main(String[] args) {
int[] array = {2,4,6};
System.out.println(array[3]);
}
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at ErrorTest.main(ErrorTest.java:4)
다음 코드는 실행하면 ArrayIndexOutOfBoundsException이라는 에러 메시지가 뜹니다.
배열의 범위를 벗어난 값을 불러오고자 시도할 때 발생하는 예외입니다.
두 코드 둘다 에러가 발생하지만, 첫번째 코드는 실행하기도 전에 빨간색 줄이 나와 무언가 잘못되었음을 알려주는 반면,
두 번째 코드에서는 아무런 경고 없이 실제로 코드를 실행한 시점에 도달해서야 에러 발생을 알리고 있습니다.
그 이유는 에러 발생 시점이 달라서 그렇습니다. 발생시점에 따라 컴파일 에러와 런타임 에러로 구분하고 있습니다.
컴파일 에러와 런타임 에러
컴파일 에러
먼저, 컴파일 에러는 이름 그대로 “컴파일할 때" 발생하는 에러를 가리킵니다.
주로 문법적인 문제를 가리키는 신택스 오류로부터 발생합니다.
컴파일 에러는 자바 컴파일러가 오류를 감지하여, 사용자에게 친절하게 알려주기 때문에 상대적으로 쉽게 발견하고 수정할 수 있습니다.
컴파일 에러는 상대적으로 발견하기도 쉽고, 어렵지 않게 해결할 수 있습니다.
런타임 에러
런타임 에러 또한 이름에서 손쉽게 유추할 수 있는 것처럼 코드를 실행하는 과정, 즉 런타임 시에 발생하는 에러를 가리킵니다.
public class RuntimeErrorTest {
public static void main(String[] args) {
System.out.println(4 * 4);
System.out.println(4 / 0); // 예외 발생
}
}
//출력값
16
Exception in thread "main" java.lang.ArithmeticException: / by zero
at RuntimeErrorTest.main(RuntimeErrorTest.java:5)
위 코드를 실행해보면 0으로 나눴을 때 발생하는 예외인 ArithmeticException예외가 발생합니다.
런타임 에러는 주로 개발자가 컴퓨터가 수행할 수 없는 특정한 작업을 요청할 때 발생합니다. 또한 프로그램이 실행될 때 자바 가상 머신(JVM)에 의해 감지됩니다.
에러와 예외
자바에서는 코드 실행(run-time) 시 잠재적으로 발생할 수 있는 프로그램 오류를 크게 에러(error)와 예외(exception)으로 구분하고 있습니다.
에러란 한번 발생하면 복구하기 어려운 수준의 심각한 오류를 의미하고, 대표적으로 메모리 부족(OutOfMemoryError)과 스택오버플로우(StackOverflowError) 등이 있습니다.
반면 예외는 잘못된 사용 또는 코딩으로 인한 상대적으로 미약한 수준의 오류로서 코드 수정 등을 통해 수습이 가능한 오류를 지칭합니다.
예외 클래스의 상속 계층도
자바에서는 예외가 발생하면 예외 클래스로부터 객체를 생성하여 해당 인스턴스를 통해 예외 처리를 합니다.
모든 예외의 최고 상위 클래스인 Exception 클래스는 다시 크게 일반 예외 클래스와 실행 예외 클래스로 나눌 수 있습니다.
try - catch문
자바에서 예외 처리는 try-catch 블록을 통해 구현할 수 있습니다. 그 기본적인 구조는 다음과 같습니다.
try {
// 예외가 발생할 가능성이 있는 코드를 삽입
}
catch (ExceptionType1 e1) {
// ExceptionType1 유형의 예외 발생 시 실행할 코드
}
catch (ExceptionType2 e2) {
// ExceptionType2 유형의 예외 발생 시 실행할 코드
}
finally {
// finally 블록은 옵셔널
// 예외 발생 여부와 상관없이 항상 실행
}
모든 예외를 받을 수 있는 Exception 클래스 하나로 처리도 가능하며, 각기 다른 예외를 하나 이상의 catch 블록을 사용하여 처리할 수 있습니다.
public class RuntimeExceptionTest {
public static void main(String[] args) {
System.out.println("[소문자 알파벳을 대문자로 출력하는 프로그램]");
printMyName("abc"); // (1)
printMyName(null); // (2) 넘겨주는 매개변수가 null인 경우 NullPointerException 발생
System.out.println("[프로그램 종료]");
}
static void printMyName(String str) {
String upperCaseAlphabet = str.toUpperCase();
System.out.println(upperCaseAlphabet);
}
}
//출력값
[소문자 알파벳을 대문자로 출력하는 프로그램]
ABC //(3) 정상 실행
Exception in thread "main" java.lang.NullPointerException // (4) 예외 발생!
at RuntimeExceptionTest.printMyName(RuntimeExceptionTest.java:11)
at RuntimeExceptionTest.main(RuntimeExceptionTest.java:6)
다음 코드를 try-catch문을 사용하여 예외 처리를 할 수 있습니다.
public class RuntimeExceptionTest {
public static void main(String[] args) {
try {
System.out.println("[소문자 알파벳을 대문자로 출력하는 프로그램]");
printMyName(null); // (1) 예외 발생
printMyName("abc"); // 이 코드는 실행되지 않고 catch 문으로 이동
}
catch (ArithmeticException e) {
System.out.println("ArithmeticException 발생!"); // (2) 첫 번째 catch문
}
catch (NullPointerException e) { // (3) 두 번째 catch문
System.out.println("NullPointerException 발생!");
System.out.println("e.getMessage: " + e.getMessage()); // (4) 예외 정보를 얻는 방법 - 1
System.out.println("e.toString: " + e.toString()); // (4) 예외 정보를 얻는 방법 - 2
e.printStackTrace(); // (4) 예외 정보를 얻는 방법 - 3
}
finally {
System.out.println("[프로그램 종료]"); // (5) finally문
}
}
static void printMyName(String str) {
String upperCaseAlphabet = str.toUpperCase();
System.out.println(upperCaseAlphabet);
}
}
// 출력값
[소문자 알파벳을 대문자로 출력하는 프로그램]
NullPointerException 발생!
e.getMessage: Cannot invoke "String.toUpperCase()" because "str" is null
e.toString: java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "str" is null
프로그램 종료
java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "str" is null
at Exception.RuntimeExceptionTest.printMyName(RuntimeExceptionTest.java:25)
at Exception.RuntimeExceptionTest.main(RuntimeExceptionTest.java:7)
발생한 예외가 NullPointerException인데, 첫 번째 catch 문에서 조건으로 받는 예외는 ArithmeticException이므로 예외처리가 되지 않고 그냥 지나갑니다.
두번째 catch문에서 에러에 대한 정보를 얻는 방법 세가지가 나와있습니다.
e.getMessage() : 에러의 원인을 간단하게 출력합니다.
e.toString() : 에러의 Exception 내용과 원인을 출력합니다.
e.printStackTrace() : 에러의 발생근원지를 찾아서 단계별로 에러를 출력합니다.
예외 전가
try-catch 문 외에 예외를 호출한 곳으로 다시 예외를 떠넘기는 방법도 있습니다.
메서드의 선언부 끝에 아래와 같이 throws 키워드와 발생할 수 있는 예외들을 쉼표로 구분하여서 나열해 주면 됩니다
특정 메서드에서 모든 종류의 예외가 발생할 가능성이 있는 경우 아래와 같이 작성할 수 있습니다.
void ExampleMethod() throws Exception {
}
package Exception;
public class ThrowExceptionTest {
public static void main(String[] args) {
try {
throwException();
}catch (ClassNotFoundException e){
System.out.println(e.getMessage());
}
}
static void throwException() throws ClassNotFoundException, NullPointerException{ // 아래에서 예외 발생시 예외 전가시킴 어디로? 호출한 곳으로
Class.forName("java.lang.StringX");
}
}
해당 코드는 살펴보면, 먼저try문을 실행을 합니다. try문 안에 있는 throwException() 메서드가 실행되고 throwException()메서드로 넘어가서 Class.forName(...); 부분을 실행시킵니다. 근데 여기서 ClassNotFoundException예외가 발생합니다. throws로 예외를 전가시켜줬기 때문에 나를 호출시킨 try문에 예외를 던집니다. try문에서 예외가 발생했으므로 catch문을 실행시킵니다.
만약 throwException안에서 try-catch문이 있다면 throws를 같이 써줄 필요가 없습니다. 예외처리를 메서드안에서 해줬기 때문에 예외전가를 할 필요가 없기 때문입니다.
예외를 의도적으로 발생시키기
throw 키워드를 사용하면 의도적으로 예외를 발생시킬수도 있습니다.
package Exception;
public class ExceptionTest {
public static void main(String[] args) {
try {
Exception intendedException = new Exception("의도된 예외 만들기");
throw intendedException;
}catch (Exception e){
System.out.println("고의로 예외 발생시키기 성공!");
}
}
}
throw는 Exception을 던질 때 예외 내용도 던져주지 않습니다. 그래서 개발자가 직접 Exception을 따로 커스터마이징해서 만들고 그 안에 메시지를 넣어서 던져주는 방식입니다.
이렇게 자바에서는 개발자의 실수 등으로 예기치 않게 발생할 수 있는 에러에 대응할 수 있는 코드를 미리 사전에 작성하여 프로그램의 오작동과 비정상적인 종료를 방지하는 예외 처리 방법을 제공하고 있습니다.
'공부 > JAVA' 카테고리의 다른 글
[Java] 람다 (1) | 2024.07.05 |
---|---|
[Java] 컬렉션 프레임워크 (Collection Framework) (0) | 2024.07.04 |
[Java] 제네릭 (0) | 2024.07.03 |
[Java] 열거형 (0) | 2024.07.03 |
[Java] [레스토랑 키오스크 프로그램] (0) | 2024.07.02 |