(Java programming) 예외처리 / 예외 위임하기

soosoorim·2024년 2월 19일
0

예외처리

Java에서 가장 많이 발생하는 예외 목록

  1. NullPointerException
    Null Instance에 접근하려 함
    Instance가 Null인지 먼저 확인 함
  2. ArrayIndexOutOfBoundsException
    List의 인덱스 범위를 벗어남
    찾으려는 인덱스가 존재하는지 먼저 확인 함
  3. IndexOutOfBoundsException
    배열의 인덱스 범위를 벗어남
    찾으려는 인덱스가 존재하는지 먼저 확인 함
  4. NumberFormatException
    숫자가 아닌 문자를 숫자로 변환하려 함
    문자가 숫자형태인지 확인 함

if를 활용한 예외처리

예외를 처리하는 가장 좋은 방법은 예외를 발생시키지 않는 것.
위 예외 목록은 if를 활용해 예외를 방지할 수 있다.

이렇게 먼저 확인하는 코드를 “Validation Logic” 이라고 하며 “입력값 검증“ 이라고 표현하기도 함.

NullPointerException이 발생하는 원인

public static void main(String[] args) {
    String name = null;
    // NullPointerException - null.equals("이름")
    System.out.println(name.equals("이름"));
}

Null 은 Reference Type의 기본 값으로 아무것도 할당되지 않은 상태를 말한다.
할당된 것이 없으니 메모리에 아무것도 없는 상태이며, 존재하지 않는 메모리를 참조하려 한다는 에러가 발생한다.

  • NullPointerException 방지 방법
public static void main(String[] args) {
    String name = null;
    if (name != null) {
        System.out.println(name.equals("이름"));
    }
}

인스턴스를 참조하기 전, null 인지 확인한다.
Name 변수의 값은 null 이므로 System.out.println(name.equals("이름")); 이 코드는 실행되지 않는다.

try – catch – finally 예외처리
if 로 예외 처리할 수 없거나 애매할 경우 try ~ catch ~ finally를 사용

  • 기본형
try {
    // ▼ NumberFormatException 발생
    int number = Integer.parseInt("ABC"); 
    System.out.println(number); // 이 코드는 실행되지 않습니다.
}
// ▼ NumberFormatException이 발생했을 때 catch가 실행됩니다.
catch ( NumberFormatException e ) {
    // try에서 예외가 발생하면 catch가 실행
    System.out.println("에러가 발생했습니다. " + e.getMessage());
    e.printStackTrace(); // 에러내용 모두 출력
}
finally {
    // 예외 발생 여부와 관계없이 무조건 실행
    System.out.println("처리가 완료됐습니다.");
}

(finally까지 쓰는 경우는 드물다.)

아래는 클래스를 동적 로딩하는 코드다.
SomeAClass 라는 클래스를 불러오게 되는데 SomeAClass 라는 클래스는 없으니 에러(ClassNotFoundException)가 발생하게 된다.

Class.forName("SomeAClass");

forName을 Ctrl을 누른채로 클릭해보면

public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName(className, caller);
}

ClassNotFoundException을 throws 하고 있다. ClassNotFoundException을 Ctrl + 클릭해보면 ReflectiveOperationException을 상속받은 것을 알 수 있다.

