Contents
Java - 예외
   Nov 9, 2022     21 min read

예외

try-catch

  • 예제를 통해서 알아보자
public class ExceptionSample {

    public static void main(String[] args) {
        ExceptionSample sample = new ExceptionSample();
        sample.arrayOutOfBounds();
    }

    public void arrayOutOfBounds(){
        int[] arr = new int[5];
        System.out.println(arr[5]);
    }
  • 결과
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
	at main.Chapter14.ExceptionSample.arrayOutOfBounds(ExceptionSample.java:16)
	at main.Chapter14.ExceptionSample.main(ExceptionSample.java:7)

위 코드를 보면 배열의 크기는 5 이다. 출력하려고 하는 것은 arr[5]다. 배열은 0부터 시작하기 때문에 arr[5] 는 존재 하지 않는다. 따라서 결과의 에러 ArrayIndexOutOfBoundsException 가 나온다.

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5

호출 관계의 가장 윗줄에는 예외가 발생한 클래스와 메소드 이름(main)과 배열의 범위를 벗어났다고 출력한다.

	at main.Chapter14.ExceptionSample.arrayOutOfBounds(ExceptionSample.java:16)
	at main.Chapter14.ExceptionSample.main(ExceptionSample.java:7)

그 메소드를 호출한 클래스(ExceptionSample)와 메소드의 이름(arrayOutOfBounds) 및 줄의 번호(16)가 출력한다.

위의 메시지에서 호출 관계가 단순하여 두 개의 스택 트레이스(at 으로 시작하는 것)만 찍혔다. 하지만 실제 운영되는 자바 기반의 시스템들은 그렇게 단순하지 않기 때문에 몇 십 줄에서 백 줄까지 출력되기도 한다.

그렇다면, 이와 같은 예외 메시지가 발생하지 않도록 할 수는 없을까? 라는 의문점이 든다.

해결법은 있다. → try-catch 문

  • 예제를 통해서 알아보자.
public void arrayOutOfBoundTryCatch(){
    try{
      int[] intArray = new int[5];
      System.out.println(intArray[5]);
    }catch(Exception e){

    }
}

위의 코드는 처음 썻던 코드에다가 try-catch문 넣었다.

  • try 뒤에는 중괄호로 예외가 발생하는 문장들을 묶어준다.
  • catch 괄호 안에 예외가 발생했을 때 처리를 해준다.

  • try-catch에서 코드 순서는 어떻게 될까 ?

위 코드에서 예외처리가 되는 코드 라인 밑에다가 출력코드를 추가해보자.

Untitled

컴파일도 되고 작동도된다. 결과는 ??

결과 :

아무것도 없다. 위 그림에서 보듯 순서는 예외처리가 걸리면 catch로 넘어간다.

따라서 아래 같은 코드는 출력되지 않는다.

System.out.println("This code should run.");

예제를 하나 더 들어가서 굳히기 들어가보자.

