예외 처리

hyHA·2023년 11월 12일
0
post-thumbnail
post-custom-banner

자바에서는 컴퓨터 하드웨어의 오동작 또는 고장으로 인한 프로그램 실행 오류를 에러라고 한다
그 외 사용자의 잘못된 조작 혹은 잘못된 코딩으로 프로그램 자체에 발생하는 오류는 예외라고 부른다
예외의 종류발생 원인, 해결방법을 알아보자

예외 클래스

자바는 모든 것이 객체로 이루어지기 때문에 예외 또한 객체로 구성되어있다.
모든 예외 클래스는 java.lang.Exception 클래스를 상속받는다.
자바의 모든 클래스가 Object를 상속받는 것과 같은 방식이다.

JVM은 프로그램을 실행하는 도중 예외가 발생하면, 해당 예외 클래스로 객체를 생성한다. 그리고 예외 처리 코드에서 예외 객체를 이용할 수 있도록 해준다.

Runtime Exception의 하위 클래스가 아니면 일반 예외 클래스이고, 하위 클래스이면 실행 예외 클래스이다.

예외가 발생하면 프로그램은 곧바로 종료된다는 점에서 에러외 비슷하다.
하지만 예외 처리를 통해 프로그램을 종료하지 않고 정상 실행 상태가 유지되도록 할 수 있다
자바는 일반 예외의 경우 컴파일할 때 예외 처리 유무를 확인 후, 예외 처리 코드가 없다면 컴파일 오류를 발생시켜 사전에 오류가 나지 않도록 예방한다.

UncheckedException이 있는 이유. 모든 예외가 CheckedException이라면?

  • CheckedException의 경우는 예외처리 즉, try-catch를 의무로 작업하게됩니다. 코드적인 복잡도가 올라게 될것이다.
  • 논리적 오류 + 예기치 않은 상황
    • UncheckedException는 개발자가 개발한 논리에 의해서 발생하는 예외상황이기때문에 구분되어 처리가 되어야 한다.

Exception(일반 예외/checked 예외)

  • 컴파일러가 예외 처리를 강제하는 예외. 반드시 예외처리(try-catch)를 해야 함
  • 일반 예외는 프로그램 실행 시 예외가 발생할 가능성이 높아, 자바 소스를 컴파일하는 과정에서 해당 예외 처리 코드가 있는지 검사한다.
  • 예측 가능한 상황에 대한 에러.
  • 즉, 컴파일러가 컴파일 에러를 발생시켜 강제적으로 개발자가 예외 처리 코드를 작성하도록 요구함
  • 외부 리소스와 관련된 작업에서 발생하며, 개발자가 예외처리를 통해 오류 상황에 대처할 수 있도록 설계되어있다.
  • 예시
    • IOException(파일 입출력)
    • SQLException
    • BrokenBarrierException

Runtime Exception(실행 예외)

  • 주로 프로그래머의 실수나 논리적인 오류로 인해 발생.
  • 개발자가 개발한 논리에 의해서 발생하는 예외.
  • 명시적인 처리를 안해도 됨
  • 어디서 어떤 예외가 나오는지 한눈에 보이지 않는다.
  • 실행 예외는 컴파일러 넌 체크 예외라고도 하는데, 실행 시 예측할 수 없이 갑자기 발생하기 때문에 컴파일하는 과정에서 예외 처리 코드가 있는지 검사하지 않는다.
  • 즉, 실행 예외는 컴파일러가 체크하지 않기 때문에 오로지 개발자의 경험에 의해서 예외 처리 코드를 작성해야 한다. 만약 개발자가 예외 처리 코드를 넣지 않았을 경우, 해당 예외 발생 시 프로그램은 바로 종료된다.

NullPointerException

해당 객체가 null인 상태에서의 접근을 했을때 발생하는 에러로 가장 빈번하게 발생한다.
객체 참조가 없는 상태, 즉 null 값을 가지고 있는 참조 변수로 객체 접근 연산자인 도트(.)를 사용했을때 발생한다.

public class NullPointerExceptionExample {
	public static void main(String[] args) {
    	String data = null;
        System.out.println(data.toString());
    }
 }

ArrayIndexOutOfBoundsException

배열에서 할당된 배열의 인덱스 범위를 초과해서 사용할 경우 발생하는 에러.

