예외처리 - accidentlywoo/java GitHub Wiki

예외처리


예외(Exception)의 개념

자바에서 예외가 발생하는 경우는..

  • 정수를 0으로 나누는 경우
  • 배열의 인덱스가 음수 값을 가지는 경우
  • 배열의 인덱스가 배열의 길이를 넘어서는 경우
  • 부적절한 형변환이 발생하는 경우
  • 입출력 시 인터럽트(Interrupt)가 발생하는 경우
  • 입출력을 위해 필요한 파일이 존재하지 않는 경우

자바는 객체지향 언어이므로 예외도 객체로 처리함 -> 예외를 객체로 처리하기 위해 객체를 생성할 클래스가 필요함 발생 가능성이 있는 모든 예외 상황을 미리 예측하여 다양한 예외 클래스를 제공함

JVM이 던진(throw)예외 객체를 받아서(catch) 예외 처리 로직을 수행하고, 프로그램을 계속 진행함.

프로그램에 예외 처리 로직을 구현하지 않은 경우에는?

기본 예외 처리 핸들러 -> 에러 메시지를 화면에 출력하고, 실행을 중지시키는 역할

자바는 예외 객체를 생성하기 위한 다양한 예외 클래스가 존재함 모든 예외는 java.lang.Throwable 클래스의 자식 클래스인 java.lang.Exception 클래스의 자식 클래스로 만들어짐

  1. java.lang.Object -> 2. java.lang.Throwable //예외 최상위 클래스 -> 3. java.lang.Error //처리할 수 없는 치명적인 에러 처리

-> 3. java.lang.Exception // 정상적인 프로그램 동작 시 만날 수 있는 오류 처리 -> 4. java.lang.ClassNotFoundException -> 4. java.lang.CloneNotSupportedException -> 4. java.lang.InstantiationException -> 4. java.io.IOException RuntimeException의 자식 클래스가 아닌 예외 클래스는 예외 처리 로직이 없으면 컴파일이 되지 않음.

-> 4. java.lang.RuntimeException RuntimeException은 실행 시에 발생하는 예외이므로 프로그럄이 예외를 다루는 소스코드를 포함하지 않아도 컴파일러가 컴파일을 해줌 RuntimeException의 자식 클래스. 예외 처리를 포함하지 않아도 컴파일이 되는 예외 클래스들 -> 5. java.lang.IllegalArgumentException -> 5. java.lang.ArithmeticException -> 5. java.lang.ArrayIndexOutOfBoundsException

RuntimeException의 자식 클래스들만 이해하면 나머지 예외 클래스들은 프로그램 실행 전에 미리 예외 처리 로직을 추가할 수 있게 됨.

  • ArithmeticException : 정수를 0으로 나누는 경우 산술 연산 오류
  • IndexOutOfBoundsException : 배열의 인덱스가 배열의 길이를 넘어서면 발생하는 오류
  • IllegalArgumentException : 메서드의 매개변수 유형을 잘못 사용하면 발생하는 오류
  • IOException : 입출력 시에 지정한 파일이 시스템에 존재하지 않으면 발생하는 오류

예외 처리

예외 처리 기능은 자바 언어를 더욱 강하게(robust) 만드는 요인임

자바의 예외 처리 방법

  1. 예외가 발생한 메서드 내에서 직접 처리하는 방식
  • try-catch 구문
  • 다중 catch 블록
  • 상속을 이용한 예외 처리
  • finally블록
  1. 해당 메서드를 호출한 곳으로 예외 처리를 넘기는 방식
  • throws 예약어

try-catch 구문

예외가 발생한 메서드 내에서 직접 처리하는 방법 중의 하나임

try{
  예외 방생 가능이 있는 소스코드;
}catch(예외 타입 매개변수명){
  예외타입의 예외가 발생할 경우 수행될 소스코드;
}
  • try 블록 프로그래머가 프로그램 중에서 예외가 발생할 수 있는 소스코드들을 try 블록 안에 작성함 try 블록은 최소한 하나의 catch 블록이 있어야 함

  • catch 블록 try 블록 다음에 위치함 catch 블록의 매개변수는 예외 객체가 발생했을 때 참조하는 변수명으로, 반드시 java.lang.Throwable클래스의 하위 클래스 타입으로 선언되어야 함.

지정한 타입의 예외 객체 발생 -> try 블록의 나머지 문장들은 수행되지 않음. -> JVM은 발생한 예외 객체를 발생시킴 -> 발생한 예외 객체 타입에 해당하는 catch 블록을 수행함.

다중 catch 블록

프로그램에서 발생하는 예외에 따라서 적절하게 예외 처리를 분기시켜야 함 다중 catch 블록을 사용하여 여러 개의 예외를 처리함

try {
  예외 발생 가능이 있는 소스코드;
} catch(예외 타입1 매개변수명){
  예외타입1의 예외가 발생할 경우 수행될 소스코드;
}catch(1예외 타입n 매개변수 명){
  예외타입n의 예외가 발생할 경우 수행될 소스코드;
}

다중 catch 블록

다중 catch 블록을 이용하면 try 블록 내에서 발생되는 모든 예외에 대해서 적절한 예외 처리 로직이 수행되도록 함

나누기 연산에서 사용자가 "9 a"라는 값을 넘기게 되면? -> NumberFormatException 발생

프로그램에서는 catch 블록을 추가하여 예외 처리를 해주어야 함.

상속을 이용한 예외 처리

자바에서 지원하는 모든 예외 클래스는 Exception 클래스의 자식 클래스로 존재함

Exception 객체 -> 형변환에 의해 Exception 타입의 변수로 할당

