예외처리 이충기 명지대학교컴퓨터공학과
예외필요성 오류처리지금까지 : 오류처리를거의하지않았다. 일이의도한대로잘될것이라고가정했다. 주먹구구식방법 : 프로그래머는컴파일오류를찾아서수정하기위해서시험하고오류수정을해야한다. 컴파일러는실행오류의문제를해결하지못한다 ( 예 : 부정확한값이나상태 ). 프로그래머는실행오류가있더라도프로그램이우아하게돌아가야한다는것을보장해야한다. 우아하게 라는의미는프로그램이단지잘못된결과를낳지않거나망가지지않는다는것을의미한다! 2
예외 전통적방법 전통적인절차적프로그래밍언어에서실행오류를다루는여러가지방법들이있었다. 한방법은메소드가성공적으로끝났는지를나타내는어떤종류의값을되돌려주는것이다. 예를들면 : public boolean somemethod( ) { // 이메소드가오류없이실행되면 true 를반환 // 그렇지않으면 false 를반환 주 : 반환값이없는메소드와반환값을가진메소드의논리적차이를불명료하게만든다... 좋지않은공학적습관! 3
예외 전통적방법 오류처리의전통적방법은빠르게추해지고복잡해지고다룰수없어질수있다. 예를들면, 우리가다음과같이세개의메소드를호출하고나서문들을수행하려고한다면, somemethod( ); someothermethod( ); somethirdmethod( ); /* 몇개의작업들을수행한다 */ 우리는아래와같은코드를가질수있다 : 4
예외 전통적방법 result1 = somemethod( ) if (result1 == true) { result2 = someothermethod( ) if (result2 == true) { result3 = somethirdmethod( ) if (result3 == true) { // 오류들을만나지않았다 ; 예정된작업들을한다 else { // somethirdmethod( ) 에의해발생된오류를처리한다 else { // someothermethod( ) 에의해발생된오류를처리한다 else { // somemethod( ) 에의해발생된오류를처리한다 5
예외 전역변수 오류처리의다른방법은전역 (global) 변수의값이오류를나타내게하는것이다. int ErrorValue = 0; public void somemethod( ) { // 수행할일을처리 // 에러가발생하면 ErrorValue = 1 을지정 public void someothermethod( ) { // 수행할일을처리 // 에러가발생하면 ErrorValue = 2 을지정 public void somelastmethod( ) { // 수행할일을처리 // 에러가발생하면 ErrorValue = 3 을지정 6
예외 전역변수 public void doit() { somemethod(); someothermethod(); somelastmethod(); if (ErrorValue == 1) if (ErrorValue == 2) if (ErrorValue == 3) 그러나 : 실행오류가프로그램이더이상실행되지못하게한다면? 예를들면 : somemethod( ) 가 someothermethod( ) 로갈수없을정도로잘못된다면? 이런문제를해결하기위해, 우리는여러개의중첩된 if 문을사용했던앞의예와같이복잡한코드를갖게된다 : 계속 7
예외 전역변수 public void doit( ) { somemethod( ); if (ErrorValue == 1) { // if else { someothermethod( ); if (ErrorValue == 2) { // if else { somethirdmethod( ); if (ErrorValue == 3) { // if else { // 예정된작업들을수행한다 // else // else ( 다른중괄호들을생략한다 ) 주 : 이기법에서우리는전프로그램을일련의 if/else 절로둘러싸야한다. 곳곳에중복된코드를갖게된다. 8
예외 전통적인접근법 지금까지보듯이간단한경우에대해서도매우빠르게복잡해진다. 역사적으로프로그래머들이이문제를해결한방법은오류처리를전혀하지않는것이었다! 대신에, 그들은일반적으로다음두가지의접근법중하나를사용했다 : 오류를탐지하자마자프로그램을종료한다. 즉, 오류를인식하나처리하지는않고단순히끝낸다. 혹은 오류를탐지하려고시도조차하지않는다 ; 즉프로그램이제멋대로예측할수없도록실행되게내버려둔다 ( 시스템중단? 잘못된값? 잘못된동작?) 이러한접근법은구조적프로그래밍의기본주의를위반한다 : 메소드로부터의출구는하나다. 9
예외 진정한문제 전통적인접근법은프로그래머가실제문제를단순히무시하는경우이다 : 실행오류가한메소드내에일어날때, 어떤손상을입히지않고메소드를어떻게종료할수있을까? 프로그램이시스템의작동을멈추게하지않거나좋지않은다른어떤일을하도록내버려두지않고우리가오류를처리하기위해서어떠한적절한조치를취할수있을까? 아래프로그램이오작동하거나좋지않은동작을하는것은받아드릴수없다! 안전과직결된프로그램 고객만족 우리는예상하지못하거나비정상적인상황에서복구할어떤메커니즘을필요로한다. 10
예외와예외처리 예외처리 : 실행오류를다루는최근의프로그래밍개념 예외 : 예외는프로그램실행시간에발생하여프로그램을종료시키거나비정상적인작동을유발하는오류이다. 예외처리의목적 : 실행오류를우아하게처리하고복구하기위해 자바의흔한실행오류예 : NullPointerException ArithmeticException ArrayIndexOutOfBoundsException 오류 : 복구가불가능한상황을정의하는객체 자바 API 는 20 여개의예외들을포함한다. 11
예외 -- 용어 예외용어 : 예외적인상황이일어날때, 예외가발생한다 (an exception is thrown ). 즉예외가인식되었다. 프로그램의흐름은예외가잡힌 ( caught ) 지점 ( 즉, 대응하는예외처리코드 ) 로넘어간다. 12
자바의예외구문 try { // 예외가발생할수있는코드를이부분에기술 catch (ExceptionType1 e) { // 예외유형이 ExceptionType1 인경우처리할 // 모듈을이부분에기술 catch (ExceptionType2 e) { // 예외유형이 ExceptionType2 인경우처리할 // 모듈을이부분에기술 13
예 : 예외 int Divisor; int Dividend; float Result; try { // 분모와분자를입력을받아위에서선언한변수에 // 대입한다 Result = (float) Dividend / Divisor; System.out.println(Result); catch (ArithmeticException e) { // 예외유형이 ArithmeticException 인경우처리할 // 모듈을이부분에기술 System.out.println(" 나누는값이 0이므로예외발생 "); 14
예외 자바의처리방법 예외가한메소드내에서일어나면그에대응해서다음두가지중의하나를할수있다 : 1. 예외를바로그자리에서인식하여처리한다. 예외를처리하는방법을알기에충분한정보를가지고있다면이렇게한다. 2. 메소드머리부에호출한메소드가예외를처리해야한다고선언한다. 그오류를처리하는방법을알기에충분한정보를가지고있지않다면이렇게한다. 15
예외 -- 예제 꽉찬 Queue 에, 사용자가한품목을집어넣으려고한다면... Queue 가무엇을하도록해야하는가? Queue 가해야만하는것을어떻게아는가? Queue 가메시지를출력해야하는가? Queue 의크기를늘려야하는가? 그항목을집어넣지못하게해야하는가? 새항목을위한공간을마련하기위해한항목을끄집어내야하는가? 여러분은무엇을해야하는가? 간단히말하면알지못한다. 해법 : 예외가전파되게한다. 다시말해호출한곳에서오류를다루는것을맡게한다 16
예외 -- 전파 예외가발생할때즉시처리되거나처리가전파되도록선언해야한다. 컴파일되지않는코드의예 : class Queue { public boolean isempty() { public Object dequeue() { if (isempty() == true) throw new QueueEmptyException(); 17
예외 -- 전파 dequeue 가 QueueEmptyException 라는예외를처리하거나선언해야한다고말하는오류를초래한다. 이를해결하기위해서예외가발생할지도모른다는것을선언하도록 dequeue 를수정한다 : public Object dequeue() throws QueueEmptyException { if (isempty( ) == true) throw new QueueEmptyException( ); 위의메소드머리부는이메소드가 QueueEmptyException 이라는예외를발생시킬수있고 dequeue( ) 를호출한메소드가이예외를처리할계획을세워야한다는것을선언한다. 18
예외 -- 예제 고객들의줄을흉내내기위해 Queue 클래스를사용하고싶다고가정하자. 그러면다음을할수있다 : class Customer { class Cashier { Queue customerqueue = new Queue( ); public void getnextcustomer( ) { Customer cust; cust = (Customer) customerqueue.dequeue( ); 19
예외 예제 ( 계속 ) 이는컴파일오류를초래할것이다. 왜냐하면 getnextcustomer 라는메소드는다음중하나를해야하기때문이다 : QueueEmptyException 이라는예외를처리한다. QueueEmptyException 이라는예외가위로전파한다고선언한다. 따라서, 이오류를다음두가지중의하나로고칠수있다 : 선택 1: 이메소드가 QueueEmptyException 이라는예외를직접처리하게한다. 선택 2: 이메소드가 QueueEmptyException 이라는예외처리를위로전파한다고선언한다. 20
예외 직접처리 선택 1: public void getnextcustomer( ) { Customer cust; try { cust = (Customer) customerqueue.dequeue( ); catch (QueueEmptyException e) { // 이부분에서예외 QueueEmptyException 의처리모듈을구현 21
예외 처리를전파한다고선언 선택 2: public void getnextcustomer( ) throws QueueEmptyException { Customer cust; cust = (Customer) customerqueue.dequeue( ); 이경우는누가 getnextcustomer( ) 라는메소드를호출하더라도거기서예외처리의책임을가진다는것을명시한다. 22
예외 새로만들기 예외적인상태를인식하기위해서표준 if-else 논리를사용하라. 그에적절히대응하기위해예외를새로만들수있다. 새로만드는모든예외는 java.lang.exception 의하위클래스이다. 예를들면 : class QueueEmptyException extends Exception { public QueueEmptyException( ) { public QueueEmptyException(String Message) { super(message); 23
상속과예외 예외를다룰때상속을이용할수있다. 아래와같은예외의상속계층구조를가지고있다고가정하자 : Exception IOException QueueFullException FileNotFoundException EOFException 한 try 구역 (block) 에대해하나이상의 catch 구역을가질수있다. 한 catch 구역은한예외혹은그예외의하위클래스만을처리할것이다. 24
상속과예외 다음 catch 구역의나열은제대로작동한다 : try { catch (QueueFullException e) { catch (FileNotFoundException e) { catch (IOException e) { 25
상속과예외 다음 catch 구역의나열은제대로작동하지않는다 : try { catch (IOException e) { catch (FileNotFoundException e) { // 예외 IOException 이 FileNotFoundEception 의 // 상위클래스이므로이블록은실행될수없다 26
상속과예외 예외를가지고할수있는것 : try { catch (QueueFullException e) { catch (IOException e) { catch (Exception e) { // 이부분은예외가발생한경우반드시실행되는모듈이다. 왜냐하면, // 모든예외클래스는클래스 Exception 을상위클래스로하기때문이다 27
예외와실행예외 자바에서실제로두가지유형의예외가있다 : Exception 의하위클래스인예외 RuntimeException 의하위클래스인예외 주 : 두가지모두실행오류를다룬다. RuntimeException 의하위클래스인예외는직접처리되거나메소드머리부에선언될필요가없다. 이는코드에예외처리관련부분이너무많아지는것을방지하는것이다 : // 예외 ArrayIndexOutOfBounds 이발생할가능성이있다 customerarray[10] = new Customer( ); // 예외 ArithmeticException 이발생할가능성이있다 x = y / z; 이러한예외들은보통예외들과같이여전히처리될수도있고위로전파될수도있다. 28
예외 사용해야할경우 프로그램의내부상태에문제가있다. 계약이위반된다. 보안위험이증가한다 (SecurityException). 객체혹은객체가다루는데이터에오류가있다. 잘못된매개변수들의처리 진정으로예외적인상황다루기 (memory, stack) 29
예외처리방법 예외를처리할때여러분은다음과같이할수있다 : 시스템을이전의올바른혹은안전한상태로복원한다. 시스템을어떤안전한상태로만든다. 오류메시지를출력한다. 예외를기록한다. 메소드의실행을다시시도한다. 호출한메소드로처리를넘긴다. 예외를인식한후무시해버린다. 예외를인식한후무시해버린다 가일반적으로나쁘다 : 오류가예외를일으킬만큼심각하다면무시해서는안되고처리되어야한다. 30
요약 예외의정의 : 비정상적이거나오류가있는상황을정의하는객체 예외처리의방법? 아무런처리도하지않는다 ( 프로그램이멈춘다 ) 예외가발생한곳에서처리한다 (trycatch) 프로그램의다른부분에서예외를처리한다 ( 예외전파 ) 오류의정의 : 복구가불가능한상황을정의하는객체 ( 보통처리되지않는다 ) 예외발생 31