쿠릉쿠릉 쾅쾅
쿠릉쿠릉 쾅쾅
쿠릉쿠릉 쾅쾅
250x250
전체 방문자
오늘
어제
  • 분류 전체보기
    • HTML CSS
    • 잡담
    • 프로그래밍 꿀팁 사이트
    • 코딩 도서
    • [자바]
      • 디자인 패턴
      • 자바의 정석 - 3판
      • 자바
      • 자바 문법
    • git
    • [TDD]
    • 개발 서적 독후감
      • 클린 코더
      • 토비 스프링3
      • 객체지향의 사실과 오해
      • 모던 자바 인 액션
      • 엘레강트 오브젝트
    • CS
      • 운영체제
      • HTTP
    • [SQL]
      • SQL 기초
      • 혼자공부하는SQL
    • [ Spring ]
      • REST API
      • Spring Toy
      • Spring 에러
      • Spring
      • Spring 입문
      • Spring 핵심 원리
      • SpringMVC 1편
      • SpringMVC 2편
      • Spring Boot를 이용한 RESTful We..
      • Batch
    • [JPA]
      • JPA
      • JPA 에러
      • JPA 프로그래밍 - 기본편
      • 스프링 부트와 JPA 활용 1 - 웹 애플리케이..
      • 실전! 스프링 부트와 JPA 활용2 - API 개..
      • 실전! 스프링 데이터 JPA
      • 실전! Querydsl
    • 인텔리제이
    • [DB]
      • DB
      • H2
    • Gradle
    • 면접
    • [알고리즘]
      • 알고리즘
      • 자료구조
      • 자바 알고리즘 공부
    • [프로젝트]
    • 쿠릉식 객체지향 사고
    • 리눅스

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 재귀
  • GitHub
  • 알고리즘
  • 함수형인터페이스
  • SQL
  • springboot
  • MVC
  • 스프링
  • 자바
  • 깃허브
  • http
  • JPA
  • REST API
  • Git
  • querydsl
  • 자료구조
  • 백준
  • java
  • 스프링부트
  • Spring

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
쿠릉쿠릉 쾅쾅

쿠릉쿠릉 쾅쾅

Chapter08 예외처리
[자바]/자바의 정석 - 3판

Chapter08 예외처리

2021. 10. 27. 17:52
728x90

 

예외처리

 

프로그램 오류

  • 프로그램 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료되는 경우에 이런 결과를 초래하는 원인
  • 발생 시점에 따라 '컴파일 에러(compile-time error)'와 '런타임 에러(runtime error)'로 나눔
  • 컴파일 에러는 컴파일 할 때 발생하는 에러
  • 런타임 에러는 프로그램 실행시 발생하는 에러
  • '논리적 에러(logical error)'는 실행은 되지만 의도와 다르게 동작하는 것

 

 

런타임 에러

  • 런타임 에러로 발생할 수 있는 프로그램 오류를 '에러(error)'와 '예외(exception)'으로 나눔
  • 에러는 메모리 부족(OutOfMemoryErro)이나 스택오버 플로우(StackOverflowError)와 같이 일단 발생하면 복구할 수 없는 심각한 오류. 프로그램이 비정상적으로 종료됨
  • 예외는 발생하더라도 비교적 덜 심각한 오류. 코드를 통해 비정상적인 종료를 막을 수 있음

 

 

컴파일러 기능

  • 구문체크, 문법체크
  • 번역
  • 최적화

 

 

예외처리의 정의와 목적

  • 정의 : 프로그램 실행시 발생할 수 있는 예외의 대해서 대비한 코드를 작성 하는 것
  • 목적 : 프로그램의 비정상 종료를 막고 정상적인 실행상태 유지 하는 것

 

 

예외클래스 계층도

  • 모든 오류의 조상은 Throwable 클래스
  • 모든 예외의 최고 조상은 Exception 클래스
  • Exception 클래스와 그 자손들 

 

예외클래스 계층도

 

 

 

 

 

Exception 클래스와 RuntiomeException 클래스 중심의 상속계층도

 

예외 클래스는 2가지로 나눌 수 있음

① Exception클래스와 그 자손들 (RuntimeException과 자손들 제외)  (≒ Exception 클래스들)

 

② RuntimeException와 그 자손들  (≒ RuntimeException 클래스들)

 

 