  • 예제
public void arrayOutOfBoundsTryCatch() {
    int[] intArray = new int[5];
    try{
       System.out.println(intArray[5];
       System.out.println("This code should run.");
    } catch(Exception e) {
       System.err.println("Exception occured.");
    }
       System.out.println("This code must run.");
}
  • 작동 과정

Untitled 1

  • 결과

Untitled 2

System.err.println("Exception occured.");

System.err 는 출력할 때 다른 색깔로 출력한다. (여기서는 빨간색)

지금까지 알아본 내용들을 정리해보면

  • try-catch에서 예외가 발생하지 않을경우
    • try 내에 있는 모든 문장이 실행되고 try-catch문장 이후의 내용이 실행된다.
  • try-catch에서 예외가 발생하는 경우
    • try 내에서 예외가 발생한 이후의 문장들은 실행되지 않는다.
    • catch 내에 있는 문장은 반드시 실행되고, try-catch 문장 이후의 내용이 실행된다.

try-catch 변수 선언 및 범위

try-catch를 사용할 때 변수의 지정을 잘해야한다.

try 블록은 중괄호가 있는 블록이다. 따라서 try 블록 내에서 선언한 변수를 catch에서 사용할 수 없다.

예시를 보면서 알아가보자.

public class ExceptionVariable{
    public void checkVariable(){
      int[] arr = new int[5];
      try{
        System.out.println(arr[5]);
      }catch(Exception e){
        System.err.println(arr.length);
      }
        System.out.println("이 코드는 반드시 실행된다.");
    }
}
  • 작동 과정

Untitled 3

  • 결과
5
 코드는 반드시 실행된다.

그렇다면 inArray 변수가 try{} 안에 있을 경우 어떻게 될까?

public void checkVariable2(){
   try{
      int[] intArray = new int[5];
      System.out.println(intArray[5]);
   }catch(Exception e){
      System.out.println(intArray.length);
   }
      System.out.println("이 코드는 반드시 실행된다.");
}
  • 컴파일 에러가 발생한다. 왜? 변수의 범위 때문이다. intArray 을 찾을 수 없다고 한다.
System.out.println(arr.length);

이유는 intArray가 try 블록 안에서 선언되었기 때문에 catch에서는 intArray를 사용할 수 없다.

그렇다면 어떻게 해결할까? try 앞에 미리 선언해 놓으면 된다!

public void checkVariable2(){
   int[] intArray = null;
   try{
      intArray = new int[5];
      System.out.println(intArray[5]);
   }catch(Exception e){
      System.out.println(intArray.length);
   }
      System.out.println("이 코드는 반드시 실행된다.");
}
int[] intArray = null; // 위치를 바꾸고 null값을 선언

intArray = new int[5];   // 배열 생성
  • 결과
5
 코드는 반드시 실행된다.

정리

  • try { } : 중괄호 블록이다 라는 걸 명심
  • catch 에서 사용하는 변수는 try 앞에 선언해야 한다.

finally : 반드시 실행되어야 한다.

try-catch 구문에서 추가로 붙을 수 있는 블록이 하나 더 있다.

finally 이다.

finally의 단어 뜻은 “드디어, 마침내” 라는 뜻을 갖고 있다.

이런 것을 보면 진짜 프로그래밍은 언어라는 말이 와닿는다.

  • finally 예를 들어보자
public class FinallySample {
    public static void main(String[] args) {
        FinallySample sample = new FinallySample();
        sample.finallySmaple();
    }

    public void finallySmaple(){
        int[] arr = new int[5];
        try {
            System.out.println(arr[5]);
        } catch (Exception e) {
            System.out.println(arr.length);
        } finally {
            System.out.println("Here is finally");
        }
        System.out.println("This code must run.");
    }
}
  • 동작 과정

Untitled 4

  1. 예외가 발생한다. → System.out.println(arr[5])
  2. catch블록 실행 → System.out.println(arr.length)
  3. finally 블룩 실행 → System.out.println("Here is finally")
  4. 마지막 출력부분 실행 → System.out.println("This code must run.")
  • 결과
5
Here is finally
This code must run.

그렇다면 코드 과정중 예외가 발생하지 않는다면 finally 블록은 실행이 될까?

  • 위 코드를 아래와 같이 수정한다.
public void finallySmaple2(){
    int[] arr = new int[5];
      try {
        System.out.println(arr[4]);
      } catch (Exception e) {
        System.out.println(arr.length);
      } finally {
        System.out.println("Here is finally");
      }
    System.out.println("This code must run.");
}
  • 수정 부분
System.out.println(arr[4]);
  • 결과
0
Here is finally
This code must run.

배열의 초기화만 하면 각 타입의 기본값이 할당된다. int의 기본값은 0이다.

따라서 finally 블록은 예외 발생 여부와 상관 없이 실행된다.

그렇다면 finally 는 어디에 활용할까?


두 개 이상의 catch

try-catch 문을 사용할 때 catch 블록이 시작되기 전에(중괄호 시작되기 전에) 있는 소괄호에는 예외의 종류를 명시한다.

따라서 항상 Exception e 를 쓰는 것이 아니다.

  • 예를 통해서 알아보자
public class MultiCatchSample {
    public static void main(String[] args) {
        MultiCatchSample sample = new MultiCatchSample();
        sample.multiCatch();
    }

    public void multiCatch(){
        int[] intArray = new int[5];
        try {
            System.out.println(intArray[5]);
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("ArrayIndexOutOfBoundsException occurred");
        }catch(Exception e){
            System.out.println(IntArray.length);
        }
    }
}
  • 결과
ArrayIndexOutOfBoundsException occurred

결과를 보면 앞에 catch 만 처리하였다.

catch 블록은 순서가 중요하다. catch 블록은 순서를 따진다.

  • 메소드의 catch 블록의 순서를 바꾼 메소드를 보자.
public void multiCatch3(){
    int[] intArray = new int[5];
      try {
          System.out.println(intArray[5]);
      }catch(Exception e){
          System.out.println(intArray.length);
      }catch(ArrayIndexOutOfBoundsException e) {
          System.out.println("ArrayIndexOutOfBoundsException occurred");
      }
    }
}
  • 결과
java: exception java.lang.ArrayIndexOutOfBoundsException has already been caught

이미 잡았다 라는 메시지가 결과에 도출된다. 순서만 바꿨는데 이런 에러가 뜬다. 왜 그럴까?

에러가 뜬 이유는 부모 예외 클래스가 이미 catch를 하고, 자식 클래스가 그 아래에서 catch를 하도록 되어 있을 경우에는 자식 클래스가 예외를 처리할 기회가 없다.

Exception 클래스가 모든 클래스의 부모 클래스이고, 배열에서 발생시키는 ArrayIndexOutOfBoundsException 은 Exception 클래스의 자식 클래스이기 때문에 절대로 Exception 클래스로 처리한 catch 블록 이후에 선언한 블록은 처리될 일이 없다.

