●
예외처리
- 프로그램 실행시 비정상적인 상황이 발생했을 때, 처리의 우선권을 개발자에 주어서 어떠한 조치를 취할 수 있도록 하는 문법. 예외를 개발자가 처리하지 않으면, 컴파일러에 의해서 처리된다.
-
try{ 예외가 발생할 가능성이 있는 프로그램; }
catch(발생한 예외를 저장할 변수) { 예외가 발생했을 경우의 처리; }
- 자바에서는 예외처리를 반드시 해야하는 프로그램 분야가 있으며, 예외처리를 하지 않으면 진행이 안된다.
- 예외처리를 반드시 해야하는 프로그램 분야 : 파일, 네트워크, 데이터베이스, 스레드.
- 사용 예)
class ExceptionTest {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while(true) {
int input1 = scan.nextInt();//화면에서 첫번째 정수를 입력받음 int input2 = scan.nextInt();//화면에서 두번째 정수를 입력받음 try {
System.out.println(input1 / input2);//나눗셈 실행 } catch(Exception e) {
System.out.println("0으로 나룰 수 없습니다.“);
}//try의 끝 }//while반복문의 끝 }//main의 끝
}//클래스의 끝
● 예외클래스의 주요 메서드
- 예외를 비롯한 오류상황도 인스턴스 형태로 생성된다.
- 모든 예외의 최고 조상은 Exception 클래스
- 다형성으로 Exception 형태로 변수를 선언하면, 이 변수에 모든 종류의 예외 인스턴스가 저장될 수 있다.
- Exception 클래스가 가지고 있는 메서드 소개
printStackTrace() : 예외 발생 당시의 호출스택(call stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력 getMessage() : 발생한 예외 클래스의 메시지(내용 즉, 어떤 종류의 예외인지 등)를 반환한다. 따라서, 발생한 예외 에 대한 메시지를 화면에 출력하려면, 다음과 같이 사용한다.
● 예외 클래스
- 대표적인 예외 클래스는 다음과 같다.
RuntimeException : 프로그램을 실행할 때 발생한 예외 IOException : 입출력에 관련된 예외
NoSuchMethodException : 메서드를 찾을 수 없을 때 발생하는 예외 InterruptedException : 인터럽트에 관련된 예외
ClasNotFoundException : 클래스를 찾을 수 없을 때 발생하는 예외
NumberFormatException : 숫자가 입력되어야 하는데 문자가 입력되어서 발생하는 예외 ArithmeticException : 수학연산할 때 발생하는 예외
- 여러 가지 예외를 따로 처리하고저 하는 경우)
try {
//예외가 발생할 수 있는 부분 } catch(NumberFormatException e) {
System.out.println("숫자가 입력되어야 합니다.“);
} catch(ArithmeticException e) {
System.out.println("0으로 나눌 수 없습니다.“);
}
- 다음과 같이 예외를 처리하면 예외가 따로 처리되지 않는다.
try {
//예외가 발생할 수 있는 부분
} catch(
Exception
e) { //모든 예외가 e에 저장된다.(다형성) 따라서, //다음 라인의 catch()는 실행되지 않는다.System.out.println("예외가 발생했습니다.");
} catch(NumberFormatException e) {
System.out.println("숫자가 입력되어야 합니다.“);
} catch(ArithmeticException e) {
System.out.println("0으로 나눌 수 없습니다.“);
}
● 예외를 일부러 발생시킬 수도 있다.
- 예외를 명시적으로 발생시킬 경우 throw 명령어를 사용한다.
- throw 다음에 발생시킬 예외의 종류(예외 클래스)를 명시한다.
- 사용 예)
public class ExceptionTest2 {
public static void main(String[] args) { try {
System.out.println("예외 발생 전입니다.“);
throw new NumberFormatException("숫자변환 예외발생“);
//throw를 사용해서 고의로 예외를 발생시키고 있다.
//고의로 발생시키는 예외의 종류는 NumberFormatException } catch(NumberFormatException e) {
System.out.println("고의로 발생한 예외 : “ + e.getMessage());
//NumberFormatException예외를 발생시키면서 전달한 “숫자변환 예외발생”
//이라는 문자열이 getMessage()에 의해서 화면에 출력된다.
} }
}
● 예외처리는 누가 해야하는가?
- 일반적으로 예외가 발생한 장소에서 직접처리하는 것이 일반적이다.
- 그러나, 예외 처리를 해당 부분에서 하지 않고 예외 처리를 상위 계층으로 떠넘길 수도 있다.
- 이렇게 예외처리를 직접하지 않고 상위 계층으로 떠넘길 때 사용하는 명령어가
throws
예외 처리른 상위 계층으로 떠넘기는 예)
class ExceptionTest3 {
public static void main(String[] args) { try {
input();
} catch(ArithmeticException e) {
System.out.println(e.getMessage());
} catch(IOException e) {
System.out.println(e.getMessage());
} catch(NumberFormatException e) { System.out.println(e.getMessage());
}//이곳에서 예외처리를 해야하는 것은 아니지만, 사용하고 있는 메서드들 즉,
//input() 메서드와 input() 메서드가 사용하는 dividing() 메서드에서 발생하는 예외들을 //모두 상위 계층으로 떠넘겼기 때문에 이 main() 메서드에서 예외를 처리하고 있다.
}//main 메서드의 끝
public static void input() throws ArithmeticException, IOException, NumberFormatException { Scanner scan = new Scanner(System.in);
while(true) {
int num1 = scan.nextInt();
int num2 = scan.nextInt();
dividing(num1, num2);
}//while의 끝
//dividing()메서드에서 떠넘겨진 예외를 이곳에서 처리해야 하지만,
//여기서도 예외를 상위 계층 즉, 이 메소드를 호출한 main() 메소드로 떠넘기고 있다.
}//intput 메서드의 끝
public static void dividing(int num1, int num2) throws ArithmeticException { System.out.println(
num1 / num2
);//나눗셈을 수행한다.//예외가 이곳에서 발생할 수 있지만, 이곳에서 예외처리를 하지 않고 상위 계층 즉, //이 메소드를 호출한 input() 메소드로 예외처리를 떠넘기고 있다.
//throws 를 사용해서 처리를 떠넘길 예외의 종류를 명시한다.
}//dividing 메서드의 끝 }
예외 고의 발생과 예외 떠넘기기가 함께 있는 예)
class ExceptionThrowsTest {
public static void main(String[] args) throws Exception {//떠넘겨진 예외를 처리하지 않고 떠넘김 method1();
}
public static void method1() throws Exception {//method2()에서 발생한 예외를 처리하지 않고 떠넘김 method2();
}
public static void method2() throws Exception {//발생한 예외를 처리하지 않고 떠넘김 throw new Exception();//고의로 예외를 발생시킴
} }
public class ExceptionTest4 {
public static void main(String[] args) { try {
method1();
} catch(Exception e) {
System.out.println("main메서드에서 예외가 처리됨.“);
e.printStackTrace();//예외가 발생했을 당시의 메모리 상태를 출력 }
}//main 메서드의 끝
staic void method1() throws Exception {//고의로 발생한 예외처리를 상위 계층으로 떠넘기고 있슴 throw new Exception();//Exception 예외를 고의로 발생시킴
} }
● finally
- finally는 try-catch와 함께 사용하며 예외의 발생 여부에 관계없이 항상 실행되어야 할 작업을 지정한다.
- try-catch-finally의 순서로 구성한다.
try {
//예외가 발생할 가능성이 있는 부분
try-catch-finally 예)
public class ExceptionTest5 {
public static void main(String[] args) { try {
startInstall();//프로그램 설치 시작 copyFiles();//설치관련 파일을을 복사 } catch(Exception e) {
e.printStackTrace();//예외가 발생했을 당시의 메모리 상태를 출력 } finally {
deleteTempFiles();//설치가 완료되었든, 설치가 실패했든, 처리되는 부분 }
}//main 메서드의 끝
static void startInstall() {
System.out.println(“프로그램 설치를 시작합니다.”);
}
static void copyFiles() {
System.out.println("파일을 복사합니다.“);
}
static void deleteTempFiles() {
System.out.println("설치 작업에 사용된 임시파일을 삭제합니다.“);
} }
● 예외 되던지기
한 메서드 내에서 발생할 수 있는 예외가 여럿인 경우, 몇 개는 try-catch를 사용해서 해당 메서드 내에서 처리하고, 나머지는 상위 계층으로 예외를 떠넘길 수도 있다. 즉, 발생한 예외를 나누어서 처리할 수 있다.
이것은 예외를 처리한 후 고의로 다시 예외를 발생시키는 방법으로 구현한다.
예외가 발생할 가능성이 있는 부문에서 try-catch 를 사용해서 예외를 처리하고 throw를 사용해서 예외를 다시 발생 시킨다.
예외 되던지기는 하나의 예외에 대해서 예외가 발생한 메서드와 이 메서드를 사용하는 메서드 양쪽 모두에서 처리해 줘야 할 작업이 있을때 사용한다.
예외 되던지기 예)
public class ExceptionRethrowTest {
public static void main(String[] args) { try {
method1();
} catch(Exception e) {
System.out.println("main 메서드에서 예외 처리“);//method1()에서 발생한 예외 처리 }
}
static void method1() throws Exception { try {
throw new Exception();//예외를 고의로 발생시킴 } catch(Exception e) {
System.out.println("method1()에서 예외 처리“);
throw e;//처리된 것과 동일한 예외를 또 고의로 발생시킴 }
} }
● 프로그래머가 자신만의 예외를 만들 수도 있다.
기존에 정의된 예외 클래스 외에 프로그래머가 필요에 따라서 새로운 예외 클래스를 만들어서 사용할 수 있다. 필요 한 예외 클래스를 상속해서 새로운 예외 클래스를 만든다.
class 나만의예외클래스이름 extends Exception {
나만의예외클래스이름(String msg) {//getMessage()메서드를 사용할 때 출력될 메세지를 넘긴다.
super(msg);//전달받은 msg를 이용해서 상위 클래스의 생성자를 호출한다.
}//“나만의예외클래스”의 생성자 }
class MyException extends Exception {//MyException 이라는 이름의 새로운 예외클래스를 만듬 MyException(String msg) {//msg 부분에 에러메시지 내용이 입력된다.
super(msg);
자신이 만든 새로운 예외 클래스를 사용한 예)
public class NewExceptionTest {
public static void main(String[] args) { try {
startInstall();
copyFiles();
} catch(SpaceException se) {
System.out.println("에러 메시지 : " + se.getMessage());
System.out.println("공간을 확보한 후 다시 설치하시기 바랍니다.");
} catch(MemoryException me) {
System.out.println("에러 메시지 : " + me.getMessage());
System.out.printnl("메모리를 확보한 후 다시 설치하시기 바랍니다.");
} finally {
deleteTempFiles();
} }
static void startInstall() throws SpaceException, MemoryException { if( ! enoughSpace() )
throw new SpaceException("설치할 공간이 부족합니다.");//본인이 만든 예외를 발생시킴 //SpaceException 예외의 에러 메시지는 "설치할 공간이 부족합니다." 가 된다.
if( ! enoughMemory() )
throw new MemoryException("메모리가 부족합니다.");//본인이 만든 예외를 발생시킴 //MemoryException 예외의 에러 메시지는 “메모리가 부족합니다.” 가 된다.
}
static void copyFiles() { }//가상으로 파일을 복사한다고 가정함.
static void deleteTempFiles() { }//가상으로 임시파일이 삭제한다고 가정함.
static boolean enoughSpace() {//설치할 공간이 부족하다는 의미로 false를 리턴함. 바꿔도 상관없다.
return false;
}
static boolean enoughMemory() {//설치할 메모리는 충분하다는 의미로 true를 리턴함. 바꿔도 상관없다.
return true;
} }
class SpaceException extends Exception {//프로그래머가 만든 예외 클래스. 이름은 SpaceException SpaceException(String msg) {
super(msg);
}//생성자 }
class MemoryException extends Exception {//프로그래머가 만든 예외 클래스. 이름은 MemoryException MemoryException(String msg) {
super(msg);
}//생성자 }