Exception 클래스들

  • 사용자의 실수, 외적인 요인에 의해 발생되는 예외
  • 예외처리 선택
    • try-catch문 필수
  • Exception 클래스들 종류
    • IOException (Input/Output Exception) : 입출력 예외
    • ClassNotFoundException : 클래스가 존재하지 않을 때 예외
    • FileNotFoundException : 파일이 존재하지 않을 때 예외
    • DataFormatException : 입력한 데이터 형식이 잘못됐을 때 예외

 

 

 

RuntimeException 클래스들

  • 프로그래머의 실수로 발생하는 예외
  • 예외처리 필수
    • try-catch문 선택
  • RuntimeException 클래스들 종류
    • ArithmeticException : 산술계산 예외. 정수를 0으로 나누려고 하는 경우
    • ClassCastException : 형변환 예외
    • NullPointException : 값이 null인 참조변수의 멤버를 호출하려 했을 때 예외
    • IndexOutOfBoundsException : 배열 범위 벗어났을 때 예외

 

 

 

예외처리하기 try ㅡ catch 문

  • 'Exception클래스'는 모든 예외의 최고 조상이므로 'Exception'이 선언된 catch블럭은 모든 예외처리 가능. 그러므로 'Exception'이 선언된 catch블럭은 가장 마지막에 쓸 것
  • 예외를 처리하지 못하면, 프로그램은 비정상적으로 종료되며, 처리되지 못한 예의(uncaught exception)는 JVM의 '예외처리기(UncaughtExceptionHandler)'가 예외의 원인을 화면에 출력

 

 

 

 

 

 

예제1

class prac{
    public static void main(String[] args){
        System.out.println(1);
        try{
            System.out.println(2);
            System.out.println(0/0);
            System.out.println(3);
        } catch (ArithmeticException ae) {
            if (ae instanceof ArithmeticException) {
                System.out.println("true");
            }
            System.out.println("ArithmeticException");
        } catch (Exception e) {
            System.out.println("Exception");
        }  // end of try-catch
        
        System.out.println(4);
    
    }

}


/* 출력값
1
2
true
ArithmeticException
4
*/

 

 

예제2

class prac{
    public static void main(String[] args){
        System.out.println(1);
        
        try{
            System.out.println(args[0]);
            System.out.println(2);
        } catch(ArrayIndexOutOfBoundsException e){
            System.out.println("ArrayIndexOutOfBoundsException");
        }
        
        System.out.println(3);
    }
}

/* 출력값
1
ArrayIndexOutOfBoundsException
3
*/

ArrayIndexOutOfBoundsException은 IndexOutOfBoundsException의 자손 클래스

 

 

  

 

printStackTrace(), getMessage(), e.toString()

  • 예외 발생 원인을 알 수 있는 메소드들
  • 예외가 발생했을 때 예외 클래스의 인스턴스가 생성됨
  • 예외가 발생 했을 때 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 남겨 있음. printStackTrace(), getMessage() 를 통해서 이 정보를 얻을 수 있음
  • catch블랙의 괄호()에 선언된 참조변수를 통해 예외클래스의 인스턴스에 접근할 수 있음. 이 참조변수는 선언된 catch 블럭 내에서만 사용 가능

 

 

■ '예외클래스참조변수명.printStackTrace()'

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

에러의 발생근원지를 찾아서 단계별로 에러 출력

 

 

■ '예외클래스참조변수명.toString()'

에러의 Exception 내용과 원인을 출력

 

 

■ '예외클래스참조변수명.getMessage()'

발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있음

에러의 원인을 간단하게 출력

 

 

 

 

예제1

class Calculator{
    int left, right;
    public void setOperands(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void divide(){
        try {
            System.out.print("계산결과는 ");
            System.out.print(this.left/this.right);
            System.out.print(" 입니다.");
        } catch(Exception e){
            System.out.println("\ne.getMessage()\n"+e.getMessage());
            System.out.println("\ne.toString()\n"+e.toString());
            System.out.println("\ne.printStackTrace()");
            e.printStackTrace();
        }
    }
}

class prac {

