포스트

[JAVA] 프로그램 오류 및 예외 처리

프로그램 오류

프로그램 오류가 발생할 경우 프로그램 문제를 야기시킨다.
그러면 버그가 발생하거나 프로그램이 강제종료되는 경우가 있다.

프로그래밍에서 오류가 발생하는 시점 3가지가 있다.

  1. 컴파일 에러(Compilation error) : 컴파일 시 발생하는 오류
  2. 런타임 에러(Runtime error) : 실행 시 발생하는 오류
  3. 논리적 에러(Logical error) : 작성 의도와 다르게 동작

컴파일 에러(Compilation error)

컴파일러는 구문체크, 번역, 최적화를 계산한다.
자주 발생하는 컴파일 에러 중 하나는 구문 오류(syntax error)이다.

컴파일 에러는 IDE(인텔리제이, 이클립스 등)에서 자동으로 컴파일 체크를 해주기 때문에 금방 오류를 확인할 수 있다.

런타임 에러(Runtime error)

에러와 예외

  • 에러(error) : 프로그램 코드에 의해 수습될 수 없는 심각한 오류
  • 예외(exception) : 프로그램 코드에 의해 수습될 수 있는 다소 미약한 오류

  • 예외처리 정의와 목적
    • 정의 : 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것
    • 프로그램 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것

논리적 에러(Logical error)

일명 ‘버그’다. 게임을 해봤던 사람들이라면 한 번쯤은 겪었을 것이다.
프로그램 실행은 잘 되지만 예기치 못한 문제가 발생한다.
작업이 생각한 의도와 다르게 수행된 경우이다.
이 경우 프로그램은 문제 없는 것으로 인식하기 때문에 개발자가 미리 확인을 하는 것이 좋다.

버그 예시

자바의 예외처리

프로그램 오류가 발생할 경우 예기치 못한 종료가 발생할 수 있지만 일부 경우 예외처리로 방지할 수 있다.

try-catch문

try-catch문은 아래 코드처럼 작성할 수 있다.
catch 괄호 안에 Exception을 사용하면 모든 예외를 처리할 수 있다.

1
2
3
4
5
6
7
try {
	// 예외가 발생할 수 있는 문장
} catch (Exception1 e1) {
	// Exception1에서 에러가 발생했을 경우, 처리해야 될 문장
} catch {
	// 에러가 발생했을 경우, 처리해야 될 문장
}

try-catch문은 문장이 하나더라도 괄호 생략이 불가능하다.

  • try 블록에서 예외가 발생하였을 경우
    • 발생한 예외와 일치하는 catch문이 있는지 확인한다.
    • 일치하는 catch문이 있다면 수행하고 해당 try-catch문을 빠져나와 다음 문장을 수행한다.
    • 만약 일치하는 catch문이 없다면 예외 처리되지 못한다.
  • try 블록에서 예외가 발생하지 않은 경우
    • catch문은 생략하고 다음 문장을 이어서 수행한다.
1
2
3
4
5
6
7
8
9
10
11
12
public class Main {  
    public static void main(String[] args) {  
        try {    
            System.out.println("3");  
            // 강제적으로 예외 발생
            throw new ArithmeticException();  
            System.out.println("4");  
        } catch (ArithmeticException e) {  
            System.out.println("오류발생");  
        }  
    }  
}

위 코드에 강제적으로 예외를 발생시켜 보았다.
코드를 실행하면 “3”이 출력되고 예외가 발생하여 “4” 출력은 넘어가게 된다.
catch문에서 발생한 예외와 맞는 예외 값을 찾는다.
그러므로 “3” 출력 후 “오류발생”이라는 문자열을 출력하게 된다.

멀티 catch블록

멀티 catch블록은 여러 catch블록을 '|' 기호를 이용하여, 내용이 같은 catch블록을 하나로 합친 것이다.

1
2
3
4
5
6
7
try {
	...
} catch (Exception1 e) {
	System.out.println(e);
} catch (Exception2 e) {
	System.out.println(e);
}

위 코드처럼 Exception1Exception2의 예외 발생처리가 같을 때 멀티 catch 블록을 사용하면 중복된 코드를 줄일 수 있다.

