JAVA/예외처리
-
Test157~158 예외(Exception) 처리 - (4) 예외 다시 던지기2020.09.22
Test157~158 예외(Exception) 처리 - (4) 예외 다시 던지기
다음은 예외를 발생(throw)시키고 하고 던지기(throw) 하고 잡아(catch) 처리하는 코드이다.
예외 처리의 흐름을 잘 파악 해보자.
// 예외 다시 던지기 public class Test157 { public static void main(String[] args) { Test157 ob = new Test157(); try { int a = ob.getValue(-2); System.out.println("a : " + a); } catch (Exception e) { System.out.println("ⓑ printStackTrace..........."); e.printStackTrace(); } } public int getData(int data) throws Exception { if (data < 0) throw new Exception("data 가 0 보다 작습니다."); // ① 예외 발생 return data + 10; } // 잡아낸 예외 다시 던지는 것이 가능하도록 처리 public int getValue(int value) throws Exception { int a = 0; try { a = getData(-1); // ② 예외 발생 } catch (Exception e) // ③ 예외 잡아내기 { // ④ 잡아낸 예외에 대한 처리 System.out.println("ⓐ printStackTrace............"); e.printStackTrace(); // ⑤ 잡아낸 예외 다시 던지기 check~!!! throw e; //-- 주석 처리 시(즉, 잡아낸 예외를 다시 던지지 않을 경우) // getValue() 메소드를 호출하고 있는 main() 메소드의 // 예외 처리 구문은 수행되지 않게 된다. } return a; } }
실행 결과
// 실행 결과 /* ⓐ printStackTrace............ java.lang.Exception: data 가 0 보다 작습니다. at Test157.getData(Test157.java:30) at Test157.getValue(Test157.java:42) at Test157.main(Test157.java:15) ⓑ printStackTrace........... java.lang.Exception: data 가 0 보다 작습니다. at Test157.getData(Test157.java:30) at Test157.getValue(Test157.java:42) at Test157.main(Test157.java:15) 계속하려면 아무 키나 누르십시오 . . . */
// 다른 예외 다시 던지기 public class Test158 { public int getValue(int value) throws Exception // ⑦ 예외 던지기 { // (파란 폭탄) int a = 0; try { a = getData(-2); // ③ 예외 발생 } // (빨간 폭탄) catch (Exception e) // ④ 예외 잡아내기 { // (빨간 폭탄) // ⑤ 예외 처리(빨간 폭탄) System.out.println("printStactTrace............."); e.printStackTrace(); throw new Exception("value 가 음수입니다"); // ⑥ 새로운 예외 발생 } // (파란 폭탄) return a; } public int getData(int data) throws Exception // ② 예외 던지기 { // (빨간 폭탄) if (data<0) throw new Exception("data가 0 보다 작습니다."); // ① 예외 발생 // (빨간 폭탄) return data + 10; } public static void main(String[] args) { Test158 ob = new Test158(); try { int a = ob.getValue(-2); // ⑧ 예외 발생 System.out.println("a : " + a); // (파란 폭탄) } catch (Exception e) // ⑨ 예외 잡아내기 { // (파란 폭탄) System.out.println("printStackTrace............"); e.printStackTrace(); } } }
실행 결과
// 실행 결과 /* printStactTrace............. java.lang.Exception: data가 0 보다 작습니다. at Test158.getData(Test158.java:33) at Test158.getValue(Test158.java:15) at Test158.main(Test158.java:44) printStackTrace............ java.lang.Exception: value 가 음수입니다 at Test158.getValue(Test158.java:23) at Test158.main(Test158.java:44) 계속하려면 아무 키나 누르십시오 . . . */
'JAVA > 예외처리' 카테고리의 다른 글
Test155~156 예외(Exception) 처리 - (3) throw(사용자 정의 예외) (0) | 2020.09.22 |
---|---|
Test153~154 예외(Exception) 처리 - (2) throws(예외 떠넘기기) (0) | 2020.09.21 |
Test152 예외(Exception) 처리 - (1) 개요, try~catch 구문 (0) | 2020.09.21 |
Test155~156 예외(Exception) 처리 - (3) throw(사용자 정의 예외)
throw
[ 사용자 정의 예외 객체 ]
- 기본 라이브러리(API)에서 제공하는 예외 객체 외 개발자가 선언해서 사용하는 예외 객체
- Exception클래스 또는 Exception의 자식 클래스를 반드시 상속받아야 함
- 일반적으로 Exception에서 선언된 형태의 생성자 외에는 다른 멤버를 가지지 않음
즉 Exception을 상속받는 예외 객체를 직접 만들고, 조건문을 사용해서 특정 상황에서 예외를 강제로 발생하도록 만든다. 사실 이 경우 조건문이기 때문에 굳이 예외 객체를 생성하지 않더라도 처리하고 싶은 예외 상황을 조건문으로 처리하거나, 예외 객체 대신 그냥 메소드로 생성해서 사용할 수도 있다. 다만 명시적인 예외 객체를 사용하므로써 코드의 가시성을 높일 수 있다.
아래는 일반 메소드로 예외 처리를 했을 경우입니다. 그냥 예외 상황에 대한 처리 구문을 메소드로 만들어 소환하면 된다.
// Test156.java 파일과 비교~!!! class Demo { private int value; public void setValue(int value) { if(value<=0) return; //-- 종료 → setValue() 메소드 종료 this.value = value; } public int getValue() { return value; } } public class Test155 { public static void main(String[] args) { Demo ob = new Demo(); ob.setValue(-3); int result = ob.getValue(); System.out.println(result); } }
아래는 예외 객체를 생성해서 위와 같은 예외 상황을 처리한 예다. 강제로 예외를 발생시킬 때는 "throw"를 사용한다. 이전글에서 예외를 호출한 쪽으로 던져주는 "throws"와 한글자가 다른 점에 유의해야 한다. 사용자 정의 예외 객체의 사용은 각자의 설계와 스타일에 맞게 사용하면 된다.
// Test155.java 파일과 비교~!!! // ※ throw class Demo { private int value; public void setValue(int value) throws Exception { if (value<=0) { // 예외 발생 throw new Exception("value 는 0보다 작거나 같을 수 없습니다."); } this.value = value; } public int getValue() { return value; } } public class Test156 { public static void main(String[] args)// throws Exception { Demo ob = new Demo(); try { ob.setValue(-3); int result = ob.getValue(); System.out.println(result); } catch (Exception e) { System.out.println("main() 에서 잡아낸 예외"); System.out.println(e.toString()); } } }
'JAVA > 예외처리' 카테고리의 다른 글
Test157~158 예외(Exception) 처리 - (4) 예외 다시 던지기 (0) | 2020.09.22 |
---|---|
Test153~154 예외(Exception) 처리 - (2) throws(예외 떠넘기기) (0) | 2020.09.21 |
Test152 예외(Exception) 처리 - (1) 개요, try~catch 구문 (0) | 2020.09.21 |
Test153~154 예외(Exception) 처리 - (2) throws(예외 떠넘기기)
throws (예외 떠넘기기)
메서드 내부에서 예외가 발생했을 때 예외를 try - catch 문으로 잡아서 처리할 수 있지만 경우에 따라서 현재 메서드를 호출한 메서드로 예외를 떠넘길 수 있다. 예외를 떠넘기는 방법은 다음과 같이 throws 키워드를 메서드 뒤에 붙여주면 된다.
public static void generateException() throws NullPointerException { //NullPointerException 발생 }
만약 떠넘겨야할 예외 종류가 여러개라면 쉼표(,)를 기준으로 나열하여 선언할 수 있다.
public static void generateException() throws NullPointerException, ArithmeticException { //예외 발생 }
예외가 발생하는 경우 try - catch문을 통해 처리하지 않고 throws를 이용해 떠넘기면 현재 메서드를 호출한 곳으로 던져지게 된다. 만약 모든 메서드에서 throws 를 이용해 예외를 떠넘기다 보면 최초 호출 지점인 main() 메서드 내부로 예외가 던져지게 되며 main() 메서드에서 마저 예외를 떠넘기게 된다면 JVM의 예외처리기까지 도달하여 프로그램은 그대로 종료된다.
이렇게 되면 사실상 예외를 처리하지 않은것이나 다름 없으므로 매우 무의미한 행동이라 할 수 있다. 의도적인 경우가 아니라면 throws는 많은 생각과 필요에 의해 사용되어야 한다.
예외를 떠넘기는 이유
예외가 발생한 경우 굳이 메서드 내에서 try-catch 문으로 예외를 처리하지 않고 throws문으로 떠넘기는 이유는 무엇일까?
첫번째 이유는 메서드 선언부에 선언된 throws문을 통해 해당 API를 사용했을 때 어떤 예외가 발생할 수 있는지를 예측할 수 있다.
다음은 java api 공식 문서이다. 선언부를 보면 IOException이 던져진다는 것을 알 수 있다.
두번째 이유로는 현재 메서드 내에서 예외를 처리할 필요가 없다고 판단했을 경우이다. 예외 처리에는 생각보다 많은 코드가 필요하게 되며 이는 코드를 읽기 어렵게 만들고 불필요한 코드가 많이 추가되게 만들어 버그를 만들기 쉽다. 또한 API를 만드는데에 있어서 내가 처리하기 보다는 내가 만든 API를 사용하는 다른 개발자에게 원하는 처리를 하도록 기회를 줄 수 있다.
다음 코드는 BufferedReader 클래스의 readLine메소드로 인해 발생하는 예외(Checked Exception)를 throws 처리한 것이다. main에서 호출한 메소드에서 IOException을 throws 하여 main메소드로 던져진 IOException을 다시 throws하는 코드이다.
import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; public class Test153 { private String[] data = new String[3]; public void proc() throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str; int n = 0; System.out.print("이름 입력[종료:Ctrl+z] : "); while ((str = br.readLine()) != null ) { data[n++] = str; System.out.print("이름 입력[종료:Ctrl+z] : "); } System.out.println("입력된 내용..."); for (String s : data) { if (s != null) { System.out.println(s); } } } public static void main(String[] args) throws IOException // check~!!! { Test153 ob = new Test153(); ob.proc(); } }
Unchecked Exception은 명시적인 예외처리를 하지 않아도 된다. 이 예외는 피할 수 있지만 개발자가 부주의해서 발생하는 경우가 대부분이며, 미리 예측하지 못했던 상황에서 발생하는 예외가 아니기 때문에 굳이 로직으로 처리를 할 필요가 없도록 만들어져 있다.
아래 코드는 위 코드에서 발생할 수 있는 Unchecked Exception에대한 처리를 try ~ catch로 처리하였다.
import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; public class Test154 { private String[] data = new String[3]; public void proc() { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str; int n = 0; try { System.out.print("이름 입력[종료:Ctrl+z] : "); while ((str = br.readLine()) != null ) { data[n++] = str; System.out.print("이름 입력[종료:Ctrl+z] : "); } } catch(ArrayIndexOutOfBoundsException e) { System.out.println("======[예외 발생]======"); System.out.println("getMessage : " + e.getMessage()); System.out.println("toString : " + e.toString()); System.out.println("printStackTrace............"); e.printStackTrace(); } catch (IOException e) { System.out.println(e.toString()); } System.out.println("\n입력된 내용..."); for (String s : data) { if(s != null) System.out.println(s); } } public static void main(String[] args) { Test154 ob = new Test154(); ob.proc(); } }
실행 결과
// 실행 결과 /* 이름 입력[종료:Ctrl+z] : 조윤상 이름 입력[종료:Ctrl+z] : 김철수 이름 입력[종료:Ctrl+z] : 배철수 이름 입력[종료:Ctrl+z] : ^Z 입력된 내용... 조윤상 김철수 배철수 계속하려면 아무 키나 누르십시오 . . . */
런타임 중 예외 발생시
입력받을 문자열 배열의 크기가 초과하는 예외 (ArrayIndexOutOfBoundsException) 즉, (Unchecked Exception) 이 발생하였을 때 catch 블록에 정의된 대로 실행되었다.
'JAVA > 예외처리' 카테고리의 다른 글
Test157~158 예외(Exception) 처리 - (4) 예외 다시 던지기 (0) | 2020.09.22 |
---|---|
Test155~156 예외(Exception) 처리 - (3) throw(사용자 정의 예외) (0) | 2020.09.22 |
Test152 예외(Exception) 처리 - (1) 개요, try~catch 구문 (0) | 2020.09.21 |
Test152 예외(Exception) 처리 - (1) 개요, try~catch 구문
예외란?
프로그램에서 발생하는 오류(Error)는 아래와 같이 두가지로 볼 수 있다.
① 잘못된 문법을 사용하거나 변수 등을 정의하지 않은 상태에서 사용함으로써 컴파일 단계에서 발생하는 문법적인 오류(Error) → 컴파일 에러
② 프로그램을 실행하는 과정에서 발생되는 오류 → 런타임 에러
- 개발자가 문제 분석을 잘못하거나 실수에 의해 엉뚱한 결과를 가져오게 되는 논리적인 오류(Error)
- 시스템 이상에서 발생되는 시스템 오류(Error)
- 프로그램 실행 중 발생되는 비정상적인 상황을 의미하는 예외사항(Exception)이 있다.
ex)
·어떤 수를 0으로 나누거나...
·배열 첨자를 벗어나는 상황이 발생하거나...
·존재하지 않는 파일을 오픈하여 읽어들인다거나...
Exception 클래스는 이런 예외 사항이 발생할 경우를 미리 예측하여 적절히 대응하기 위한 절차를 구현하도록 정리해 놓은 것이다.
- 예외는 프로그램 실행 중에 발생할 수 있는 명령어의 정상적인 흐름을 방해하는 이벤트로 자바에서 예외는 하나의 오브젝트(Object, 객체)이다.
- 프로그램 실행 중에 메소드 안에서 오류(Error)가 발생하게 될 경우, 메소드는 그 오류에 해당하는 예외 오브젝트를 만들고 그것을 자바 런타임 시스템(Runtime System)에 전달해 준다.
- 자바에서 모든 예외 클래스는 Throwable 클래스나 Throwable 클래스의 하위 클래스를 상속받아 사용한다.
- Throwable 클래스는 예외를 설명하는 문장이나 예외가 발생할 때의 프로그램의 상태에 관한 정보를 포함하고 있다.
- Throwable 클래스에서 파생된 클래스
·Exception 클래스
Exception 예외 클래스는 일반적으로 프로그래머에 의해 복원될 수 있는 예외 상황으로 메소드가 실행 중에 던지는 예외를 가리킨다.
·Error 클래스
심각한 예외의 형태로 개발자가 복원할 수 없는 형태의 예외이다.
※ 예외의 종류
- checked exception
메소드 내에서 예외가 발생한 경우 메소드를 정의할 때 『throws』문에 메소드 내에서 발생할 수 있는 예외들을 명시해 주거나 또는 그 예외를 『try~chtch』에서 처리해 주어야만 하는 예외이다. 컴파일러가 컴파일을 수행하는 과정에서 『checked exception』이 『throws』 되는가의 여부 혹은 『try~catch』되는지의 여부를 판단하여 프로그램에서 예외를 어떤 형식으로든 처리하지 않으면 컴파일 자체가 불가능하다.
- unchecked exception
사전에 처리하지 않아도 컴파일러가 체크하지 않는 런타임 시에 발생할 수 있는 예외이다.
○ java.lang.Throwable 클래스의 주요 메소드
- String toString()
: Throwable 각각에 대한 설명을 문자열 형태로 반환한다.
- void printStackTrace(PrintStream s)
- void printStackTrace(PrintWriter w)
: 표준 출력 스트림에 스택 호출 목록을 마지막 메소드부터 출력한다.
○ 주요 런타임 예외 클래스
- Arithmeticexception
: 수치 계산상의 오류(0으로 나누기 등)
- ArrayStoreException
: 배열에 잘못된 데이터 형을 저장하려 했을 경우 발생하는 오류
- IndexOutOfBoundException
: 배열, 문자열, 벡터 등에서 인덱스 범위가 벗어난 경우 발생하는 오류
- ClassCastException
: 클래스 변환을 잘못한 경우 발생하는 오류
- NullPointerException
: 빈 객체를 참조하는 경우(초기화 되지 않은 변수 사용 등) 발생하는 오류
- SecurityException
: 자바의 내부 보안 사항을 위반하였을 경우 발생하는 오류
:
:
예외처리의 몇가지 예제인 try ~ catch 구문과 throws 에대해 알아보자.
try ~ catch ~ (finally)
try블록에는 예외가 발생할 수 있는 코드가 위치한다. try 블록의 코드가 예외없이 정상 실행되면 catch블록의 코드는 실행되지 않고 finally 블록의 코드를 실행한다. 하지만 try 블록의 코드에서 예외가 발생하면 즉시 실행을 멈추고 catch 블록으로 이동하여 예외처리 코드를 실행한다. 그리고 finally 블록의 코드를 실행한다. (finally 블록은 생략 가능합니다.)
try catch 문을 주로 쓰는 구간은 주로 데이터베이스에 데이터를 주고받을 경우 많이 사용한다. 데이터베이스를 거쳐올때는 변수가 많이 생기기 때문에 try catch문은 필수이다. 그리고 finally에는 데이터베이스와의 연결을 끊어주는 코드를 주로 삽입된다. 특정 예외가 발생하여 데이터베이스와의 연결이 끊어지지 않으면 여러가지 문제를 야기할수 있기 때문이다.
// BufferedReader 클래스 인스턴스 생성 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 주요 변수 선언 int a, b, c; System.out.print("첫 번째 정수 입력 : "); a = Integer.parseInt(br.readLine()); System.out.print("두 번째 정수 입력 : "); b = Integer.parseInt(br.readLine()); c = a + b; System.out.println("결과 : " + c);
BufferedReader 인스턴스로 입력을 받게되면 컴파일시 아래와 같은 예외사항이 발생하게 된다.
이는 IOException이 잡아내지거나 던져져야 함을 의미한다.
try ~ catch 구문을 활용하여 예외처리를 하면 아래와 같다.
// BufferedReader 클래스 인스턴스 생성
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 주요 변수 선언
int a, b, c;
try
{
System.out.print("첫 번째 정수 입력 : ");
a = Integer.parseInt(br.readLine());
System.out.print("두 번째 정수 입력 : ");
b = Integer.parseInt(br.readLine());
c = a + b;
System.out.println("결과 : " + c);
}
catch (IOException e)
{
// IOException → checked exception
//-- 메소드를 정의하는 과정에서 throws 한 예외.
// 잡아내거나 던지지 않을 경우 컴파일 에러
System.out.println(e.toString());
}
실행 결과
// 실행 결과 /* 첫 번째 정수 입력 : 2 두 번째 정수 입력 : 5 결과 : 7 계속하려면 아무 키나 누르십시오 . . . */
IOException은 checked exception이라 필수적으로 처리를 해야한다. 하지만 위 코드에서는 컴파일시에는 정상적으로 처리되나 런타임 중 unchecked exception인 NumberFormatException이 경우에 따라 발생할 가능성이 있다.
런타임 중 예외 발생시
try { System.out.print("첫 번째 정수 입력 : "); a = Integer.parseInt(br.readLine()); System.out.print("두 번째 정수 입력 : "); b = Integer.parseInt(br.readLine()); c = a + b; System.out.println("결과 : " + c); } catch (IOException e) { // IOException → checked exception //-- 메소드를 정의하는 과정에서 throws 한 예외. // 잡아내거나 던지지 않을 경우 컴파일 에러 System.out.println(e.toString()); } catch (NumberFormatException e2) { // NumberFormatException → unchecked exception //-- 런타임 시 발생할 수 있는 예외로 // 문법적으로 반드시 던질 필요도, 잡아낼 필요도 없다. System.out.println(e2.toString()); System.out.println("숫자 형태의 데이터를 입력하세요~!!!"); }
unchecked exception인 NumberFormatException까지 예외처리가 된 코드이다.
실행 결과
런타임 중 예외 발생시
런타임중 숫자 형태 이외의 데이터를 입력하여 unchecked exception 발생하였음에도 사용자가 의도한대로 처리되고, 비정상적으로 종료가 일어나지 않았다.
예외가 발생할 여지가 있으며 어떠한 에러인지 확신이 없을때, 다음과 같이 특정 예외를 catch문에 선언하지않고 Exception을 선언하면 Exception 클래스에 정의되어 있는 모든 예외를 잡아낼 수 있다.
이는 Exception 클래스는 UncheckedException 과 CheckedException의 상위 클래스이기 때문이다.
try { System.out.print("첫 번째 정수 입력 : "); a = Integer.parseInt(br.readLine()); System.out.print("두 번째 정수 입력 : "); b = Integer.parseInt(br.readLine()); c = a + b; System.out.println("결과 : " + c); } catch (Exception e) { System.out.println("[e.toString()]================="); System.out.println(e.toString()); System.out.println("[e.getMessage()]==============="); System.out.println(e.getMessage()); System.out.println("[printStackTrace]=============="); e.printStackTrace(); }
실행 결과
// 실행 결과 /* 첫 번째 정수 입력 : 3 두 번째 정수 입력 : 6 결과 : 9 계속하려면 아무 키나 누르십시오 . . . */
런타임 중 예외 발생시
마찬가지로 try ~ catch ~ finally 구문을 이용하여 처리할 수 있다.
finally 영역은 예외가 발생하거나 발생하지 않거나 언제나 실행되는 영역이다.
try { System.out.print("첫 번째 정수 입력 : "); a = Integer.parseInt(br.readLine()); System.out.print("두 번째 정수 입력 : "); b = Integer.parseInt(br.readLine()); c = a + b; System.out.println("결과 : " + c); } catch (IOException e) { System.out.println(e.toString()); } finally { System.out.println("고생많으셨습니다. 감사합니다"); }
실행 결과
// 실행 결과 /* 첫 번째 정수 입력 : 5 두 번째 정수 입력 : 2 결과 : 7 고생많으셨습니다. 감사합니다 계속하려면 아무 키나 누르십시오 . . . */
'JAVA > 예외처리' 카테고리의 다른 글
Test157~158 예외(Exception) 처리 - (4) 예외 다시 던지기 (0) | 2020.09.22 |
---|---|
Test155~156 예외(Exception) 처리 - (3) throw(사용자 정의 예외) (0) | 2020.09.22 |
Test153~154 예외(Exception) 처리 - (2) throws(예외 떠넘기기) (0) | 2020.09.21 |