Java/Java의 정석

Chapter 08 예외처리 Exception Handling

계란💕 2022. 2. 23. 12:27

1. 예외처리(Exception Handling)

  Def) 예외: 컴파일도 되고 실행도 되지만 의도와 다른게 동작하는 것

 

  1.1 프로그램 오류

  - 컴파일 에러: 컴파일 시에 발생하는 에러 ex) system.out.println(); => 오류

  - 런타임에러: 실행 시에 발생하는 에러

  - 논리적 에러: 실행되지만, 의도와 다르게 동작하는 것

  - 에러와 예외 차이

    -> 에러: 코드에 의해 수습될 수 없는 심각한 오류

    -> 예외: 코드로 수습할 정도의 미약한 오류(실행 잘 되지만 의도와 다르게 동작)

  - 예외처리의 정의, 목적

    Def) 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것

  - 목적: 비정상 종료를 막고, 정상적 실행상태를 유지한다. 

 

  1.2 예외 클래스의 계층구도

  - 모든 예외는 Exception(모든 예외의 최고 조상)의 자손 클래스

  - checked예외: 컴파일러가 예외처리 여부를 체크(예외 처리(try-catch) 필수)

    -> Exception클래스와 그 자손들,  RuntimeException는 제외)

  - unchecked예외: 컴파일러가 예외처리 여부를 체크하지 않는다 (예외 처리 선택) -  (RuntimeException와 자손들)

  - RuntimeException클래스들은 주로 프로그래머의 실수에 의해 발생될 수 있는 예외들이다. 

  - Exception클래스들은 주로 외부의 영향으로 발생할 수 있는 것들이다.

    -> 주로 프로그램 사용자의 동작에 의해 발생한다.

 

  1.3 예외처리하기 try - catch문

  - 예외발생하면 이를 처리할 catch블럭을 찾아 내려간다.

  - 일치하는 catch블럭을 찾지 못하면 예외는 처리되지 못한다. 

  - Exception 선언된 catch블럭은 맨 마지막 블럭.

    

c++
열기

  Note) 하나의 try블럭 다음에는 여러 종류의 예외를 처리할 수 있도록 하나 이상의 catch블럭이 올 수 있다.

  - 이 중에서 발생한 예외와 일치하는 단 한 개의 catch블럭만 수행된다. 

  - 일치하는 예외가 없으면 처리 되지 않는다.

   - catch블럭 내에 또 다른 try-catch문이 포함된 경우에는 같은 이름의 참조변수를 사용할 수 없다.

 

  1.5 예외발생과 catch블럭

  - printStackTrace() : 예외발생 당시의 호출 스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 출력

  - getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.

  - 멀티 catch블럭: 내용이 같은 catch블럭을 하나로 합친 것

 

  1.6 예외 발생시키기 

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

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

  Ex)

c++
열기

  Note)

  - 예외 발생시키는 두 줄을 간략히, throw new exception("고의로 발생시켰음."); 으로 나타낼 수 있다.

 

  1.7 메서드에 예외 선언하기

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

c++
열기

  - 만일 위에 Exception만 선언하면 예외뿐만 아니라  자손타입의 예외까지도 발생할 가능성이 있다는 것이다.

  - 오버라이딩 할 때는 선언된 예외의 개수가 아니라 상속관계까지 고려해야한다.

 

  Ex)

c++
열기

  Note) 실행 결과

  - 프로그램의 실행도중 java.lang.Exception이 발생하여 비정상적으로 종료했다.

  - 예외 발생했을 때의 호출 스택내용을 알 수 있다.

    1) 예외가 발생했을 때, 모두 3개의 메서드가 호출 스택에 있었다.

    2) 예외가 발생한 곳은 제일 윗줄에 있는 method2()

    3) main메서드가 method1() 호출, 그리고 method1()은 method2()를 호출했다. 

 

  - method2에서 예외가 (강제로) 발생했으나 예외처리를 해주지 않았기 때문에 메서드 종료하고

  - 자신을 호출한 method1()에게 예외를 넘겨준다. 

  - 그러나 main메서드에서조차 예외처리를 해주지 않아서 main이 종료되어

  - 프로그램이 예외로 인해 비정상적으로 종료된다.

  - 따라서 어느 한 곳에서 반드시 try-catch문으로 예외처리해야한다.

 

  Ex 8-15)

