Study/java

[백기선님의 자바 라이브 스터디] 9주차 - 예외 처리

에디개발자 2021. 2. 24. 00:35
반응형

www.youtube.com/watch?v=HLnMuEZpDwU 

나를 닮았다고 한다...

목표

자바의 예외 처리에 대해 학습하세요.

학습할 것 (필수)

  • 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
  • 자바가 제공하는 예외 계층 구조
  • Exception과 Error의 차이는?
  • RuntimeException과 RE가 아닌 것의 차이는?
  • 커스텀한 예외 만드는 방법

 

추천도서

www.11st.co.kr/products/2874490917?utm_medium=%EA%B2%80%EC%83%89&gclid=Cj0KCQiA7NKBBhDBARIsAHbXCB5I37sBM_HgHnyywcYnfRFFqVixy9co8mCWT83X-aNHrulHvk99TTIaAkZqEALw_wcB&utm_source=%EA%B5%AC%EA%B8%80_PC_S_%EC%87%BC%ED%95%91&utm_campaign=%EA%B5%AC%EA%B8%80%EC%87%BC%ED%95%91PC+%EC%B6%94%EA%B0%80%EC%9E%91%EC%97%85&utm_term=

 

[11번가] 리팩토링 2판

카테고리: 0-3세, 가격 : 31,500

www.11st.co.kr

www.yes24.com/Product/Goods/17525598

 

GoF의 디자인 패턴

이 책은 디자인 패턴을 다룬 이론서로 디자인 패턴의 기초적이고 전반적인 내용을 학습할 수 있다.

www.yes24.com

 

Exception과 Error의 차이는?

차바에서는 문제가 발생했을 때 크게 예외(Exception)와 에러(Error)가 발생합니다. 

  Error Exception
타입 어플리케이션에서 확인되지 않는 오류 Checked와 Unchecked로 분류
패키지 java.lang.error java.lang.exception
복구 불가능 가능
발생 컴파일시에 발생하지 않음 런타임 & 컴파일 시 발생

Error

시스템의 비정상적인 상황이 생겼을 경우 발생을 합니다. ( JVM에서 문제가 생김 )

시스템 레벨에서 발생하는 심각한 수준의 오류로 개발자가 예측할 수 없는 오류입니다. Error에 대한 처리는 하지 않고,  Error가 발생하지 않도록 코딩합니다.

Exception

개발자의 실수로 로직에서 발생합니다. 

Exception은 개발자가 충분히 예측할 수 있고 Exception에 대한 처리도 할 수 있습니다. 

 

예외 클래스의 구조

Object
Throwable
Error Exception
  Checked Exception Unchecked Exception

예외 클래스 또한 클래스이므로 가장 부모 클래스는 Object 클래스입니다. 그리고 Throwable class를 상속받는 Error, Exception 클래스가 존재합니다. 

 

여기서 Checked Exception과 Unchecked Exception 클래스는 Exception 클래스를 상속받고 있습니다. 그럼 이것이 무엇이고 어떻게 나뉘어지는지 살펴보겠습니다.

  • RuntimeException과 RE가 아닌 것의 차이는?

RuntimeException과 RE가 아닌 것의 차이는?

RuntimeException을 상속받는 예외는 Unchecked Execption이라하고 상속받지 않는 예외는 Checked Exception이라고 합니다. 자세하게 알아보겠습니다.

 

Checked Exception

체크가 가능한 Exception을 말합니다. 이를 자바는 강제로 예외 처리를 하라고 알려줍니다.