    public static void main(String[] args) {
        Calculator c1 = new Calculator();
        c1.setOperands(10, 0);
        c1.divide();
    }
}

/* 출력값
계산결과는 
e.getMessage()
/ by zero

e.toString()
java.lang.ArithmeticException: / by zero

e.printStackTrace()
java.lang.ArithmeticException: / by zero
	at Calculator.divide(prac.java:10)
	at prac.main(prac.java:26)
*/

 

 

 

 

멀티 catch 블록

  • catch 블록을 '|' 기호를 이용해서 하나의 catch 블럭으로 합칠 수 있음
  • 멀티 catch 블록에 사용되는 '|'는 논리연산자가 아니라 기호임
  • 중복 코드를 줄일 수 있음
  • '|' 기호로 연결할 수 있는 예외 클래스의 개수에는 제한이 없음 
  • 예외 클래스를 '|' 기호로 연결할 때 예외 클래스들이 서로 조상과 자손 관계면 안됨
  • '|' 기호로 연결된 예외 클래스의 참조변수는 예외 클래스의 공통된 멤버만 사용할 수 있음
  • 멀티 catch불록에 선언된 참조변수는 상수이므로 값을 변경할 수 없음

 

 

 

 

예외 발생시키기

 

 

 

 

// 예외 발생시키기
Exception e = new Exception("에러 에러");
throw e;


// 위의 2줄을 한줄로 줄일 수 있음
throw new Exception("에러 에러");

 

 

예제1

dd

 

 

 

 

checked 예외, unchecked 예외

  • checked 예외 : 컴파일러가 예외 처리 여부를 체크 (예외 처리 필수)
    • Exception과 그 자손들
    • try-cathc문 필수
  • unchecked 예외 : 컴파일러가 예외 처리 여부 체크 안함 (예외 처리 선택)
    • RuntimeException과 그 자손들
      • try-cathc문 선택

 

checked 예외

class prac {
    public static void main(String[] args) {
        // checked 예외
    	throw new Exception();  // 컴파일러가 에러 표기함
    }
}

/* 출력값
Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	Unhandled exception type Exception

	at prac.main(prac.java:3)
*/

 

 

unchecked 예외

class prac {
    public static void main(String[] args) {
    	// unchecked 예외
        throw new RuntimeException();  // 컴파일러가 에러를 안냄
    }
}

/* 출력 값
Exception in thread "main" java.lang.RuntimeException
	at prac.main(prac.java:3)
*/

 

 

checked 예외와 unchecked 예외 둘 다 프로그램 실행시 에러가 나지만 checked 예외는 프로그램 실행 전 컴파일러가 에러 표시를 하지만 unchecked 예외는 프로그램 실행 전 컴파일러가 에러 표시 안함

 

 

 

 

 

 

 

 

메서드에 예외 선언하기

  • 메서드에 예외를 선언하려면, 메서드의 선언부에 키워드 throws 사용해서 메서드내에서 발생할 수 잇는 예외를 적어주면 됨
  • throws를 사용한다는 것은 해당 메서드에서 예외가 나올 수 있음. 그러나 해당 메서드에 예외처리 하지 않을거니깐 해당 메소드를 호출한 곳에서 예외처리를 할거라는 의미
  • 예외가 여러 개일 경우에는 쉼표(,)로 구분 
  • 메서드 선언부에 예외를 적을 때 적어도 상관없으나 일반적으로 unchecked 예외(RunTimeException 과 그 자손들)는 적지 않음
  • 메서드에 throws를 통해 예외를 적는것은 예외를 처리하는것이 아니라 자신을 호출한 메서드에게 예외를 전달하는 것
  • 예외 처리는 try-catch문으로 처리 해야함

 

 

 

 

 

 

예제1

class prac{
    public static void main(String[] args) throws Exception{
        method1();
    }
    static void method1() throws Exception{
        method2();
    }
    static void method2() throws Exception{
        throw new Exception("에러 에러"); // 예외 발생시킴
    }
}

/*
Exception in thread "main" java.lang.Exception: 에러 에러
	at prac.method2(prac.java:12)
	at prac.method1(prac.java:8)
	at prac.main(prac.java:5)
*/

method1() 호출 → method2() 호출 → method2()에서 예외 발생 → method1로 예외 전달 → method1()에서 try-catch문이 없으므로 예외처리 못하고 프로그램 비정상 종료

 

 

 

예제2

class prac{
    public static void main(String[] args){
        method1();
    }
    static void method1() {
    	try{
    	    throw new Exception("예외");
    	} catch(Exception e){
    	    System.out.println("method1 메서드에서 예외가 처리");
    	    e.printStackTrace();
    	}
    }
}

/*
method1 메서드에서 예외가 처리
java.lang.Exception: 예외
	at prac.method1(prac.java:7)
	at prac.main(prac.java:3)
*/

method1에서 예외가 발생하고 처리함

 

 

 

 

예제3

class prac{
    public static void main(String[] args){
        try{
            method1();
        } catch(Exception e){
           System.out.println("main 메서드에서 예외 처리");
           e.printStackTrace();
        } // end of try-catch
    } // end of main
    