예를 들어 길이가 3인 int[] arr = new int[3] 배열을 선언했다고 가정하면,
해당 배열에서 사용할 수 있는 인덱스의 범위로는 위의 arr[0], arr[1], arr[2]만을 사용할 수 있는데, arr[3]이나 arr[4]등 해당 인덱스의 범위에 벗어나는 경우에는 ArrayIndexOutOfBoundsException가 발생한다.

NumberFormatException

프로그램을 개발하다가 보면 문자열로 되어있는 데이터를 숫자로 변경하는 경우가 매우 자주 발생한다.

만약 parseXXX() 메소드들을 이용하여 문자열을 숫자로 변환할 수 있지만 매개변수로 오는 문자열이 숫자로 변환이 되는 값이면 숫자를 리턴하지만, 숫자로 변환될 수 없는 문자가 온다면 java.lang.NumberFormatException을 발생시킨다.

public class NumberFormatExceptionExample {
	public static void main(String[] args) {
    	String corretData = "100";  // 문자열 100은 숫자로 변환 할 수 있는 값이다.
    	String wrongData = "a100";  // 문자열 A100은 숫자로 변환 할 수 없는 값이다.
    	
    	int value1 = Integer.parseInt(corretData);
    	int value2 = Integer.parseInt(wrongData); //NumberFormatException 발생
    	
    	System.out.println(value1);
    	System.out.println(value2);
    }
 }

ClassCaseException

타입 변환(Casting)은 상위 클래스와 하위 클래스간에 발생하고 구현 클래스와 인터페이스 간에도 발생한다.

클래스 타입 변환이 가능한 경우는 아래와 같다.

  1. 상속

    • 자동 타입 변환
      • 자식 타입은 부모 타입으로 자동 타입 변환이 가능
        Animal animal = new Cat();
    • 강제 타입 변환
      • 부모 타입을 자식 타입으로 변환할 수 있음
      • 자식 타입이 부모 타입으로 자동 타입 변환 후 다시 자식 타입으로 변환 시 사용함
      • 부모 타입으로 생성된 객체는 자식 타입으로 변환할 수 없음.
        Parent parent = new Child(); //자동 타입 변환
        Child child = (Child)parent; //강제 타입 변환
  2. 인터페이스

    • 자동 타입 변환
      • 구현 객체가 인터페이스 타입으로 변환되는 것
        Vehicle vehicle = new Bus();
    • 강제 타입 변환
      • 인터페이스 타입을 구현 클래스 타입으로 변환하는 것
        Vehicle vehicle = new Bus();//자동 타입 변환
        Bus bus = (Bus) vehicle;//강제 타입 변환

이런 경우가 아니라, 억지로 타입변환을 시도할 경우 ClassCastException이 발생한다.

public class ClassCastExceptionExample {
	static class Animal {}
	static class Dog extends Animal{}
	static class Cat extends Animal{}

	
	public static void main(String[] args) {
    	
    	// Cat 객체 생성
    	Cat cat = new Cat();
    	changeDog(cat); 
    }
	public static void changeDog(Animal animal) {
		Dog dog = (Dog) animal; // ClassCastException 발생 가능
	}
}

ClassCastException을 발생시키지 않으려면 instanceof 연산자로 확인하는 것이 좋다

Animal animal = new Dog();
if(animal instanceof Dog){
	Dog dog = (Dog) animal;
} else if(animal instanceof Cat){
	Cat cat = (Cat) animal;
}

예외 처리

프로그램에서 예외가 발생했을 경우 프로그램이 종료되지 않고, 정상 실행을 유지할 수 있도록 예외 처리를 한다.
예외 처리란 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것을 말한다

예외 처리 코드 - try-catch-블록

try-catch-finally 블록은 생성자 내부와 메소드 내부에서 작성되어 예외처리를 할 수 있도록 해줌.

예외 종류에 따른 처리 코드 - 다중 catch

  • 다중 catch 블록
    • try 블로 내부에서는 다양한 예외가 발생할 수 있음
      • 예외 별로 예외 처리 코드를 작성하기 위해 다중 catch 블록을 사용함
    • catch 블록이 여러 개라 할지라도 단 하나의 catch 블록만 실행됨.
    • try 블록에서 동시다발적으로 예외가 발생하지 않고 하나의 예외가 발생하면 즉시 실행을 멈추고 해당 catch 블록으로 이동하기 때문

try 블록에서 예외 발생 시 예외를 처리해줄 catch블록은 위에서부터 차례로 검색됨.

예외 떠넘기기 - throws 키워드