public void test() throws ClassNotFoundException {  // throws
    Class clazz = Class.forName("dd");
}
public void test() {
    // try, catch
    try {
        Class clazz = Class.forName("dd");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

위 코드를 살펴보면 Class.forName() 사용하여 메서드를 찾습니다. 하지만 작성한 클래스를 찾지 못하는 경우 ClassNotFoundException을 발생시킵니다. 개발자는 해당 Exception을 throws 혹은 try, catch를 사용하여 처리할 수 있습니다.

 

Unchecked Exception

말 그대로 체크가 불가능한 Exception입니다. Checked Exception과는 반대로 자바에서 예외 처리를 강제화하지 않습니다.

public void test() {
    Integer carCount = null;

    System.out.println(carCount.toString());  // NPE 발생
}

위 코드를 살펴보면 Integer Wrapper 변수에 null을 할당하고 변수에 toString()을 사용하면 NullPointException이 발생됩니다. 하지만 코드에서는 예외처리가 존재하지 않고 컴파일할 때에도 예외는 발생하지 않습니다. 이것을 Unchecked Exception 이라고 합니다.

 

왜 Checked Exception과 Unchecked Exception으로 분류되어있는가?
사용하는 용도가 다릅니다. Checked Exception을 사용할 때는 예외가 발생했을 경우 처리를 할 수 있는 경우입니다.
Unchecked Exception은 에러가 발생하는 것을 알지만 처리할 수 없는 경우에 사용됩니다.

 

자바에서 예외 처리 방법 (try, catch, throw, throws, finally)

try, catch, finally

try 블록 내에 로직 중 Exception이 발생할 경우 Exception의 종류에 따라 catch블록이 실행됩니다. Exception이 발생하지 않은 경우 catch 블록은 생략하게 됩니다. finally는 Exception 발생 여부와 상관없이 필수로 실행되는 블록입니다.

 

하나의 예제 코드로 살펴보겠습니다.

try {
    System.out.println("try");
    Class clazz = Class.forName("dd");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
    System.out.println("catch");
} finally {
    System.out.println("finally");
}
// 정상 실행 경우
try
finally

// exception 발생 경우
try
catch
finally

 

다중 catch

catch문을 여러개를 써서 예외하는 처리방법도 있습니다. 

try {
    System.out.println("try");
    Class clazz = Class.forName("dd");  // ClassNotFoundException
    carCount.toString();                // NullPointException
} catch (ClassNotFoundException e) {
    e.printStackTrace();
    System.out.println("catch ClassNotFoundException");
} catch (NullPointerException e) {
    e.printStackTrace();
    System.out.println("catch NullPointException");
} finally {
    System.out.println("finally");
}

위 코드는 Exception이 발생하여 N개의 처리를 하는 코드입니다. 

위 코드를 실행하면 ClassNotFoundException이 발생하여 아래와 같은 결과가 나타납니다. 

try
catch ClassNotFindException
finally

catch 문 중 NullPointerException 블록 로직은 실행되지 않은 것을 확인할 수 있습니다.

 

try {
    System.out.println("try");
    // Class clazz = Class.forName("dd");  // ClassNotFoundException
    carCount.toString();                // NullPointException
} catch (ClassNotFoundException e) {
    e.printStackTrace();
    System.out.println("catch ClassNotFoundException");
} catch (NullPointerException e) {
    e.printStackTrace();
    System.out.println("catch NullPointException");
} finally {
    System.out.println("finally");
}

그렇다면 ClassNotFoundException이 발생하는 로직을 주석처리하고 실행하면 NullPointException catch 블록내 로직이 실행됩니다. 

 

다중 catch 주의사항

다중 catch를 사용할 때 중요한 건 순서입니다.

Exception 클래스들 또한 상위, 하위 개념이 존재합니다. 잘못 된 순서의 예외를 사용할 경우 Java Compiler가 오류라고 알려주거나 레거시 catch가 나올 수 있습니다. 

 

catch를 정의할 땐 자식 클래스를 먼저 선언합니다.

try {
    System.out.println("try");
    Class clazz = Class.forName("dd");
} catch (Exception e) {
    System.out.println("catch Exception");
} catch (ClassNotFoundException e) {    // compiler 오류발생
    e.printStackTrace();
    System.out.println("catch ClassNotFoundException");
} finally {
    System.out.println("finally");
}

 

try with resource

java7 부터 도입된 기능입니다.

closeable

기존의 try catch finally 에서 finally 블록의 코드를 줄이는 데 사용합니다.

 

java7 이전버전의 try catch finally 코드입니다.

FileInputStream file = null;
try {
    file = new FileInputStream("");
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (file != null) {
        try {
            file.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

java7 이후버전의 try with resource 코드입니다.

try (FileInputStream file = new FileInputStream("")) {

} catch (IOException e) {
            e.printStackTrace();
}

무조건 사용할 수 있는 것은 아닙니다. closeable 일 경우에만 사용이 가능합니다.

AutoCloseable 인터페이스를 구현한 객체

 

throw

코드에서 강제로 Exception을 발생시키고 싶을 때 throw를 사용합니다.

public void test2(String data) {
    if (data == null) {
        throw new NullPointerException();  // exception 발생
    }

    System.out.println(data);
}

 

throws

throw와 throws는 s 한글자 차이지만 하는 액션은 완전히 다릅니다.

throw는 메서드 블록 내에서 Exception을 던지고 throws는 메서드 명 우측에 선언하여 메서드를 호출한 지점으로 예외를 던지는 액션을 취합니다.

public void test3() throws Exception {
    test2("");    throws 메서드 호출
}

public void test2(String data) throws Exception { // ㅅthrows 사용
    System.out.println(data.indexOf(1));
}

throws 하여 예외를 던지면 메서드를 호출한 지점에서 try catch를 적용하거나 다시 throws 즉, 어떤 방식으로든 예외처리를 강제화시킵니다.

 

예외처리 비용

예외처리는 Throwable 생성자의 fillInStackTrace()는 예외가 발생했을 경우 예외가 발생한 메서드의 Stack Trace를 모두 찾아서 출력해주기 때문에 매우 비쌉니다.

예외처리를 남용하는 것은 좋지 않습니다.

저 같은 경우에는 Return Code를 리턴하여 Code에 따라 처리하도록 로직을 작성하였습니다.

 

커스텀한 예외 만드는 방법

예외에는 정말 여러가지 종류가 있습니다. 하지만 우리는 Application을 개발하면서 많은 Exception 중 정작 내가 필요한 Exception이 존재하지 않는 경우가 있습니다. 또한 내가 정의한 Exception만을 예외 처리를 묶을 경우 이러한 경우 커스텀한 예외를 만들어서 사용할 수 있습니다.

하지만 대부분 내가 원하는 Exception은 다 있습니다!
public class CustomException extends Exception {
    public CustomException(String message, Throwable cause) {
        super(message, cause);
    }
}
public void test4() throws CustomException {
    try {
        FileInputStream fi = new FileInputStream("dddd");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        throw new CustomException("Exception을 Custom했다", e);
    }
}

위와 같이 커스텀한 예외를 만들 수 있습니다. 

여기서 Best Practice는 Exception을 상속받고 근본적인 원인 유실을 막기위해 생성자에서 Throwable 변수가 있는 생성자를 사용하는 것이 좋습니다. 

 

intellij short cut tip!!
option + comman + t : try, catch로 감싸준다.
반응형