    static void method1() throws Exception{
        throw new Exception("예외 예외");
    }
}

/*
main 메서드에서 예외 처리
java.lang.Exception: 예외 예외
	at prac.method1(prac.java:12)
	at prac.main(prac.java:4)
*/

method1에서 예외가 발생했지만 main메서드에서 예외를 처리함

 

 

 

 

 

 

 

 

예제4

import java.io.*;

class prac{
    public static void main(String[] args){
        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 = "제목없음.text";
        } finally{
            File f = new File(fileName);
            createNewFile(f);
            return f;
        }
    }
    
    static void createNewFile(File f) {
        try{
            f.createNewFile();
        } catch(Exception e){
            e.printStackTrace();
        }
    }

}
  • 실행 시 커맨드라인에 파일이름을 입력하지 않으면, args[0]이 유효하지 않으므로 ArrayIndexOutOfBoundsException 발생
  • createFile 메서드와 createNewFile 메서드에서 예외 처리
  • String[] args 배열에 인자 넣는 방법 : 이클립스 상단 카테고리 → Run → Run Configurations → Arguments → 인자 넣기 → Run 버튼 클릭
  • createFile 메서드에서 fileName 인자 값이 유효하지 않을 때 특정 인자값으로 지정해서 메서드 자체에서 예외 처리 

 

 

 

 

예제5

import java.io.*;

class prac{
    public static void main(String[] args){
        try{
            File f = createFile(args[0]);
            System.out.println(f.getName() + "파일 생성 완료");
        } catch(Exception e) {
        	e.printStackTrace();
        }
    }
    
    static File createFile(String fileName) throws Exception{
        if(fileName == null || fileName.equals(""))
            throw new Exception("파일 이름 유효 x");
        
        File f = new File(fileName);
        f.createNewFile();
        return f;
        
    }
}
  • 실행 시 커맨드라인에 파일이름을 입력하지 않으면, args[0]이 유효하지 않으므로 ArrayIndexOutOfBoundsException 발생
  • main 메서드에서 예외 처리
  • String[] args 배열에 인자 넣는 방법 : 이클립스 상단 카테고리 → Run → Run Configurations → Arguments → 인자 넣기 → Run 버튼 클릭
  • createFile 메서드에서 fileName 인자 값이 유효하지 않을 때 fileName 인자 값을 사용자에게 다시 받아오기 위해서 main 메서드에게 예외를 던짐

 

 

 

 

 

 

finally 블럭

  • finally 블럭은 예외의 발생 여부에 상관없이 실행되어야할 코드를 적음
  • try-catch=finally 순서로 구성되지만 finally 블럭은 선택적으로 사용 (필수사항 아님)
  • 예외 발생시 : try → catch → finally 
  • 예외 미발생시 : try → finally
  • try 또는 catch 블럭에서 return문을 만나도 finally 블럭은 수행됨

 

 

 

 

예제1

class prac{
    public static void main(String[] args){
        method1();
        System.out.println("method1() 수행 끝. 메인 메서드 도착");
    
    }
    
    static void method1() {
        try{
            System.out.println("method1 호출");
            return;
        } catch(Exception e) {
            e.printStackTrace();
        } finally{
            System.out.println("finally 블럭 싱행");
        }
    }
}


/* 출력값
method1 호출
finally 블럭 싱행
method1() 수행 끝. 메인 메서드 도착
*/

