예외 처리는 어떻게 하는거지?

이정빈·2024년 4월 29일

TIL

목록 보기
5/8

오류 및 예외에 대한 이해

  • 의도하지 않은 입력값을 받았을 경우
  • 프로그램의 메모리가 감당 할 수 없는 값인 경우
  • 프로그램을 돌리기 위한 메모리가 부족한 경우
💡 위 처럼 다양한 오류가 발생 할 수 있고, 이러한 오류 즉, 준비할 수 있는 오류를 미리 구현하고 생각해두면 개발하는 단계에서 막힘없이 진행을 할 수 있습니다. Junior Dev일때는 Implementation을 중심으로 진행을 하기 때문에 이러한 예외에 대한 내용을 복기하고 생각을 하고 있어야 합니다!

오류란?

우선, 일반적으로 회복이 불가능한 문제입니다.

  • 이는 시스템 레벨에서, 또는 주로 환경적인 위와 같은 이유로 발생합니다.

이에 반해, Exception(예외)는 일반적으로 회복이 가능한 문제입니다.

  • 회복이 가능하다는 것은 예외 발생을 인지하고 대응할 수 있음을 의미합니다.

  • 예외의 종류

    1. 컴파일 에러

      • .java 파일을 .class 파일로 컴파일 하는 과정에서 발생하는 에러
      • 대부분 코드에서 오류가 있어 발생합니다.
    2. 런타임 에러(Runtime Error)
      - 우리가 주로 다루게 될 예외입니다.
      - 문법적인 오류가 아닌 컴파일은 잘되었지만 “프로그램”이 실행도중 맞딱드리게 되는 예외입니다.

      이렇게 크게 예외의 종류를 구분할 수 있고, 확인된 에러를 다시 한번 두가지로 분류를 할 수 있습니다.

    3. Checked Exception ✅

      • 컴파일 시점에 확인하는 예외입니다.
      • 반드시 예외 처리를 해줘야하는 경우입니다.
      • 🚨 컴파일 시점에서 확인하는 에러이기 때문에 Complie Error와 헷갈려서는 안됩니다. 우리가 이미 특정한 문제를 인지하고 있어, 해당 예외를 정의해두고 컴파일 하는 동안 해당 예외에 대한 처리를 했는지 안했는지 확인 할 수 있는 예외를 말합니다. → 알고있는 문제에 대해서 예외 처리를 이미 해놓은 경우
    4. Unchecked Exception ❌

      • 런타임 시점에 확인되는 예외입니다.
      • 예외 처리가 반드시 필요하지 않은 예외입니다.
    • 그러면 예외처리를 어떻게 할까?

    1. 우리가 예외를 어떻게 정의하고,

    2. 예외가 발생할 수 있음을 알리고,

    3. 사용자는 예외가 발생할 수 있음을 알고 핸들링하는지

      위의 부분을 주의 깊게 봐야 합니다.

      public class CustomException extends Exception{
      	public CustomException(String message){
      		super(message);
      	}
      }

      위 처럼 Exception을 커스텀하기 위해서는 Exception 클래스를 상속받아야하고, super() 키워드를 통해서 우리의 코드에서 입력되는 메세지에 대하서 throw 할 수 있게 해줘야 합니다.

      public class OurClass{
      	private final boolean just = true;
      	
      	// throws : method 이름 뒤에 붙어 이 method가 어떠한 예외사항을 던질 수 있는지 알려줍니다.
      	// throw : method 안에서 실제로 예외 객체를 던칠 때 사용합니다.
      	public void thisMethodIsDangerous()throws CustomException{
      		//logic<>
      		if(just){
      			throw new CustomException("Just is true.")
      		}
      	}
      }

      이렇게 위 코드 같이 구현할 수 있습니다. 여기서 throws 를 통해서 다양한 Exception을 받아 올 수 있습니다. 이러한 클래스를 main method 에서 사용하려고 할때 따로 flag 를 주지 않았기 때문에 실행이 되지 않습니다. 그렇기에 try~catch~finally 문을 사용하여 동작을 시킵니다.

      OurClass ourClass = new OurClass();
      try{
      	ourClass.thisMethodIsDangerous();
      }catch(CustomException e){
      	System.out.println(e.getMessage());
      }finally{
      	// 무조건 거쳐가는 단계
      	System.out.println("예외처리 완료 했습니다.");
      }

      예외 클래스 구조 이해하기

      ObjectThrowable Class

      시작은 모든 객체의 원형인 Object 의 상속을 받아 시작합니다. Throwable 클래스에는 자식으로서 에러와 예외 클래스가 존재합니다. 이러한 클래스 들은 내부에서 IOError 클래스, RuntimeException 클래스와 같이 구분되어 처리됩니다.

      Untitled

      Untitled


      Chained Excpetion 처리방법

      Chained Excpetion

    • 예외는 다른 예외를 유발 할 수 있습니다.
    • 원인 예외를 새로운 예외에 등록한 후 다시 새로운 예외를 발생시키는 데, 이를 예외 연결이라고 합니다.
    • 이렇게 연결하는 이유는 연속적으로 발생할 수 있는 예외를 묶어 한번에 처리하기 위함입니다.
    • 원인 예외를 다루기 위한 메서드
      • initCause() : 지정한 예외를 원인 예외로 등록하는 경우
      • getCause() : 원인 예외를 반환하는 메서드