public class ClassNotFoundException
         extends ReflectiveOperationException {

다시 ReflectiveOperationException을 Ctrl + 클릭해보면

public class ReflectiveOperationException extends Exception {

결국 ReflectiveOperationException은 Exception을 상속받은 예외클래스라는 것을 알 수 있다.


이 상속관계를 is a 관계로 정리해보면,

  • ClassNotFoundException is a ReflectiveOperationException
  • ReflectiveOperationException is a Exception
  • ClassNotFoundException is a Exception

ClassNotFoundException은 Exception을 상속받은 예외 클래스이기 때문에 try ~ catch가 반드시 필요

  • 코드 실습은 github - TryCatchExam 보기

이 코드 실행해보면,

public static void main(String[] args) {
    Class.forName("SomeAClass");
}

Unhandled exception type ClassNotFoundException
이런 에러 발생하는데, ClassNotFoundException 타입의 예외를 처리하지 않았다라는 에러이다.

이 에러를 처리하기 위해 try ~ catch를 사용해보면

public static void main(String[] args) {
    try {
        Class.forName("SomeAClass");
    }
    catch(ClassNotFoundException cnfe) {
        cnfe.printStackTrace();
    }
}

cnfe.printStackTrace(); 코드에 의해 아래처럼 메시지가 나타나게 된다.

java.lang.ClassNotFoundException: SomeAClass
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:391)
        at java.base/java.lang.Class.forName(Class.java:382)
**        at App.main(App.java:4)**

Exception이 발생한 최초의 위치와 호출스택들이 한번에 노출되게 되며 가상 상위에 Exception이 발생한 원인에 대해 설명해준다.

  • 여러 Exception에 대한 처리를 똑같이 해주고 있다면 Exception을 하나로 묶어 처리하는 것이 유용하다.
public static void main(String[] args) {
    String type = null;
    String number = "abc";
    try {
        if (type.equals("number")) {
            int num = Integer.parseInt(number);
            System.out.println(num);
        }
    }
    catch (NullPointerException | NumberFormatException e) {
        e.printStackTrace();
    }
}



예외 위임하기

  • RuntimeException이 아닌 Exception은 try ~ catch 처리가 반드시 필요하다.
  • ClassNotFoundException 같은 Exception 타입의 예외가 발생하면 즉시 try ~ catch를 해주어야만 하고 그렇지 않을 경우 컴파일 에러가 발생한다.

  • 코드 실습은 github - TryCatchExam 보기



사용자 예외 발생시키기

RuntimeException의 기본 생성자

public RuntimeException() {
    super();
}
  • 예외를 발생시킬 때, 아무런 값도 전달하지 않는다.
  • 이 생성자를 사용할 경우, 예외를 받았을 때 아무런 정보를 얻을 수 없다.
  • 잘 사용하지 않는다.

RuntimeException의 생성자들

public RuntimeException(String message) {
    super(message);
}
  • 예외를 발생시킬 때, 원인이 된 메시지를 파라미터로 전달한다.
  • 가장 많이 사용한다.
public RuntimeException(String message, Throwable cause) {
    super(message, cause);
}
  • Throwable은 모든 예외들의 Root Class 이다.
  • RuntimeException is a Throwable Exception is a Throwable 즉, 모든 예외타입은 is a Throwable 타입
  • 보통 try ~ catch 에서 처리한 Exception을 RuntimeException으로 변경하여 다시 발생시킬 때 주로 사용한다.
  • Message는 예외를 발생시킨 원인이 된 메시지를 전달하며
  • Cause는 catch에서 처리한 예외를 전달한다. 즉, RuntimeException이 발생된 원인이 된다.
  • public RuntimeException(String message) { } 와 함께 많이 사용된다.
public class InvalidUserIdException extends RuntimeException {
    
    public InvalidUserIdException(String message) {
        super(message);
    }
    public InvalidUserIdException(String message, Throwable cause) {
        super(message, cause);
    }
}
  • 사용자 예외는 거창할 필요없이 필요한 생성자만 호출 해주면 된다.

  • InvalidUserIdException을 발생시켜 본다.

  • id가 root, admin, system 인 경우 위 예외를 발생시키는 코드이다.

public static void main(String[] args) {
    
    Scanner input = new Scanner(System.in);
    System.out.println("ID를 입력하세요.");
    String userId = input.nextLine();
    if (userId.equals("root") || userId.equals("admin") 
        || userId.equals("system")) {
        InvalidUserIdException iuie = 
               new InvalidUserIdException(userId + "는 사용할 수 없습니다.");
        throw iuie;
    }
}
  • throw Exception객체 코드를 통해서 예외를 발생시키게 된다.

  • 보통, 예외를 발생시킬 경우는 아래 코드처럼 한 줄로 사용한다.

public static void main(String[] args) {
    
    Scanner input = new Scanner(System.in);
    System.out.println("ID를 입력하세요.");
    String userId = input.nextLine();
    if (userId.equals("root") || userId.equals("admin") 
        || userId.equals("system")) {
        throw new InvalidUserIdException(userId + "는 사용할 수 없습니다.");
    }
}

0개의 댓글

관련 채용 정보