[2023.08.22] 예외처리

이재하·2023년 8월 21일
2

항해99

목록 보기
26/48

프로그램 오류

컴파일러가 하는 일
- 구문체크 (e.g. 빨간 밑줄)
- 번역
- 최적화 (e.g. 상수 계산이나 생략된 코드 추가)


컴파일 에러 : 컴파일 할 때 발생하는 에러
런타임 에러 : 실행 할 때 발생하는 에러
논리적 에러 : 작성 의도와 다르게 동작

Java의 런타임 에러

에러 (Error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
예외 (Exceoption) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류

  • 에러는 어쩔 수 없지만, 예외는 처리하자.

예외처리
예외의 발생에 대비한 코드를 작성하는 것
프로그램의 비정상 종료를 막기 위해 !!


예외 클래스의 계층 구조

  • 자바에서는 실행 시 발생할 수 있는 오류 (에러 와 예외) 를 클래스로 정의하였다.

  • Object 는 모든 클래스의 부모 클래스이기 떄문에 에러와 예외 클래스 역시 Object 클래스의 자식들이다.

  • Exception 클래스들 : 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외

    	FileNotFoundException : 존재하지 않는 파일의 이름을 입력한 경우 발생
    	ClsssNotFoundException : 클래스의 이름을 잘못 적었을 경우 발생
    	DataFormatException : 입력한 데이터 형식이 잘못된 경우 발생
  • RuntimeException 클래스들 : 프로그래머의 실수로 발생하는 예외

    	ArrayIndexOutOfBoundsException : 배열의 범위를 벗어난 경우 발생
    	NullPointerException : 값이 null인 참조변수의 멤버를 호출하려 하는 경우 발생
    	ClassCastException : 클래스의 형변환을 잘못한 경우 발생
    	ArithmeticException : 정수를 0으로 나누려고 하는 경우 발생

예외 처리하기

try-catch 문

try {
	// 예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch (Exception1 e1) {
	// Exception1 이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
} catch (Exception2 e2) {
	// Exception2 이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
} catch (ExceptionN eN) {
	// ExceptionN 이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
}
if문과 달리, try 블럭이나 catch 블럭 내에 포함된 문장이 하나뿐이어도 괄호 {  } 생략 불가 ! ! !

try-catch 문에서의 흐름

  1. try 블럭 내에서 예외가 발생한 경우
    1. 발생한 예외와 일치하는 catch 블럭이 있는지 확인한다.
    2. 일치하는 catch 블럭을 찾게되면, 그 catch 블럭 내의 문장들을 수행하고 전체 try-catch 문을 빠져나간다. 만약 일치하는 catch 블럭이 없다면 예외는 처리되지 못한다.
  1. try 블럭 내에서 예외가 발생하지 않은 경우
    1. catch 블럭을 거치지 않고 전체 try-catch 문을 빠져나간다.

if 문과 마찬가지로 try 블럭에서 예외가 발생하면 예외가 발생한 위치 이후에 있는 try 블럭의 문장들은 수행되지 않으므로 try 블럭에 포함시킬 코드의 범위를 잘 선택해야 한다.

예제

// 예외가 발생하지 않았을 때
class Ex8_1 {
	public static void main(String args[]) {
			System.out.println(1);			
			try {
				System.out.println(2);
				System.out.println(3);
			} catch (Exception e)    {
				System.out.println(4); // 예외가 발생하지 않아서 수행되지 않음
			} // try-catch의 끝
			System.out.println(5);
	}
}

// 결과
1
2
3
5

// 예외가 발생했을 때
class Ex8_2 {
	public static void main(String args[]) {
			System.out.println(1);
			try {
				System.out.println(0/0); // 예외 발생!!! 
                // 예외 발생 이후 try 블럭 문장들은
				System.out.println(2); 	// 실행되지 않는다.
			} catch (ArithmeticException ae)	{
				System.out.println(3);
			}	// try-catch의 끝
			System.out.println(4);
	}	// main메서드의 끝
}

// 결과
1
3
4

예외의 발생과 catch 블럭

  • try 블럭에서 예외가 발생하면, 발생한 예외를 처리할 catch 블럭을 찾는다.

  • 첫번째 catch 블럭부터 순서대로 찾아 내려가며, 일치하는 catch 블럭이 없으면 예외는 처리되지 않는다.

  • 예외의 최고 조상인 Exception을 처리하는 catch 블럭은 모든 종류의 예외를 처리할 수 있다.
    (반드시 마지막 catch 블럭이어야 한다.)

    // 결과
    1
    2
    3
    true
    ArithmeticException
    6
    
    catch 문 수행후 바로 try-catch 문 빠져나오기 때문에 Exception 출력 안됨

마지막으로

  • 발생한 예외 객체를 catch 블럭의 참조변수로 접근할 수 있다.

아래에 후술하겠다.


printStackTrace() 와 getMessage()

자주 쓰이는 두가지이다.

printStackTrace() : 예외발생 당시의 호출스택(Call Stack)에 있었던 메소드의 정보와 예외 메시지를 화면에 출력한다.
getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.

  • 예외가 발생하면 예외 객체가 생긴다.

멀티 catch블럭

  • 내용이 같은 catch블럭을 하나로 합친 것 (중복 제거)
...
try {
	...
} catch (ExceptionA | ExceptionB e) {	// 여러개의 catch 블럭을 |로 합칠 수 있다.
	e.printStackTrace();
}
  • 부모 자식 관계일 때는 멀티 catch 블럭이 아닌 부모 타입의 참조변수가 선언된 catch 블럭 사용
try {
	...
// } catch (ParentException | ChildException e { // 에러!
} catch (ParentException e) { // Ok. 위의 라인과 의미상 동일
	e.printStackTrace();
}
  • 참조변수는 두 예외의 공통된 부분만 사용가능하다.
try (
	...
} catch (ExceptionA | ExceptionB e) {
	e.methodA(); // 에러! ExceptionA에 선언된  methodA()는 호출불가
    
    if(e instanceof ExceptionA) {
    	ExceptionA 31 = (ExceptionA)e;
        e1.methodA(); // Ok. ExceptionA에 선언된 메소드 호출가능
    } else { // if(e instanceof ExceptionB)
    	...

정 사용하고 싶으면 if-else 문으로 형변환... 굳이


예외 발생시키기

  1. 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 다음
    Exception e = new Exception("고의로 발생시켰음");
  2. 키워드 throw를 이용해서 예외를 발생시킨다.
    throw e;
class Ex8_6 {
	public static void main(String args[]) {
		try {
			Exception e = new Exception("고의로 발생시켰음.");
			throw e;	 // 예외를 발생시킴
		//  throw new Exception("고의로 발생시켰음.");

		} catch (Exception e)	{
			System.out.println("에러 메시지 : " + e.getMessage());
			e.printStackTrace();
		}
		System.out.println("프로그램이 정상 종료되었음.");
	}
}

// 결과
에러메시지 : 고의로 발생시켰음
java.lang.Exception: 고의로 발생시켰음
	  at Ex8_6.main(Ex8_6.java:4)
프로그램이 정상 종료되었음.

e.printStackTrace가 스택의 내용을 찍어서 프로그램 비정상 종료때와 비슷함 하지만 정상 종료됨.
try에서 throw 로 던져서 catch 로 예외 처리했기 때문.


checked 예외, unchecked 예외

Exception과 자손
checked 예외 : 컴파일러가 예외 처리 여부를 체크 (예외처리 필수)

RuntimeException과 자손
unchecked 예외 : 컴파일러가 예외 처리 여부를 체크 안함 (예외처리 선택)


메소드에 예외 선언하기

  • 예외를 처리하는 방법 :
    1. try-catch
    2.예외 선언하기 (예외 떠넘기기 = 알리기)
    3. 은폐 (catch 블럭에 아무것도 안넣기)

메소드가 호출시 발생가능한 예외를 호출하는 쪽에 알리는 것

구별하기!
예외를 발생시키는 키워드 throw
예외를 메소드에 선언할 때 쓰이는 throws

import java.io.*;

class Ex8_10 {
	public static void main(String[] args) {
		try {
			File f = createFile(args[0]);
			System.out.println( f.getName()+"파일이 성공적으로 생성되었습니다.");
		} catch (Exception e) {
			System.out.println(e.getMessage()+" 다시 입력해 주시기 바랍니다.");
		}
	}	// main메서드의 끝

	static File createFile(String fileName) throws Exception {
		if (fileName==null || fileName.equals(""))
			throw new Exception("파일이름이 유효하지 않습니다.");
		File f = new File(fileName);		//  File클래스의 객체를 만든다.
     	// File객체의 createNewFile메서드를 이용해서 실제 파일을 생성한다.
		f.createNewFile();
		return f;		// 생성된 객체의 참조를 반환한다.
	}	// createFile메서드의 끝
}	// 클래스의 끝

예외가 발생한 메소드 내에서 자체적으로 처리해도 되는 것은 메소드 내에서 try-catch 문을 이용해서 처리하고,
메소드 내에서 자체적으로 해결이 안되는 경우에는 메소드에 예외를 선언해서 호출한 메소드에서 처리하도록 한다.

fianlly 블럭

  • 예외 발생여부와 관계없이 수행되어야 하는 코드를 넣는다.
    try 블럭 안에 return 문이 있어서 try 블럭을 벗어날 때도 finally 블럭이 실행된다
try {
	// 예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch (Exception e1) {
	// 예외처리를 위한 문장을 적는다.
} finally {
	// 예외 발생여부에 관계없이 항상 수행되어야 하는 문장들을 넣는다.
    // finally 블럭은 try-catch 문의 마지막에 위치한다.
}
try {
	startInstall();
    copyFiles();
} catch (Exception e) {
	e.printStackTrace();
} finally {
	deleteTempFiles();
} // try-catch의 끝

사용자 정의 예외 만들기

  • 직접 예외 클래스를 정의할 수 있다.
  • 조상은 Exception과 RuntimeException 중에서 선택

예외 되던지기

  • 예외를 처리한 후에 다시 예외를 발생시키는 것
  • 호출한 메소드와 호출된 메소드 양쪽 모두에서 예외처리하는 것
class Ex8_12 {
	public static void main(String[] args) {
		try  {
			method1();		
		} catch (Exception e)	{
			System.out.println("main메서드에서 예외가 처리되었습니다.");
		}
	}	// main메서드의 끝

	static void method1() throws Exception {
		try {
			throw new Exception();
		} catch (Exception e) {
			System.out.println("method1메서드에서 예외가 처리되었습니다.");
			throw e;			// 다시 예외를 발생시킨다.
		}
	}	// method1메서드의 끝
}

연결된 예외 (chained exception)

  • 한 예외가 다른 예외를 발생시킬 수 있다.


0개의 댓글