[java] 예외처리

young-gue Park·2023년 7월 27일
0

Java

목록 보기
9/11
post-thumbnail

⚡ 예외처리


이 파트도 이전에 일부 다룬적이 있다.

📌 예외처리(Exception Handling)

⭐ 프로그램의 오류

🔷 어떤 원인에 의해 오동작 하거나 비정상적으로 종료되는 경우

  • 발생시점에 따라 구분이 가능
    1. 컴파일 에러(compile-time error): 문법적 오류
    2. 런타임 에러(runtime error): 실행 과정에서 발생하는 에러
    3. 논리적 에러(logical error): 의도와 다르게 나오는 것
  • 에러(error)
    • 발생하면 복구할 수 없는 심각한 오류
    • 메모리 부족
    • 스택 오버 플로우(Stack over flow): 스택 메모리 공간에서 데이터를 감당하지 못하고 넘칠 때 발생

      💡 스택 오버 플로우는 재귀호출 시에 자주 발생하는 에러이다.

  • 예외(Exception)
    • 프로그래머가 적절한 코드를 통해 대비할 수 있는 오류
    • 클래스 형변환 실패
    • 파일 읽기 실패

⭐ 예외처리

🔷 예외 발생 시 프로그램의 비 정상 종료를 막고 정상적인 실행 상태를 유지하는 것

  • 예외의 감지 및 예외 발생 시 동작할 코드 필요

🔷 예외의 계열

  • RuntimeException 클래스들
    • 프로그래머의 실수로 발생하는 계열
    • unchecked exception
    • 대처 코드가 없어도 컴파일 문제 x

      ❗ 프로그래머가 직접 체크해야한다.

  • Exception 클래스들
    • 사용자의 실수 등 외적인 요인에 의해 발생하는 계열
    • checked exception
    • 대처 코드가 없으면 컴파일 진행 x
int [] nums = {10};
		
// UnChecked Exception
System.out.println(nums[2]);
		
// Checked Exception
Thread.sleep(1000);

🔷 예외 처리 키워드

  • 직접 처리
    1. try
    2. catch
    3. finally
  • 간접 처리
    • throws
  • 사용자 정의 예외 발생시킬 때
    • throw

🔷 예외처리 (try~catch 구문)

  • 프로그램 실행 시 발생할 수 있는 예외에 대한 대비코드 작성
  • 비정상적인 종료를 막고, 정상적인 상태를 유지함
  • 예외 발생시 JVM의 예외처리기가 받아서 처리
  • 중괄호(블록) 생략 불가능
  • 블럭 안에 또 다른 try-catch 구문이 올 수 있음.
try {
	// 예외가 발생할 수 있는 코드
    // 정상 코드
} catch (Exception e) {
	// 예외가 발생했을 때 처리할 코드
}
// 일반 코드

// 1. 예외가 발생했을 때: 1-3-4
int [] nums = {10};
try { 
	System.out.println(nums[2]); // 예외가 발생할 수 있는 코드
	System.out.println();
	int n = 10; // 정상 코드
} catch (ArrayIndexOutOfBoundsException e) {
	System.out.println("크기를 벗어나 버림;"); // 예외 발생 시 처리 코드
}
System.out.println("종료"); // 일반 코드
		
// 2. 예외가 발생하지 않았을 때: 1-2-4
try { 
	System.out.println(nums[0]); // 예외가 발생할 수 있는 코드
	System.out.println();
	int n = 10; // 정상 코드
} catch (ArrayIndexOutOfBoundsException e) {
	System.out.println("크기를 벗어나 버림;"); // 예외 발생 시 처리 코드
}
System.out.println("종료"); // 일반 코드
		
// 3. 예외를 처리하지 못했을 때: 1-중단
try { 
	System.out.println(nums[2]); // 예외가 발생할 수 있는 코드
	System.out.println();
	int n = 10; // 정상 코드
} catch (InputMismatchException e) {
	System.out.println("크기를 벗어나 버림;"); // 예외 발생 시 처리 코드
}
System.out.println("종료"); // 일반 코드

🔷 다중 예외처리

  • try 블록에서 여러 종류의 예외가 발생할 경우
  • 하나의 try 블록에 여러 개의 catch 블록 추가 가능
try {
	// 예외가 발생할 수 있는 코드
} catch (XXException e) {
	// XX 예외가 발생했을 때 처리할 코드
} catch (YYException e) {
	// YY 예외가 발생했을 때 처리할 코드
}

try {
	// 예외가 발생할 수 있는 코드
} catch (XXException e | YYException e) {
	// 필요시 instanceof를 통해 나누어 작성
    if(e instanceof XXException)
    // XX 예외가 발생했을 때 처리할 코드
    else
    // YY 예외가 발생했을 때 처리할 코드
}