Exception ex = new ArithmeticException();
ex = new ArrayIndexOutOfBoundsException();
ex = new NumberFormatException();
ex = new IOException();
public class ExceptionHandlerTest {
	public static void main(String[] args) {
		System.out.println("Program start...");
		try {
			int num1 = Integer.parseInt(args[0]);
			int num2 = Integer.parseInt(args[1]);
			System.out.println(num1 / num2);
		}
	
		catch (Exception e) {
			System.out.println("문제 발생!");
			//한번에 처리할 수 있지만 구체적으로 알 슈 없음
		}
		System.out.println("Program end...");
	}
}

--문제점--> 구체적으로 어떤 예외가 발생했는지를 알 수 없음 발생된 예외 객체에 따라서 예외 처리 로직를 분기 처리할 수 없음

다중 catch문에서 Exception을 이용한 올바른 예외 처리

public class ExceptionHandlerTest {
	public static void main(String[] args) {
		System.out.println("Program start...");
		try {
			int num1 = Integer.parseInt(args[0]);
			int num2 = Integer.parseInt(args[1]);
			System.out.println(num1 / num2);
		}
		/*
		 * catch (Exception e) {
			System.out.println("문제 발생!");
			//한번에 처리할 수 있지만 구체적으로 알 슈 없음
		}
		*/
		catch (ArithmeticException e) {
			System.out.println("정수를 0으로 나눌 수는 없습니다.");
		}catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("명령행 매개변수 2 개가 필요합니다.");
		}catch (NumberFormatException e) {
			System.out.println("매개변수는 정수만 허용됩니다.");
		}catch (Exception e) {
			System.out.println("프로그램 수행 중 문제 발생!");
		}
		System.out.println("Program end...");
	}
}

-> Exception 타입의 catch 블록은 다중 catch구문에서 맨 마지막에 위치해야 함.

try-catch 블록

try 블록 내의 문장에서 예외가 발생하지 않을 경우 -> try 블록은 수행되지만, catch 블록은 수행되지 않음

try 블록 내의 문장에서 예외가 발생할 경우 -> try 블록은 수행되지 않지만, 해당 예외 객체의 타입과 같은 매개변수를 지정한 catch 블록을 수행함.

finally 블록

finally 블록은 try-catch 블록 지정 시 try 블록과 함께 사용될 수 있음 -> 필수적으로 사용하는 블록은 아니므로 프로그래머의 편의에 따라 사용함 finally 블록이 사용되면 finally 블록의 내용은 예외 발생 유무나 예외 catch 유무와 상관 없이 무조건 수행됨 데이터베이스 연동 로직, 파일 입출력 로직을 수행한 후에 반드시 수행되어야 할 로직이 있는 경우에 사용함

try {
  예외 발생 가능이 있는 코드;
} catch(예외 타입 매개변수명){
  예외타입의 예외가 발생할 경우 수행될 코드;
} finally {
  예외 발생 여부와 무관하게 무조건 수행될 코드;
}

throws 예약어

자바의 예외 처리는 예외가 발생한 지점에서 try-catch또는 try-catch-finally 블록을 이용하여 예외 처리 로직을 작성함

예외가 발생한 메서드를 호출한 지점으로 예외를 전달하여 처리하는 방법 -> throws 예약어

[modifiers] returnType 메서드명([argType argName, ...]) [throws exceptionName1, exceptionName2, ...]

여러 개의 예외 클래스 이름을 나열할 수 있음

throws를 사용하여 예외처리를 다른 메서드로 전달할 때, 발생된 예외 객체가 RuntimeException의 자식 클래스인 경우 -> 명시적으로 throws 문을 사용하지 않아도 자동으로 throws 됨.

예외 발생

  1. 해당 메서드 안에서 처리
  2. throws 예약어를 이용하여 그 메서드가 호출된 곳에서 처리

왜 이런 작업이 필요할까? 클라 --객체의 메서드 호출--> 서버1 --객체의 메서드 호출 --> 서버2 예외 발생 -> 예외를 자신이 직접 처리하면 안됨 서버2 -- 예외 전달--> 서버1 --예외 메시지 출력-->클라

사용자 예외 클래스의 정의

API에서 제공하지 않음

  • 개발자가 직접 예외 클래스를 정의해서 사용

모든 예외 클래스의 최상위 클래스인 java.lang.Exception 클래스를 상속받아 정의함.

class 예외 클래스명 extends Exception
class Account {
	String name;
	int currentMoney;
	
	public Account(String name, int currentMoney) {
		this.name = name;
		this.currentMoney = currentMoney;
	}
	public void withdraw(int money) {
		currentMoney = currentMoney - money;
	}
	
	public String toString() {
		return "Account [name="+name+", currentMoney ="+currentMoney+"]";
	}
}

public class CustomExceptionTest {
	public static void main(String[] args) {
		Account kimAccount = new Account("woo", 100);
		kimAccount.withdraw(150);
		System.out.println(kimAccount.toString());
	}
}

산술적으로 100만원 - 150만원 = -50만원은 당연한 결과임 현재 예금액 보다 많은 금액을 인출하는 경우 -> 예외로 처리하여 인출 작업이 처리되지 못하게 해야함 사용자에게 적절한 메시지를 출력하여 인출이 실패한 이유를 알려줘야 함.

public class BadBankingException extends Exception{
    public BadBankingException(String s){
        super(s);
    }
}

JVM은 프로그램 수행중에 예외가 발생하면, 자동으로 해당하는 예외 객체를 발생시킨 후 catch 블록을 수행함

예외 - 프로그램에서 직접 생성(사용자가 강제적으로 발생)

사용자 정의 예외 클래스 형태의 예외 객체는 자바 가상 머신에 의해 자동으로 발생되지 않음 -> 사용자 정의 예외 클래스 객체를 발생시켜서 처리하려면 반드시 throw 예약어를 이용해야 함