본문 바로가기
멀티캠퍼스 풀스택 과정/Java의 정석

자바의 정석5-2 예외 발생, 예외 처리, Finally블럭

by 이쟝 2022. 1. 6.

예외 발생시키기

예외 발생시키기 -> 키워드 throw를 사용해서 프로그래머가 고의로 예외를 발생!

 

1. 연산자 new를 이용해서 발생시키려는 예외클래스의 객체를 만든 다음

   Exception e = new Exception(“고의로 발생시켰음”);

2. 키워드 throw를 이용해서 예외를 발생시킨다.

 

예제1) throw를 사용해서 고의로 예외를 발생시키는 프로그램

 

public class ExceptionTest2 {
	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 ExceptionTest2.main(ExceptionTest2.java:9)

프로그램이 정상 종료되었음

 

-> Exception인스턴스를 생성할 때, 생성자에 String을 넣어주면, 이 String이 Exception 인스턴스에 메시지로 저장된다. 이 메시지는 getMessage( )를 이용해서 얻을 수 있다!


자바 예외 구문 checked 예외, unchecked예외

 

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

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

 

- checked예외는 필수 처리!! (try-catch문 필수)

- unchecked예외try-catch문 없이 throw만으로 사용 가능 -> 컴파일은 성공했지만 비정상 종료(try-catch문 선택)

 

throw new Exception();        // Exception을 고의로 발생시킴(컴파일 불가 try-catch문의 부재)
throw new RuntimeException(); // RuntimeException을 고의 발생시킴(컴파일 가능, 하지만 비정상적인 종료)

 

try { // Exception과 그 자손은 반드시 예외처리를 해줘야 한다.
	throw new Exception();  
} catch(Exception e) {
}
try {
	throw new RuntimeException();  
} catch(Exception e) {
}

 

더보기
더보기

throw new Exception으로 예외가 발생했지만 밑의 catch(Exception e) 구문으로 예외처리되었다.

두 번째 try구문으로 들어가면서 throw new RuntimeException으로 예외가 발생했지만 밑에 catch(Exception e) 구문으로 예외처리되었다. 


메서드에 예외 선언하기

 

<예외를 처리하는 방법>

(1) try-catch문(직접 처리), (2) 예외 선언하기(예외 떠넘기기)

 

- 예외 선언하기: 메서드가 호출 시 발생 가능한 예외를 호출하는 쪽에 알리는 것(예외를 떠넘기는 것이고 처리하는 것이 아님) -> main 메서드까지 예외처리가 안된다면 비정상적으로 종료!

- 메서드의 예외 선언하기(메서드의 선언부에 키워드 throws를 사용해서 메서드 내에서 발생할 수 있는 예외를 적어주면 되고, 여러 개일 경우 쉼표로 구분)

 

-> 예외를 발생시키는 키워드 throw예외를 메서드에 선언할 때 쓰이는 throws 잘 구분하기!!!!

- 예외선언은 Exception 자손만! RuntimeException 선언해도 되기는 하는데 보통 안 함

 

-> 만일 예외의 최고조 상인 Exception클래스를 메서드에 선언하면 이 메서드는 모든 종류의 예외가 발생할 가능성이 있다는 뜻!

 

static void startInstall() throws SpaceException, MemoryException { 
	if(!enoughSpace())        // 충분한 설치공간이 없으면..
		throw new SpaceException("설치할 공간이 부족합니다.");
	if(!engouhMemory())
		thrwo new MemoryException("메모리가 부족합니다.");
} // startInstall메서드의 끝

 

-> startInstall 메서드를 호출하면 SpaceExceptionMemoryException 예외가 발생할 수 있다는 것을 선언부에 적음!

-> StartInstall 메서드를 호출한 메서드가 또다시 자신을 호출한 메서드에게 전달하거나 예외처리를 할 수 있음!

-> 이런식으로 계속 호출스택에 있는 메서드들을 따라 전달되다가 main메서드에서도 예외처리가 되지 않으면, main메서드마저 종료되어 프로그램 전체가 비정상 종료됨!

 

예제1) 메서드의 예외 선언 흐름

 

public class ExceptionTest3 {

	public static void main(String[] args) throws Exception {
		method1();  // 같은 클래스 내의 static 멤버이므로 객체생성없이 직접 호출가능
	} // main메서드의 끝 
	
	static void method1() throws Exception { //try-catch구문이 없어서 예외처리 실패
		method2(); 
	} // method1()의 끝
	
	static void method2() throws Exception { // try-catch구문이 없어서 예외처리 실패
		throw new Exception(); 
	} // method2()의 끝
}

 

더보기
더보기

(1) main 메서드method1 호출

(2) method1이 method2호출 -> method2에서 예외 발생, 처리 하지 못함(try-catch구문이 없어서)

(3) 예외를 method2 -> method1 에게 떠넘김(선언)

(4) method1 예외 처리 하지 못함(try-catch구문이 없어서) ->

(5) 예외를 method1 ->  main 메서드에게 떠넘김(선언)

(6) main메서드도 예외를 처리하지 못함(try-catch구문이 없어서)

(7) mian메서드가 종료가 되고 프로그램이 비정상 종료되면서 예외는 JVM(자바버추얼머신)한테 가게됨

 

<출력결과>