  • NullPointerException 추가 했다.
public void multiCatch4(){
    int[] intArray = new int[5];
      try{
          System.out.println(intArray[5]);
      }catch(NullPointerException e){
          System.out.println("NullPointerException occurred");
      }catch(ArrayIndexOutOfBoundsException e){
          System.out.println("ArrayIndexOutOfBoundsException occurred");
      }catch(Exception e){
          System.out.println(intArray.length);
      }
    }
}
  • 결과
ArrayIndexOutOfBoundsException occurred
  • NullPointerException 으로 선언한 catch 블록은 그냥 건너 뛰고, 그 다음에 있는 catch 문장을 살펴보도록 되어 있다. 그래서 두 번째에 있는 예외가 처리된 것이다.
public void multiCatch5(){
    int[] intArray = new int[5];
      try{
          intArray = null;
          System.out.println(intArray[5]);
      }catch(NullPointerException e){
          System.out.println("NullPointerException occurred");
      }catch(ArrayIndexOutOfBoundsException e){
          System.out.println("ArrayIndexOutOfBoundsException occurred");
      }catch(Exception e){
          System.out.println(intArray.length);
      }
    }
}
  • 결과
NullPointerException occurred

이런 결과가 나온 이유는 intArray의 5번째 값을 찾는 작업을 하기 전에 해당 객체가 null인지 확인하는 작업을 먼저 한다.

null인 객체를 갖고 작업하면 안 되기 때문에 해당 객체가 null인지 확인하는 작업은 반드시 먼저 선행되어야 한다.

따라서 ArrayIndexOutOfBoundsException 예외가 발생하기도 전에 NullPointerException 이 먼저 발생한다.

먼저 예외가 발생해버렸기 때문에 나머지 try 블록에 있는 내용은 모두 무시된다.

  • NullPointerException 부분을 주석처리할 경우