c++
닫기
package javaStudy;
import java.io.*;
public class ExceptionEx15 {
	public static void main(String[] args) {
		// command line에서 입력받은 값을 이름으로 갖는 파일을 생성한다.
		File f = createFile(args[0]);
		System.out.println(f.getName() + " 파일이 성공적으로 생성되었습니다."  );	
	}
	static File createFile(String fileName) {
		try {
			if( fileName == null || fileName.equals("") ) 
				throw new Exception("파일이름이 유효하지 않습니다.");
		}catch (Exception e){
			// fileName이 부적절한 경우, 파일이름을 '제목없음.txt'로 한다.
			fileName = "제목없음.txt";
		}finally {
			File f  = new File(fileName); // File클래스의 객체를 만든다.
			createNewFile(f);		// 생성된 객체를 이용해서 파일을 생성한다.
			return f;
		}
	}
	static void createNewFile(File f) {
		try {
			f.createNewFile();	// 파일을 생성한다. 
			
		}catch(Exception e) {}	
	}
}

  Note)

  - 이 예제는 예외가 발생한 메서드에서 직접 예외를 처리하도록 되어 있다. 

  - createFile메서드를 호출한 main메서드에서는 예외가 발생한 사실을 알지 못한다. 

 

  1.8 finally블럭

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

c++
열기

 

  1.9 자동 자원 반환 - try - with - resources 문

  - try-catch문의 변형이다.

  - 입출력에 사용되는 클래스 중에서는 사용 후에 꼭 닫아줘야하는 것들이 있다. 

  - 그래야 사용했던 자원이 반환(resource)되기 때문이다.

  Ex)

c++
열기

  Note) 실행 결과

  - main메서드에 두 개의 try-catch문이 있는데 첫 번째 것은 close()에서만 예외를 발생시키고

  - 두 번째 것은 exceptionWork()와 close()에서 모두 예외를 발생시킨다.

  - 첫번째 것은 일반적인 예외가 발생했을 때와 같은 형태

  - 두 번째는 출력형태가 다른.

  - 두 예외가 동시에 발생할 수는 없다. 실제 발생한 예외를 'WorkException'이라고한다.

  - CloseException은 억제된 예외이다.

    -> void addSuppressed(Throwable exception) : 억제된 예외를 추가

    -> Throwable [] getSuppressed() : 억제된 예외(배열)을 반환한다. 

 

  1.10 사용자 정의 예외 만들기

  - 우리가 직접 예외를 정의할 수 있다.

  - Exception과 RuntimeException 중에 조상을 선택한다. 

c++
열기

 

  Ex)

c++
열기

  Note) 실행 결과

  - MemoryException, SpaceException 두 개의 사용자 정의 예외 클래스를 새로 만들었다. 

  - 두 예외는 startInstall()을 수행하는 동안 발생할 수 있으며

  - enoughSpace()와 enoughMemory()의 실행 결과에 따라 예외의 종류가 달라지기도한다. 

 

  1.11 예외 되던지기(exception re-throwing)

  - 예외를 처리한 후에 다시 예외를 발생시키는 것

  - 호출한 메서드와 호출된 메서드 양쪽 모두에서 예외처리하는 것

  Ex)

c++
열기

  

  Note) 실행 결과

  - method1()과 main매서드 양쪽의 catch블럭이 모두 수행되었다.

  - method1()의 catch블럭에서 예외를 처리하고도 throw문을 통해 다시 예외발생시켰다

  - 그 예외를 main메서드에서 한 번 더 처리했다.

 

  1.12 연결된 예외(chained exception)

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

  - 예외 A가 예외B를 발생시키면 A는 B의 원인예외(cause exception)

c++
열기

  -  initCause()는 throwable 클래스에 정의 되어 있어서 모든 예외에서 사용 가능.

 

  - 연결된 예외를 사용하는 이유

    1) 여러 예외를 하나로 묶어 다루기 위해

    2) checked예외를 unchecked예외로 변경하려할 때 (예외처리가 선택적인 사항이 된다.)

    3) 예외의 상속 관계를 변경하지 않아도 된다.

  Ex)

c++
열기

  Note) 실행 결과

  - throw new RuntimeException(new MemoryException("메모리가 부족합니다."));

    -> MemoryException은 Exception의 자손이므로 반드시 예외처리를 해야한다. 

    -> 이 예외를 RuntimeException으로 감쌌기 때문에 unchecked예외가 되었다.