Exception in thread "main" java.lang.RuntimeException

       at ExceptionTest3.method2(ExceptionTest3.java:13)

       at ExceptionTest3.method1(ExceptionTest3.java:9)

       at ExceptionTest3.main(ExceptionTest3.java:5)

 

-> 예외가 발생한 당시의 호출스택의 상황을 보여주고 있음

1) 예외가 발생했을 때, 모두 3개의 메서드(main, method2, method1)가 호출스택에 있었음

2) 예외가 발생한 곳은 제일 윗줄에 있는 method2( ), 13번째 줄라는 것

3) main메서드method1( ), method1method2( )를 호출했다는 것을 알 수 있음(아래서부터 위로)


예제2) 메서드의 예외선언하기

1) main메서드에서 try-catch구문 사용하기

(method1이 예외선언하고 main메서드가 예외처리(try-catch문으로!)

 

public class FinallyTest3 {
	public static void main(String[] args) {
		try {
			method1(); // java:6 (3)
		} catch (Exception e) {
			System.out.println("main메서드에서 예외가 처리되었습니다. "); // (1)
			e.printStackTrace();
		}
	} // main메서드의 끝

	static void method1() throws Exception {
			throw new Exception("method에서 예외가 발생했습니다."); // java:14 (2)
	} // method1의 끝
} // class의 끝

 

더보기
더보기

<출력값>

main메서드에서 예외가 처리되었습니다.

java.lang.Exception: method에서 예외가 발생했습니다.

       at FinallyTest3.method1(FinallyTest3.java:14)

       at FinallyTest3.main(FinallyTest3.java:6)

 

(1) 예외처리 완료된 catch구문부터 출력!(main 메서드에)

(2) 예외 발생된 곳 출력 (throw new Exception("method에서 예외가 발생했습니다."))

(3) 예외 사항이 있는 메서드를 호출한 곳!  main메서드에서 method1( );

 

2) method1 메서드(자기자신이 직접)에서 try-catch구문 사용하기(예외처리하기)

 

public class FinallyTest3 {

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

	static void method1() {
		try {
			throw new Exception("method1에서 예외 발생했습니다."); 
		} catch ( Exception e ) {
			System.out.println("method1에서 예외 처리되었습니다."); 
			System.out.println("끝!"); //
		} 
	}
}

 

더보기
더보기

<출력값>

method1에서 예외 처리되었습니다.

!

-> method1( )이 직접 예외처리하기 때문에 메서드 선언부에 throws를 안적어도 된다.

-> 예외가 method1( )에서 발생했지만 catch구문에서 예외처리 되었다!


Finally블럭

- 예외 발생여부와 관계없이 수행되어야 하는 코드 작성

 

 

-> 예외가 발생한 경우 ‘try -> catch -> finally’ 의 순으로 실행. 예외가 발생하지 않은 경우 ‘try -> finally’ 의 순으로 실행

 

예제1) Finally블록을 사용하지 않고 프로그램 작성(코드의 중복 생김)

 

public class FinallyTest {
	public static void main(String[] args) {
		try {
			startInstall();      // 프로그램설치에 필요한 준비하기
			copyFiles();         // 파일들 복사
			deleteTempFiles();    // 프로그램 설치에 사용된 임시파일 삭제
		} catch ( Exception e ) {
			e.printStackTrace();
			deleteTempFiles();    // 프로그램 설치에 사용된 임시파일 삭제
		} // try-catch의 끝
	} // main의 끝

	static void startInstall() {
		/* 프로그램 설치에 필요한 준비를 하는 코드 */	
	}
	static void copyFiles() { /* 파일을 복사하는 코드 */}
	static void deleteTempFiles() { /* 파일을 삭제하는 코드 */ }
}

 

예제2) Finally블록을 사용하고 프로그램 작성

 

try {
    startInstall();      // 프로그램설치에 필요한 준비하기
    copyFiles();         // 파일들 복사
} catch ( Exception e ) {
	e.printStackTrace();
} finally {	
	deleteTempFiles();    // 프로그램 설치에 사용된 임시파일 삭제
	} // try-catch의 끝
} // main의 끝

 

-> 예제1과 예제2 나머지는 다 동일하고 중복되는 코드를 삭제하고 finally 블록을 추가해준다!

 

예제3) method1을 호출하면서 흐름 이해하기

 

public class FinallyTest3 {
	public static void main(String[] args) {
		// method1은 static 메서드여서 객체 없이 사용 가능
		FinallyTest3.method1(); // method1을 호출
		System.out.println("method1()의 수행을 마치고 main메서드로 돌아왔습니다.");
	}
	static void method1() {
		try {
			System.out.println("method1()이 호출되었습니다.");
			return;     // 현재 실행 중인 메서드를 종료
		} catch ( Exception e ) { 
			e.printStackTrace();
		} finally {
			System.out.println("method1()의 finally블럭이 실행되었습니다.");
		}
	}
}

 

더보기
더보기

<출력>

method1() 호출되었습니다.

method1() finally블럭이 실행되었습니다.

method1() 수행을 마치고 main메서드로 돌아왔습니다.

 

-> method1 호출되고 “method1() 호출되었습니다 출력되면서 반환됨. 에러가 있다면 catch 들어가서 e.printStackTrac() 출력하겠지만,여기서는 예외가 없고 반환되면서 바로 finally 구문으로 넘어가서 출력된다!