public void multiCatch6(){
    int[] intArray = new int[5];
        try {
          intArray = null;
          System.out.println(intArray[5]);
        }catch(ArrayIndexOutOfBoundsException e){
          System.out.println("ArrayIndexOutOfBoundsException occurred");
        }catch(Exception e){
          System.out.println("Exception occurred");
    }
}
  • 결과
Exception occurred

Untitled 5

실제로는 NullpointerException 이 발생했지만 Exception 클래스가 가장 마지막에 버티고 있기 때문에 예외가 걸러진 것이다.

그렇다면 마지막 Exception catch가 없다면?

public void multiCatch7(){
    int[] intArray = new int[5];
    try {
        intArray = null;
        System.out.println(intArray[5]);
    }catch(ArrayIndexOutOfBoundsException e){
        System.out.println("ArrayIndexOutOfBoundsException occurred");
    }
}
  • 결과
Exception in thread "main" java.lang.NullPointerException
	at main.Chapter14.MultiCatchSample.multiCatch7(MultiCatchSample.java:95)
	at main.Chapter14.MultiCatchSample.main(MultiCatchSample.java:12)

try-catch로 묶은 것이 필요없게 되면서 예외 로그를 발생시킨다.

정리

  • try 다음에 오는 catch 블록은 1개 이상 올 수 있다.
  • 먼저 선언한 catch 블록의 예외 클래스가 다음에 선언한 catch 블록의 부모에 속하면, 자식에 속하는 catch 블록은 절대 실행될 일이 없으므로 컴파일이 되지 않는다.
  • 하나의 try 블록에서 예외가 발생하면 그 예외와 관련이 있는 catch블록을 찾아서 실행한다.
  • catch 블록 중 발생한 예외와 관련있는 블록이 없으면, 예외가 발생되면서 해당 쓰레드가 끝난다. 따라서 마지막 catch 블록 Exception 클래스로 묶어주는 버릇을 들여 놓으면 안전한 프로그램이 될 수 있다.

예외의 종류 세 가지

  • checked exception
  • error
  • runtime exception 혹은 unchecked exception

error

Exception 클래스는 에러가 아니다.

뭔가 자바 프로그램에 오류가 발생했을 때,

  • 오류의 이름이 Error로 끝나면 에러이다.
  • Exception으로 끝나면 예외다.

Error와 Exception으로 끝나는 오류의 가장 큰 차이는 프로그램 안에서 발생했는지, 밖에서 발생했는지 여부이다.

더 큰 차이는 프로그램이 머추어 버리느냐 계속 실행할 수 있느냐의 차이이다.

  • Error는 프로세스에 영향
  • Exception은 쓰레드에만 양향을 준다.

Runtime exception (런타임 예외)

  • 런타임 예외는 예외가 발생할 것을 미리 감지하지 못했을 때 발생한다.
  • 예외를 묶어주지 않는다고 해서 컴파일할 때 예외가 발생하지 않는다.
  • 실행 시에는 발생할 가능이 있는 예외들을 런타임 예외라고 한다.
  • 컴파일시에 체크를 하지 않기 때문에 unchecked exception이라고도 한다.

Untitled 6


모든 예외의 할아버지는 java.lang.Throwable 클래스다.

ExceptionError의 공통 부모 클래스는 Object 클래스다. 그리고 공통 부모 클래스가 또 있는데, 바로 java.lang 패키지에 선언된 Throwable 클래스다.

ExceptionError 클래스는 Throwable 클래스를 상속받아 처리하도록 되어 있다.

따라서 Exception이나 Error를 처리할 때, Throwable로 처리해도 무관하다.