다중 예외처리 유의 사항

  • JVM이 던진 예외는 catch 문장을 찾을 때는 다형성이 적용됨
  • 상위 타입의 예외가 먼저 선언되는 경우 뒤에 등장하는 catch 블록은 동작할 기회가 없음
  • 상속 관계가 없는 경우는 무관
  • 상속 관계에서는 작은 범위(자식)에서 큰 범위(조상)순으로 정의

🔷 Exception 인스턴스의 주요 메서드

  • getMessage(): 발생된 예외에 대한 구체적인 메시지를 반환
  • printStackTrace(): 예외 발생 당시의 호출 스택(Call Stack)을 출력한다.

🔷 메서드에서 예외 던지기

  • 메서드 선언부에 throws 키워드를 사용하여 예외 작성
  • 예외가 여러 개일 경우 ,를 이용하여 나열
  • 메서드가 예외를 처리하는 것이 아닌 전달
  • 조상 타입의 예외로 처리 가능
public class ThrowsTest01 {
	public static void main(String[] args) throws ClassNotFoundException {
		// 내부에서 객체를 생성하거나 static 메서드로 변경
		method1();
	}

	public static void method1() throws ClassNotFoundException {
		method2(); // 메인 메서드로 처리 위임 
	}
	
	public static void method2() throws ClassNotFoundException {
		// Checked exception
		Class.forName("GUE"); // 1번 메서드로 처리 위임
	}
}
public class ThrowsTest02 {

	public static void main(String[] args) {
		try {
			method1();
		} catch (ArithmeticException e) {
			System.out.println("그건 안돼용");
		}
	}

	public static void method1() {
		method2(); // 메인 메서드로 처리 위임 
	}
	
	public static void method2() {
		// Unchecked exception
		int i = 1/0; // 1번 메서드로 처리 위임
	}
}

🔷 메서드 재정의와 throws

  • 메서드 재정의 시 조상클래스 메서드가 던지는 예외보다 부모 예외를 던질 수 없다.
import java.io.FileNotFoundException;
import java.io.IOException;

class Parent {
	public void methodA() throws IOException {
		
	}
	public void methodB() throws ClassNotFoundException {
		
	}
}

public class ThrowsTest02 extends Parent {

	public static void main(String[] args) {
	
	}

	@Override
	public void methodA() throws FileNotFoundException {
	}

	// 부모가 던지는 예외보다 더 큰 예외를 자식이 던질 수 없음.
	@Override
	public void methodB() throws Exception {
	}
}

🔷 finally 구문

  • finally는 예외 발생 여부와 상관 없이 언제나 실행
try {
	// 1. 예외가 발생할 수 있는 코드
    return;
} catch (XXException e) {
	// 2. XX 예외가 발생했을 때 처리할 코드
} catch (YYException e) {
	// 3. YY 예외가 발생했을 때 처리할 코드
} finally {
	// 4. 예외 상관 없이 무조건 실행
}
  • 예외 발생시 -> try - catch - finally 순으로 코드 실행
  • 예외 미발생시 -> try - finally 순으로 코드 실행
  • 중간에 return이 있어도 finally 블록 수행 후 반환

    1 - 4 - return

🔷 자동 자원 반납 구문 (try with resources)

  • 자원 등을 반납할 때 finally에서 close()를 통해 반납을 주로 함.
  • 코드가 지저분해지고 다른 예외상황을 발생시킬 수 있음.
// 기존의 지저분한 코드
try {
	fis = new FileInputStream("test.txt");
} catch (FileNotFoundException e) {
	e.printStackTrace();
} finally {
	try {
		if(fis != null)
			fis.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}
  • try() -> 괄호 안에 객체를 생성하는 코드를 작성하면, 해당 객체는 close()를 호출하지 않아도 블록을 벗어나는 순간 close()가 호출됨.
  • 해당 객체의 클래스가 AutoCloseable이라는 인터페이스를 구현한 클래스이어야만 함.
// try with resources
try(FileInputStream fis2 = new FileInputStream("test.txt")) {
	
} catch (Exception e) {
	e.printStackTrace();
}

🔷 사용자 정의 예외

  • 기존에 정의된 예외 이외에 사용자가 직접 정의 예외를 작성할 수 있음.
  • 대부분 Exception 또는 RuntimeException 클래스를 상속받아 작성
    • checked exception 활용: 명시적 예외 처리 또는 throws 필요

      💡 코드는 복잡해지지만 처리, 누락 등 오류 발생 가능성은 down

    • runtime exception 활용: 묵시적 예외 처리 가능

      💡 코드는 간결해지지만 처리, 누락 등 발생 가능

public class MadeException extends Exception {
	// 코드
}
 
public class ThrowsTest {
	public static void main(String[] args) throws MadeException{
		throw new MadeException();	
	}
}
profile
Hodie mihi, Cras tibi

0개의 댓글