1
2
3
4
5
6
// 멀티 catch 블록 사용
try {
	...
} catch (Exception1 | Exception2 e) {
	System.out.println(e);
}
  • 하지만 멀티 catch 블록에 사용될 클래스가 부모 자식 관계일 경우 컴파일 에러가 난다.

예외 발생시키기

연산자 new를 이용해 발생시키려는 예외 클래스의 객체를 만들어 throw 키워드를 사용해 예외를 발생시킬 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {  
    public static void main(String[] args) {  
        try {   
            System.out.println("Hi");  
            throw new ArithmeticException("111");  
        } catch (ArithmeticException e) {
            System.out.println("오류코드 : " + e);
        }  
    }  
}

> Hi
> 오류코드 : 111

printStackTrace()와 getMessage()

  • printStackTrace()
    • 예외발생 당시 호출스택에 있던 메소드의 정보와 예외 메세지를 출력
  • getMessage()
    • 발생한 예외클래스의 인스턴스에 저장된 메세지를 얻을 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {  
    public static void main(String[] args) {  
        try { 
            System.out.println("3");  
            System.out.println("4");  
            throw new ArithmeticException("오류");  
        } catch (ArithmeticException e) {
            System.out.println(e.getMessage());   
            e.printStackTrace();   
        }  
    }  
}

> 3
> 4
> 오류
> java.lang.ArithmeticException: 오류
   	at Main.main(Main.java:6)
  • 3과 4는 정상적으로 출력된다.
  • 이후 예외발생으로 해당 예외발생에 맞는 catch문을 수행하게 된다.
  • 예외발생 메세지로 “오류”를 작성하여 getMessage()로 "오류"라는 문자열이 출력하게 된다.
  • 이후 printStackTrace()로 참조변수를 통해 생성된 인스턴스에 접근하여 예외발생 내용을 화면에 출력이 되었다.

Checked Exception & Unchecked Exception

자바의 예외는 컴파일 에러, 런타임 에러, 논리적 에러가 있다.
근데 예외 종류에도 Checked Exception과 Unchecked Exception이 있다.
간단히 아래 표로 이해할 수 있다.

 Checked ExceptionUnchecked Exception
설명컴파일 예외클래스들을 가리키는 것런타임 예외클래스들을 가리키는 것
처리 여부반드시 예외 처리명시적 처리 안 해도 됨

메소드 예외 선언

  • 현재까지는 예외 처리 방법이 2가지(try-catch,예외 선언)가 있었다.
  • 메소드 호출 시 발생가능한 예외를 호출하는 곳으로 알리는 것
    • throw : 예외 발생시키는 키워드
    • throws : 메소드 예외 선언 시 사용

finally블록

try-catch에 이어 예외 발생여부 관계없이 수행되어야 하는 코드를 작성할 수 있다.
finally블록은 try-catch 맨 마지막에 위치해야 한다.

1
2
3
4
5
6
7
try {
	// 예외 처리가 발생할 수 있는 코드
} catch () {
	// 해당 예외 발생 시 처리해 줄 코드
} finally () {
	// 예외 발생 여부 상관없이 수행되어야되는 코드
}

사용자 정의 예외 만들기

직접 예외 클래스를 정의할 수 있다.
부모는 Exception, RuntimeException 중에 선택해야 한다.

1
2
3
4
5
6
class MyException extends Exception {
	// 사용자가 발생시키는 예외
	MyException(String msg){ // 문자열을 매개변수로 받는 생성자
		super(msg); // 부모 Exception 클래스의 생성자 호출
	}
}

연결된 예외

  • 예외를 다른 예외로 감싸는 것
  • Checked Exception를 Unchecked Exception로 변경

  • 필수처리를 선택처리로 변경할 때 사용된다.
  • 선택처리로 변경하게 된다면 예외처리를 강제로 할 필요가 없게 할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
class SpaceException extends Exception {
	SpaceException (String msg) {
		super(msg);
	}
}

static void startInstall() throws SpaceException, MemoryException {
	if(!enoughSpace())
		throw new SpaceException("공간 부족");
	if(!enoughMemory())
		// Checked 예외를 Unchecked 예외인 RuntimeException으로 감싸 Unchecked 예외로 변경한다.
		throw new RuntimeException(new MemoryException("메모리 부족"));
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.