상속 관계가 이렇게 되어 있는 이유는 Exception이나 Error 의 성격은 다르지만 모두 동일한 이름의 메소드를 사용하여 처리할 수 있도록 위함이다.

  • Throwable의 생성자
    • Throwable() : 매개 변수가 없는 생성자(기본 생성자)
    • Throwable(String message) : 에러 메시지를 String으로 보내준다.
    • Throwable(String message, Throwable cause)
    • Throwable(Throwable cause) : 별도의 예외의 원인을 Throwable 객체로 넘겨 줄 수 도 있다.
  • Throwable 클래스에서 Overriding한 메소드 중 많이 사용하는 메소드
    • getMessage()
    • toString()
    • printStackTrace()

getMessage()

예외 메시지를 String형태로 제공받는다. 예외가 출력되었을 때 어떤 예외가 발생되었는지를 확인할 때 유용하다.

toString()

예외 메시지를 String형태로 제공받는다. 그러덴 getMessage() 메소드보다는 약간 더 자세하게 예외 클래스 이름도 같이 제공한다.

printStackTrace()

가장 첫 줄에는 예외 메시지를 출력하고, 두 번째 줄부터는 예외가 발생하게 된 메소드들의 호출관계(스택 트레이스)를 출력해준다.

  • 예제를 통해서 알아보자
public class ThrowableSample{
    public static void main(String[] args){
        ThrowableSample sample = new ThrowableSample();
        sample.throwable();
    }

    public void throwable() {
        int[] inArray = new int[5];
        try{
            intArray = null;
            System.out.println(intArray[5]);
        }catch(Throwable T){
            System.out.println("               ");
        }
    }
}

catch 부분에 출력문에 getMessage() , toString() , printStackTrace() 를 넣어서 확인해보자.

여기서 tcatch 블록에 선언한 Throwable 클래스의 객체 이름이다. 이 클래스를 컴파일하고 실행하면 다음과 같은 결과가 출력된다.

null
  1. getMessage()
null
  1. toString()
java.lang.NullPointerException
  1. printStackTrace()
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
	at main.Chapter14.ThrowableSample.throwable(ThrowableSample.java:20)
	at main.Chapter14.ThrowableSample.main(ThrowableSample.java:6)

예외를 던지는 Throw, Throws

지금까지 예외를 처리하는 방법을 배웠다. 이제는 예외를 발생시키는 방법(예외를 던질수 있는 방법)을 알아보자.

  • 예제 - throw

public class ThrowSample{
    public static void main(String[] args){
        ThrowSample sample = new ThrowSample();
        sample.throwException(13);
    }