method1메서드에서 try블럭에 return문이 실행되어도 finally 블럭은 실행됨

 

 

 

 

 

 

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

  • 입출력(I/O) 관련 클래스를 사용할 때 유용
    • 입출력에 사용되는 클래스는 사용한 후에 꼭 닫아줘야함. 그래야 사용했던 자원(resources)이 반환됨 
  • try 괄호()안에 객체를 생성하는 문장을 넣으면, 이 객체는 따로 close()를 호출하지 않아도 try 블럭을 벗어나는 순간 자동적으로 close()가 호출됨
  • try괄호()안에 선언된 변수는 try 블럭 내에서만 사용 가능 

 

 

 

class prac{
    public static void main(String[] args){
    	try (CloseableResource cr = new CloseableResource()){
    		cr.exceptionWork(false);  // 예외 발생x
    	} catch(WorkException e) {
    		e.printStackTrace();
    	} catch (CloseException e) {
    		e.printStackTrace();
    	}
    	System.out.println();
    	
    	try (CloseableResource cr = new CloseableResource()){
    		cr.exceptionWork(true);  // 예외 발생
    	} catch(WorkException e) {
    		e.printStackTrace();
    	} catch (CloseException e) {
    		e.printStackTrace();
    	} 
    }

}

class CloseableResource implements AutoCloseable{
	public void exceptionWork(boolean exception) throws WorkException{
		System.out.println("exceptionWork(" + exception + ") 호출");
		
		if (exception)
			throw new WorkException("WorkException 발생");
	}
	
	public void close() throws CloseException {
		System.out.println("close() 호출");
		throw new CloseException("CloseException 발생");
	}
}

class WorkException extends Exception{
	WorkException(String msg) {super(msg);}
}

class CloseException extends Exception{
	CloseException(String msg){super(msg);}
}




/*출력값
exceptionWork(false) 호출
close() 호출
CloseException: CloseException 발생
	at CloseableResource.close(prac.java:33)
	at prac.main(prac.java:5)

exceptionWork(true) 호출
close() 호출
WorkException: WorkException 발생
	at CloseableResource.exceptionWork(prac.java:28)
	at prac.main(prac.java:13)
	Suppressed: CloseException: CloseException 발생
		at CloseableResource.close(prac.java:33)
		at prac.main(prac.java:14)
*/
  • main메서드에 두개의 try-catch문 존재 
  • 첫 번째 것은 close()에서만 예외 발생 시킴
  • 두 번째 것은 exceptionWork()와 close()에서 모두 예외 발생
    • exceptionWork()예외가 출력된 후에 '억제된(Suppressed)' 의미의 머리말과 함께 close() 예외가 출력
    • 두 예외가 동시에 발생할 수 없기에 실제 발생한 예외를 WorkException으로 하고 CloseException은 억제된 예외로 다룸
    • 억제된 예외에 대한 정보는 실제로 발생한 예외인 WorkException에 저장
    • 만약 기존의 try-catch문을 사용했다면 먼저 발생한 WorkException은 무시되고, 마지막에 발생한 CloseException에 대한 내용만 출력

 

 

Throwable의 억제된 예외 메서드 종류

void addSuppressed(Throwable exception)  // 억제된 예외 추가



Throwable[] getSuppressed()   // 억제된 예외(배열)를 반환

 

 

 

 

 

 

 

 

사용자정의 예외 만들기

  • 보통 Exception 클래스 또는 RuntimeException 클래스로부터 상속 받아 사용자정의 예외 클래스를 만듦
  • 가능한 새로운 예외 클래스를 만들기보단 기존의 예외 클래스 활용할 것
  • 예전에는 주로 Exception을 상속받아서 checked예외로 작성하는 경우가 많았지만, 요즘은 예외처리를 선택적으로 할 수 있도록 RuntimeException을 상속받아 작성하는 쪽을 권장
  • checked예외 클래스로 상속 받으면 예외처리가 불필요한 경우에도 try-catch문을 넣어서 코드가 복잡해짐

 

 

 

예제1

class prac{
    public static void main(String[] args){
        try{
            startInstall();  // 프로그램 설치 준비
            copyFiles();  // 파일 복사
        } catch(SpaceException se) {
            System.out.println("에러 메세지: " + se.getMessage());
            se.printStackTrace();
            System.out.println("공간 확보 후 다시 설치할 것");
        } catch(MemoryException me) {
            System.out.println("에러 메시지:" + me.getMessage());
            me.printStackTrace();
            System.gc();  // Garbage Collecetion을 수행하여 메모리를 늘림
            System.out.println("다시 설치할 것");
        } finally{
            deleteTempFiles();  // 프로그램 설치에 사용된 임시파일 삭제
        }
    }
    
