관리 메뉴

나구리의 개발공부기록

자바의 정석 기초편 ch8 - 11 ~ 18[예외선언하기, finally블럭, 사용자정의예외만들기, 예외 되 던지기, 연결된 예외] 본문

유튜브 공부/JAVA의 정석 기초편(유튜브)

자바의 정석 기초편 ch8 - 11 ~ 18[예외선언하기, finally블럭, 사용자정의예외만들기, 예외 되 던지기, 연결된 예외]

소소한나구리 2023. 12. 5. 10:02

1) 예외를 처리하는 방법

(1) try-catch문

  • 직접 처리하는 방법

(2) 메서드에 예외 선언하기

  • 예외를 호출하는 쪽에 알리는 것으로 예외를 떠넘긴다고도 표현함
  • 체크드 예외와 언체크드 예외를 모두 적어도 되지만 보통 체크드 예외만 적는 것이 정석
// 메서드의 예외 선언
// - throws 이후부터 예외1,2 ~ N까지의 예외의 상황이 발생할 수 있음
void method() throws Exception1, Exception2, ... ExceptionN {
	// 메서드 내용
}


// 메서드의 예외 선언
// - 모든 예외의 최고조상인 Exception으로 모든 예외가 발생할 수 있음
// Exception은 모든 예외의 최고 조상이므로 위의 3가지의 예외를 선언한 것보다 예외를 더 많이 선언한 것이 됨.
void method() throws Exception {
	// 메서드 내용
}

 

(3) 예제

  • 위 예제는 예외가 발생하면 처리하지 않고 예외가 발생된 메서드에 끝부분에 throw를 작성하여 예외를 그대로 밖으로 던짐
  • 아래 예제는 예외가 발생하는 부분에 대해 직접 try - catch로 예외를 처리
// 예외를 호출한 곳에서 처리하도록 throw로 던지는 예제
import java.io.*;

class ex8_10 {
	public static void main(String[] args) {
		try {
			File f = createFile(""); // 예외 발생
			System.out.println( f.getName()+"파일이 성공적으로 생성되었습니다.");
            
		} catch (Exception e) {
			System.out.println(e.getMessage()+" 다시 입력해 주시기 바랍니다.");
            
		}
	}	// main메서드의 끝
	
	// 예외가 발생하면 밖으로 던지기 위해 메서드에 throws Exception이 선언됨
	static File createFile(String fileName) throws Exception {
		if (fileName==null || fileName.equals(""))
			throw new Exception("파일이름이 유효하지 않습니다.");
        
		File f = new File(fileName); //  File클래스의 객체를 만듦
     	f.createNewFile();           // createNewFile메서드를 이용해서 실제 파일을 생성
		return f; // 생성된 객체의 참조를 반환
        
	}	// createFile메서드의 끝
}	// 클래스의 끝

// 예외를 try - catch로 직접 처리한 예제

import java.io.*;

class ex8_10 {
	public static void main(String[] args) {
        File f = createFile(""); // 예외 발생
        System.out.println( f.getName()+"파일이 성공적으로 생성되었습니다.");
	}	// main메서드의 끝

    static File createFile(String fileName) {
        try {
            if (fileName==null || fileName.equals(""))
                throw new Exception("파일이름이 유효하지 않습니다.");
                
        } catch (Exception e) {
            fileName = "이름없음.txt";
        }
        
        File f = new File(fileName);		//  File클래스의 객체를 만듦
        
        try {
            f.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return f;		// 생성된 객체의 참조를 반환한다.
    }	// createFile메서드의 끝
}	// 클래스의 끝

 

(4) 은폐

  • 발생된 예외를 감춤
  • catch문으로 발생된 예외를 잡지만 빈블럭으로 둠으로써 예외를 잡고 그에 따라 실행되는 코드가 없이 그냥 정상흐름으로 넘어감
  • 거의 사용하지 않지만 빅데이터를 사용할 때 일부 문제있는 데이터를 버리는 용도로 사용하기도 함

2) Finally블럭

  • 예외 발생여부와 관계없이 프로그램 로직상에서 꼭 수행되어야 하는 코드를 작성
  • try블럭 안에 return문이 있어서 try블럭을 벗어나도 finally블럭은 실행 됨, 즉 무조건 실행된다고 보면됨
  • try-catch문 작성시 발생되는 코드의 중복을 제거하기 위해 만들어졌으며 catch문 다음에 선언함(가장 마지막에 선언)
try {
	// 예외가 발생할 가능성이 있는 문장 작성
} catch (Exception1 e1) {
	// 예외 처리를 위한 문장 작성
} finally {
	// 예외의 발생여부에 관계없이 항상 수행되어야하는 문장들을 작성
	// finally블럭은 try-catch문의 맨 마지막에 위치
}

// try-catch의 코드 중복 발생
try {
	startInstall();
	copyFile();
	deleteTempFiles();	// 임시파일 삭제
} catch {
	e.printStackTrace();
	deleteTempFiles();	// 임시파일 삭제
}

// 임시파일을 삭제하는 코드가 중복이되어 finally 문으로 중복을 제거
try {
	startInstall();
	copyFile();
} catch {
	e.printStackTrace();
} finally {
	deleteTempFiles();	// 임시파일 삭제
}

3) 사용자 정의 예외 만들기

  • 사용자가 직접 예외 클래스를 정의하여 사용할 수 있으며 상속을 통해서 작성함
  • 조상은 Exception / RuntimeException 예외 중에서 선택하여 예외를 생성할 수 있음,
  • 일반적으로 선택처리가 가능한 RuntimeException으로 예외를 만드는 경우가 많고 꼭 필요한 경우에는 Exception으로도 할 수 있음