    public void throwException(int number) {
        try{
            if(number > 12){
            throw new Exception("NUmber is over than 12");
        }
          System.out.println("Number is " + number);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

Untitled 7

1년 12개월 처리하는 로직이 필요하다고 봤을 때, 메소드의 매개 변수로 13월이 넘어 온다.

그러면 if 조건을 만족하여

throw new Exception("NUmer is over than 12")

코드를 실행한다.

그렇게 되면 try 블럭 내에서 예외 클래스의 객체를 생성하면 된다. 그렇게 되면 try 블록은 수행은 끝나고 catch 블록으로 이동한다.

여기서는 number가 12보다 크면 예외를 던지고, 그 밑에 출력 문장은 수행되지 않는다.

catch 블록 중에 throw한 예외와 동일하거나 상속 관계에 있는 예외가 있다면 그 블록에서 예외를 처리할 수 있다.

e.printStackTrace()

위의 결과로 예외 스택 정보가 출력될 것이다.

java.lang.Exception: NUmber is over than 12
	at main.Chapter14.ThrowableSample.throwException(ThrowableSample.java:31)
	at main.Chapter14.ThrowableSample.main(ThrowableSample.java:7)
  • 예제 - throws

public class ThrowSample{
    public static void main(String[] args){
        ThrowSample sample = new ThrowSample();
        sample.throwException(13);
    }

    public void throwException(int number) {
        try{
            if(number > 12){
            throw new Exception("NUmber is over than 12");
        }
          System.out.println("Number is " + number);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

throws는 예외가 발생된 메소드를 호출한 메소드로 던진다는 의미이다.

  • throws 구문은 메소드 선언할 때 사용하면 된다.
public void throwsException(int number) throws Exception{
    if(number > 12){
        throw new Exception("Number is over than 12");
    }
    System.out.println("Number is " + number);
}

➡️throws의 장점 1

  • 이렇게 메소드 선언에 해놓으면, 예외가 발생했을 때 try-catch로 묶어주지 않아도 그 메소드를 호출한 메소드로 예외 처리를 위임하는 것이기 때문에 전혀 문제가 되지 않는다.

➡️throws의 장점 2

  • try-catch 블록으로 묶지 않아도 예외를 throw한다고 할지라고, throws가 선언되어 있기 때문에 전혀 문제없이 컴파일 및 실행이 가능하다.

따라서 아래와 같이 코드를 작성하면 컴파일 에러가 난다.

public class ThrowableSample {
    public static void main(String[] args) {
        ThrowableSample sample = new ThrowableSample();
        sample.throwException(13);
        sample.throwsException(13);
    }
}
  • 결과
java: unreported exception java.lang.Exception; must be caught or declared to be thrown

컴파일 에러 메시지를 보면 “caugth 되거나(catch 블록으로 막거나) throw 한다고 선언되어 있어야만 한다.”고 되어 있다.

throws문장으로 인한 컴파일 오류가 생겼을 경우에는 두 가지 방법

  • try-catch로 묶는법
public static void main(String[] args){
    ThrowSample sample = new ThrowSample();
    sample.throwException(13);
    try{
       sample.throwsException(13);
    }catch (Exception e) {

    }
}
  • 호출한 메소드(여기서는 main() 메소드)에서도 다시 throws 해버리면 된다.
public static void main(String args[]) throws Exception{
    ThrowSample sample = new ThrowSample();
    sample.throwException(13);
    sample.throwsException(13);
}

두 번째 방법은 권장하지 않는 방법이다. 가장 좋은 방법은 첫 번째 방법이다.

  • 방법으로 하고 나면 나오는 출력
java.lang.Exception: NUmber is over than 12
	at main.Chapter14.ThrowableSample.throwException(ThrowableSample.java:31)
	at main.Chapter14.ThrowableSample.main(ThrowableSample.java:7)

여러가지 예외를 던질 때 throws 구문에 예외 클래스를 여러 개 나열 할 수 있다.

public void multiThrows() throws NullPointerException, Exception{

}

정리

  • 메소드 선언할 때 매개 변수 소괄호 뒤에 throws라는 예약어를 적어 준 뒤 예외를 선언하면, 해당 메소드에서 선언한 예외가 발생했을 때 호출한 메소드로 예외가 전달된다.
  • 만약 메소드에서 두 가지 이상의 예외를 던질 수 있다면, implements 처럼 콤파로 구분하여 예외 클래스 이름을 적어주면 된다.

  • try 블록 내에서 예외를 발생시킬 경우에는 throw라는 예약어를 적어 준 뒤 예외 객체를 생성하거나, 생성되어 있는 객체를 명시해준다.
  • throw 한 예외 클래스가 catch 블록에 선언되어 있지 않거나. throws 선언에 포함되어 있지 않으면 컴파일 에러가 발생한다.

  • catch 블록에서 예외를 throw할 경우에도 메소드 선언의 throws 구문에 해당 예외가 정의되어 있어야만 한다.

나만의 예외를 만들어 보자.

Throwable 을 직적 상속 받는 클래스는 ExceptionError가 있다.

Error 와 관련된 클래스는 손대어서는 안된다.

하지만 Exception 을 처리하는 예외 클래스는 개발자가 임의로 추가해서 만들 수 있다. 단, Throwable 이나 그 자식 클래스의 상속을 받아야만 한다.

Throwable 클래스의 상속을 받아도 되지만, Exception을 처리하는 클래스라면 java.lang.Exception 클래스의 상속을 받는 것이 좋다.

  • MyException 클래스로 Exception을 만들어보자
    • 예외 클래스가 되지 위한 조건
public class MyException extends Exception{
    public MyException(){
       super();
    }

    public MyException(String name){
       super(message);
    }
}
  • CustomException
public class CustomExcepion {
    public static void main(String[] args) {
        CustomExcepion sample = new CustomExcepion();
        try{
            sample.throwMyException(13);
        }catch(MyException mye){
            mye.printStackTrace();
        }
    }

    public void throwMyException(int number) throws MyException{
        try {
            if (number > 12) {
                throw new MyException("Number is over than 12");
            }
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}

이와 같이 직접 만든 예외를 던지고, catch 블록에서 사용하면 된다.

MyException 을 던진다고 명시해 놓았지만, 이 메소드를 호출하는 메소드(main()메소드)에서는 반드시 MyException으로 catch할 필요는 없다.

MyException의 부모 클래스인 Exception 클래스로 catch해도 무방함.

만약 MyException을 선언할 때 관련된 클래스를 확장하지 않았을 때에는 이 부분에서 제대로 컴파일이 되지 않는다.

MyException이 아무런 상속을 받지 않고 있다면, CustomException 클래스를 컴파일할 때 에러를 출력한다.

public class MyException {//extends Exception {
    public MyException(){
         super();
    }

    public MyException(String message){
        //super(message);
    }
}

자바 예외 처리 전략

예외를 직접 만들 때 Exception 클래스를 확장하여 나만의 예외 클래스를 만들었다.

그런데 이 예외가 발생하지 않고, 실행시에 발생할 확률이 매우 높은 경우에는 런타임 예외로 만드는 것이 나을 수도 있다.

즉, 클래스 선언히 extends Exception 대신에 extends RuntimeException 으로 선언하는 것이다.

이렇게 된다면, 해당 예외를 던지는(throw 하는) 메소드를 사용하더라도 try-catch로 묶지 않아도 컴파일시에 예외가 발생하지 않는다.

하지만 이 경우에는 예외가 발생할 경우 해당 클래스를 호출하는 다른 클래스에서 예외를 처리핟록 구조적인 안전 장치가 되어 있어야만 한다.

여기서 안전 장치는 try-catch로 묶지 않은 메소드를 호출하는 메소드에서 예외를 처리하는 try-catch가 되어 있는 것을 뜻한다.

public void methodCaller(){
    try{
        methodCaller();
    }catch(Exception e){
     //예외 처리
    }
}

public void methodCallee(){
     // RuntimeException 예외 발생 가능성 있는 부분
}

이와 같이 unchecked exception인 RuntimeException이 발생하는 메소드가 있다면, 그 메소드를 호출하는 메소드는 try-catch로 묶어주지 않더라도 컴파일할 때 문제가 발생하지 않는다.

하지만, 예외가 발생할 확률은 높으므로, 위의 예에서 methodCaller() 처럼 try-catch로 묶어주는 것이 좋다.

catch 문장에서 처리를 해 줄 때 다음과 같이 처리하는 것은 피해야한다.

try{
   // 예외 발생 가능한 코드
} catch(SomeException e){
   // 여기 아무 코드 없음
}

여기서 SomeException이라는 것은 그냥 어떤 예외를 잡는다는 것을 의미한 것이지, SomeException이라는 것이 실제 존재한다는 것이 아니다.

정리

  • 임의의 예외 클래스를 만들 때에는,
    • 반드시 try-catch로 묶어줄 필요가 있을 경우메나 Exception 클래스를 확장한다. 일반적으로 실행시 예외를 처리할 수 있는 경우에는 RuntimeException 클래스를 확장하는 것을 권장한다.
  • catch문 내에 아무런 작업 없이 공백을 놔두면 예외 분석이 어려워지므로 꼭 로그 처리와 같은 예외 처리를 해줘야만 한다.