    static void startInstall() throws SpaceException, MemoryException{
        if(!enoughSpace())
            throw new SpaceException("설치 공간 부족");
        if(!enoughMemory())
            throw new MemoryException("메모리 부족");
    }
    
    static void copyFiles() {/* 파일 복사하는 코드 */}
    static void deleteTempFiles() {/* 임시파일 삭제 코드 */}
    
    static boolean enoughSpace(){
        return false;
    }
    
    static boolean enoughMemory(){
        return true;
    }
}

class SpaceException extends Exception{
    SpaceException(String msg){
        super(msg);
    }
}

class MemoryException extends Exception{
    MemoryException(String msg){
        super(msg);
    }
}


/* 출력값
에러 메세지: 설치 공간 부족
SpaceException: 설치 공간 부족
	at prac.startInstall(prac.java:22)
	at prac.main(prac.java:4)
공간 확보 후 다시 설치할 것
*/

 

 

 

 

 

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

  • 한 메서드에서 발생할 수 있는 예외가 여럿인 경우, 몇 개는 try-catch문을 통해서 메서드 내에서 자체적으로 처리하고, 그 나머지는 선언부에 지정하여 호출한 메서드에서 처리함으로써, 양쪽에서 나눠서 처리할 수 있음
  • 예외 되던지기란, 예외를 처리한 후에 인위적으로 다시 예외를 발생시키는 방법
  • 먼저 예외가 발생할 가능성이 있는 메서드에서 try-catch문을 사용해서 예외를 처리해주고 catch문에서 필요한 작업을 행한 후 throw문을 사용해서 예외를 다시 발생시킴. 다시 발생한 예외는 이 메서드를 호출한 메서드에게 전달되고 호출한 메서드의 try-catch문에서 예외를 또 다시 처리

 

 

예제1

class prac{
    public static void main(String[] args){
    	try {
    		method1();
    	} catch (Exception e) {
    		System.out.println("main 메서드에서 예외 처리");
    	}
    	
    }
    static void method1() throws Exception{
    	try {
    		throw new Exception();
    	} catch(Exception e) {
    		System.out.println("method1 메서드에서 예외 처리");
    		throw e;  // 다시 예외 발생
    	}
    }

}

/* 출력값
method1 메서드에서 예외 처리
main 메서드에서 예외 처리
*/

 

 

 

예제2

class prac{
    public static void main(String[] args){
    	method1();
    }
    
    static int method1() {
    	try {
    		System.out.println("method1() 호출");
    		return 1;  // 실행중인 메서드 종료
    	} catch(Exception e) {
    		e.printStackTrace();
    		return 0;  // catch문에도 return문 필요
    	} finally {
    		System.out.println("method1()의 finally 블럭 실행");
    	}
    }
}


/* 출력값
method1() 호출
method1()의 finally 블럭 실행
*/
  • try-catch문에서 try 블럭안에 return문이 있을경우, catch문에도 return문이 있어야함
  • try 블럭안에 return문이 있어도 catch블럭에서 예외 던지기를 해서 호출한 메서드로 예외를 전달한다면, catch문에 return문 필요 없음
  • finally 블럭에서 return문 사용 가능
    • try블럭이나 catch 블럭의 return문 다음에 수행되고 최종적으로 finally 블럭의 return문의 값이 반환됨 

 

 

 

 

연결된 예외(chained exception)

  • 연결된 예외란, 어떤 예외를 다른 예외로 감싸는 것
  • 한 예외가 다른 예외 유발 가능. 예외 A가 예외 B를 발생시켰다면, A는 B의 '원인 예외(cause exception)'
    • 원인 예외 A를 예외 B에 포함 시킴 
    • ex) '예외B.initCause(예외A)'  

 

 