Generic을 배우고 활용해보자!

Generic의 장점

  • 타입 언어에서 “중복되거나 필요없는 코드를 줄여주는 것”입니다.

    • 중복해서 다른 타입의 매개변수로 객체를 생성할 때, 각각을 Over-loading 해줘야 하는 불편함을 방지할 수 있습니다.
  • 타입 안정성을 해치지 않습니다!

    • 다양한 연산자를 문제없이 사용할 수 있습니다.
  • 예시

// 1. 제네릭은 클래스 또는 메서드에 사용 가능합니다. 
// <>안에 사용할 타입을 넣어주면 됩니다.
// 그래서 T를 타입변수라고 명명합니다.
// Generic class 를 원시 타입이라고 합니다.
public class Generic<T> {
    // 2. 내부 필드에 대입됩니다.
    private T t;

    // 3. 리턴 타입도 입력 타입으로 바뀝니다.
    public T get() {
        return this.t;
    }

    public void set(T t) {
        this.t = t;
    }

    public static void main(String[] args) {
                                // 4.생성 및 사용
        Generic<String> stringGeneric = new Generic<>();
                                // 5. 출력
        stringGeneric.set("Hello World");
                                
        String tValueTurnOutWithString = stringGeneric.get();

        System.out.println(tValueTurnOutWithString);
    }
}

Generic의 문법을 알아보자

  • 객체의 static 멤버에 사용할 수 없습니다.
    • 왜냐하면, 타입변수는 인슽턴스 변수로 간주되고 모든 객체에 동일하게 동작해야하는 static 필드 특성상 사용이 불가합니다.
  • 제네릭 배열 생성 불가
  • 문법
    • 다수의 타입을 가져올 수 있습니다.

      public class Generic<T, U, E> {
          public E multiTypeMethod(T t, U u) { ... }
      }
      
      Generic<Long, Integer, String> instance = new Generic();
      instance.multiTypeMethod(longVal, intVal);
    • 다형성 즉 상속과 타입의 관계는 그대로 적용됩니다.

      • 대표적으로 부모 클래스로 제네릭 타입 변수를 지정하고, 그 안에 자식 클래스를 넘기는 것은 잘 동작합니다.
    • 와일드카드를 통해 제네릭의 제한을 구체적으로 정할 수 있습니다.

      public class ParkingLot<T extends Car> { ... }
      
      ParkingLot<BMW> bmwParkingLot = new ParkingLot();
      ParkingLot<Iphone> iphoneParkingLot = new ParkingLog(); // error!
    1. : T 와 그 자손들만 사용 가능합니다.
    2. : T와 그 조상들만 가능합니다.
    3. : 제한없음
profile
백엔드 화이팅 :)

0개의 댓글