예외를 메소드를 호출한 곳으로 떠넘기는 역할
그러면 메소드를 호출하는 곳(method1)에서 try-catch 블록을 사용해서 예외처리를 함

  • 예외 떠넘기기 선언방법
    • 메소드 선언부 끝에 throws 예외종류를 작성한다
리턴타입 method1(매개변수...) throws Exception1, Exception2,...{
}
  • 사용방법

    1. 메소드를 선언할 때 예외를 throws 처리한다
      public void method2() throws ClassNotFoundExeption
    2. 메소드를 사용한 곳에서 try-catch를 작성해준다
    3. main()에서도 throws 키워드를 사용하여 예외를 떠넘길 수 있는데, 결국 JVM이 최종 처리하게 됨.
    • JVM은 예외 내용을 콘솔에 출력하는 것으로 예외처리를 한다.
    • 별로 좋지 못한 예외 처리 방법.
  • 예시 : 자바 API 도큐먼트

    • forName 메소드를 사용하려면 try-catch블록으로 예외처리를 해야 한다.
forName : 
public static Class<?> forName(String className) throws ClassNotFoundException
---

//forName 메소드 사용 : try-catch블록을작성한다
public class ThrowsExample{
	try{
    	findClass();
    } catch (ClassNotFoundException e) {
    	System.out.println("클래스가 존재하지 않습니다");
    }
}

//forName 메소드 선언 : ClassNotFoundException 예외를 throws 처리한다
public static void findClass() throws ClassNotFoundException {
	Class clazz = Class.forName("java.lang.String2");
}

사용자 정의 예외와 예외 발생

  • 사용자 정의 예외
    • 자바 표준 API에서 제공하지 않는 예외
    • 애플리케이션 서비스와 관련된 예외
    • ex. 잔고 부족 예외, 계좌 이체 실패 예외, 회원 가입 실패 예외..
  • 선언방법
 public class XXXException extends [Exception | RuntimeException] {
 
	public XXXException(){}

 	public XXXException(String message){super(message);}
}
  • 사용방법
public void method() throws XXXException {
	throw new XXXException("메시지");
}

throws / throw

  • throw

    • 예외를 발생시키기 위해 사용
    • 메소드 내부에서 사용되며, 실제로 예외를 발생시키는 데 사용된다.
    • 생성된 예외 객체를 던져서 예외 처리 흐름을 시작한다.
    • 예: throw new FileNotFoundException("File not found.");
  • throws

    • 예외를 떠넘기기 위해 사용
    • 메소드 선언부에 사용되며, 해당 메소드가 발생시킬 수 있는 예외를 명시한다.
    • 메소드를 호출하는 쪽에서 이 메소드가 발생시킬 수 있는 예외에 대한 처리> 를 대비하도록 한다
    • 예: public void readFile() throws IOException { ... }

연결된 예외 (Chained Exception)

  • 자바에서 예외 처리를 할 때, 하나의 예외가 다른 예외를 유발하거나 함께 처리해야 하는 상황을 다루기 위한 개념
  • 발생한 예외를 다른 예외와 연결하여, 하나의 예외 처리 블록에서 여러 예외를 처리하는 것
  • 연결된 예외를 통해 원래 발생한 예외의 원인을 추적하고, 예외 처리를 더 명확하고 효과적으로 할 수 있다
  • 연결된 예외(chained exception)를 사용하는 또 다른 이유는 checked예외를 unchecked예외로 바꿀 수 있도록 하기 위함이다.
  • 연결된 예외 사용 방법
    • Throwable 클래스의 initCause(Throwable cause) 메소드를 사용하여 예외 객체에 원인 예외를 설정한다.
    • Throwable 클래스의 getCause() 메소드를 사용하여 원인 예외를 가져온다.
try {
    // 예외를 발생시키는 코드
} catch (IOException e) {
    // IOException을 처리하면서 다른 예외가 발생하는 상황을 가정한다.
    RuntimeException runtimeException = new RuntimeException
    						("An error occurred while handling IOException");
    runtimeException.initCause(e);
    throw runtimeException;
}

예외 정보 얻기 - getMessage() | printStackTrace()

참고
혼자공부하는자바, 이것이 자바다
https://rebeccacho.gitbooks.io/java-study-group/content/chapter8.html
https://imasoftwareengineer.tistory.com/86
https://slidesplayer.org/slide/15489240/

profile
룰루랄라
post-custom-banner

0개의 댓글