사용 용도

  • 여러 예외를 하나로 묶어서 다루기 위해
    • 만약 조상으로 묶어버리면 실제로 발생한 예외가 어떤것이 알 수 없는 문제가 생김
    • 예외가 원인 예외를 포함할 수 있게하면 두 예외는 상속 관계가 아니여도 됨
    • catch 블럭을 너무 많이 사용하면 가독성이 떨어지기 때문에 여러 예외를 하나로 묶음
  • checked 예외를 unchecked 예외로 바꾸기 위해
    • 의미없는 try-catch문을 사용안해도 됨

 

 

 

연결 예외 메소드

Throwable initCause(Throwable cause)  // 지정한 예외를 원인 예외로 등록


Throwable getCause()  // 원인 예외 반환


RunTimeException(Throwable cause)  // 원인 예외를 등록하는 생성자

 

 

 

 

 

여러 예외를 하나로 묶어서 다룰 때

try{
    startInstall();
    copyFiles();
} catch(SpaceException se){
    InstallException ie  = new InstallException("설치중 예외발생");
    ie.initCause(se);
    throw ie;
} catch(MemoryException me) {
    InstallException ie  = new InstallException("설치중 예외발생");
    ie.initCause(me);
    throw ie;
}

SpaceException 예외와 MemoryException 예외가 원인 예외로서 InstallException 예외에 포함됨

 

 

 

 

 

checked 예외를 unchecked 예외로 바꾸기 위해

static void startInstall() throw SpaceException {
    if(!enoughSpace())
        throw new SpaceException("설치할 공간 부족");
    
    if(!enoughMemory())
        throw new RuntimeException(new MemoryException("메모리 부족"));
}

사용자 정의 예외 만들기에서 예제1번에 MemoryException은 Exception 자손임. 그래서 반드시 예외 처리를 해야함. 근데 이 예외를 RuntimException으로 감싸버려서 unchecked 예외가 됨. 그래서 startInstall() 메서드 선언부의 throw에 MemoryException을 안적어도 됨. 참고로 여기서는 initCause() 대신 RuntimeException의 생성자를 사용

 

 

 

 

 

 

예제1

class prac{
    public static void main(String[] args){
        try{
            install();
        } catch(InstallException e){
            e.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    static void install() throws InstallException {
        try{
            startInstall();
            copyFiles();
        } catch(SpaceException se){
            InstallException ie = new InstallException("설치 중 예외발생");
            ie.initCause(se);
            throw ie;
        }
    }
    
    static void startInstall() throws SpaceException{
        if(!enoughSpace()){
            throw new SpaceException("설치할 공간 부족");
        }
        if(!enoughMemory()) {
            throw new RuntimeException(new MemoryException("메모리 부족"));
        }
    }
    
    static boolean enoughSpace(){
        return false;
    }
    
    static boolean enoughMemory(){
        return true;
    }
    
    static void copyFiles(){/* 파일 복사 코드 */}
    static void deleteTempFiles(){/* 임시 파일 삭제 코드 */}
}

class SpaceException extends Exception{
    SpaceException(String msg) {super(msg);}
}

class MemoryException extends Exception {
    MemoryException(String msg) {super(msg);}
}

class InstallException extends Exception{
    InstallException(String msg) {super(msg);}
}



/* 출력값
InstallException: 설치 중 예외발생
	at prac.install(prac.java:17)
	at prac.main(prac.java:4)
Caused by: SpaceException: 설치할 공간 부족
	at prac.startInstall(prac.java:25)
	at prac.install(prac.java:14)
	... 1 more
*/

출력값에 발생한 예외는 InstallException이 뜨지만 세부정보로 원인예외 SpaceException이 나옴

728x90

'[자바] > 자바의 정석 - 3판' 카테고리의 다른 글

Chapter 10 날짜와 시간 & 형식화  (0) 2021.11.08
Chapter 09 java.lang 패키지와 유용한 클래스  (0) 2021.10.30
Chapter07 객체지향2  (0) 2021.10.17
Chapter06. 객체지향  (0) 2021.09.25
Chapter.05 배열  (0) 2021.08.29
    '[자바]/자바의 정석 - 3판' 카테고리의 다른 글
    • Chapter 10 날짜와 시간 & 형식화
    • Chapter 09 java.lang 패키지와 유용한 클래스
    • Chapter07 객체지향2
    • Chapter06. 객체지향
    쿠릉쿠릉 쾅쾅
    쿠릉쿠릉 쾅쾅
    깃허브 주소 : https://github.com/kureung

    티스토리툴바