class MyException extends Exception {	// 필수 처리 예외(try - catch문이 필수)
	MyException(String msg) { // 문자열을 매개변수로 받는 생성자 필수
		super(msg);	// 조상인 Exception클래스의 생성자를 호출 - 보통 넣어줌
	}
}

4) 예외 되던지기

  • 예외를 처리한 후에 다시 예외를 발생시키는 것
  • try-catch로 예외를 처리(전부 처리하거나 일부)했지만 메서드에 throws로 처리한 예외(혹은 아직 처리하지 못한 예외를)를 던져서 호출한쪽에서 예외를 처리하도록 함
  • 호출한 메서드와 호출된 메서드 양쪽에서 각각 2번 예외를 처리를 하도록 코드를 설계하는 것
  • 이러한 상황이 거의 발생하지 않을 수 있지만 실무에서는 여러가지 상황을 마주할 수 있으므로 알고있으면 좋음
class Ex8_12 {
	public static void main(String[] args) {
		try  {
			method1();	// 메서드 호출
		} catch (Exception e)	{	// 예외 처리2(예외선언 처리)
			System.out.println("main메서드에서 예외가 처리되었습니다.");
		}
	}	// main메서드의 끝

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

5) 연결된 예외(chained exception)

  • 한 예외가 다른 예외를 발생시키거나 하나의 예외안에 또다른 예외를 포함
  • 예외A가 예외B를 발생시키면 A는 B의 원인예외(cause exception)가 됨

(1) Throwable 클래스의 일부

  • Throwable initCause(Throwable cause) : 지정한 예외를 원인 예외로 등록하는 메서드가 정의 되어있음
  • Throwable getCause() : 원인 예외를 반환하는 메서드가 정의 되어있음
// 예외 안에 또다른 예외를 포함

public class Throwable implements Serializable {
	...
	private Throwable cause = this;	// 객체 자신을 원인 예외로 등록
	
	...
    
	public synchronized Throwable initCause(Throwable cause) {
    
	...
    	this.cause = cause;	// cause를 원인 예외로 등록
    	return this;
    }
    ...
    
}

 

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

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

  • 코드의 중복을 일부 제거할 수 있음
  • 예외가 발생한 원인은 SpaceException 예외 이지만 catch문에서 새로운 예외를 하나 생성하여 해당 예외의 원인예외로 SpaceException 예외를 등록하고 새로 생성된 예외를 던짐
  • 메서드에 throws문을 작성하여 원인예외를 담아 새로 작성한 예외를 밖으로 던짐
  • 예외 정보나 예외 메세지를 출력 시 예외의 이유를 세부적으로 알 수 있음
// 여러 예외가 발생 시 코드 중복 발생
try {
	install();
} catch(SpaceException e) {
	e.printStackTrace();
} catch(MemoryException e) {
	e.printStackTrace();
} catch(Exception e) {
	e.printStackTrace();
}
    
// install()를 정의하여 SpaceException,MemoryException을 한개의 InstallException으로 변경
void install() throws InstallException { // 예외 B로 예외를 밖으로 던짐
	try {
		startInstall();  // SpaceException발생, 예외 A라고 가정
		copyFiles();
	} catch (SpaceException e) {
		InstallException ie = new InstallException("설치중 예외발생");	// 예외 B라고 가정
		ie.initCause(e);  // InstallException의 원인예외를 SpaceException으로 지정
		throw ie;         // InstallException을 발생, 예외 B를 발생
	} catch (MemoryException me) {
    	...
      
// 실제 발생한 예외는 SpaceException 이지만 InstallException 안에 포함시켜서 밖으로 던짐

// 코드의 중복을 일부 제거함
try {
	install();
} catch(InstallException e) {
	e.printStackTrace();
} catch(Exception e) {
	e.printStackTrace();
}

 

(2) checked예외를 unchecked예외로 변경하려 할 때

  • try - catch를 쓰지 않아도되는 필수 처리 예외가 존재할 경우 사용함
  • 조상을 바뀌어서 언체크드 예외로 변경할 수도 있지만, 해당 예외가 여러곳에 사용 중이라면 상속계층도를 바꾸는 것이 어렵기 때문에 특정 시점에 연결된 예외를 써서 런타임 예외로 변경할 수 있음
// SpaceException을 RuntimeException으로 변경하고자 할 때 조상을 바꿔 주면 되지만
// SpaceException이 여러 곳에 쓰이고 있다면 상속계층도를 변경하는 것은 어려움 -> 연결된 예외 사용
class SpaceException extends Exception {
	SpaceException(String msg) {
    	super(msg);
    }
}

// 둘다 checked일때 try-catch를 선언하지 않으면 둘다 밖으로 던져야 함
static void startInstall() throws SpaceException, MemoryException {
	if(!enoughSpace())
		throw new SpaceException("설치할 공간이 부족합니다.");
        
	if(!enoughMemory())
		throw new MemoryException("메모리가 부족합니다.");
}
    
// MemoryException을 런타임 예외로 변경하여 메서드에 throws를 작성 하지 않아도됨
static void startInstall() throws SpaceException {
	if(!enoughSpace())
		throw new SpaceException("설치할 공간이 부족합니다.");
        
	if(!enoughMemory())
		// MemoryException을 RuntimeException으로 변경하는 방법
		throw new RuntimeException(new MemoryException("메모리가 부족합니다."));    
}

 

** 출처 : 남궁성의 정석코딩_자바의정석_기초편 유튜브 강의