티스토리 뷰

Java/문법

12. 예외처리 : Exception handling (try-catch-finally)

알 수 없는 사용자 2018. 11. 2. 04:05


이번 포스팅에서는

예외처리에 대해 알아보도록 하겠습니다.

예외처리는

파일 입출력이나 네트워킹에서

필수적으로 사용되고

개발자가 직접 예외상황을 만들 수도 있기 때문에

안전한 프로그램을 위해서는 필요한 기능입니다.


======================================================================


예외처리 : Exception handling(try-catch-finally)






1. Exception handling : What?



 예외라는 단어가 어떤 단어인지 모르시는 분은 없을 것입니다.

하지만 자바에서의 예외처리는 구체적으로 어떤 예외를 처리하는지 정확히 알아야 사용할 수 있습니다.


 보통 흔히 프로그램이 작동하다가 오작동, 혹은 비정상적으로 종료되는 등 다양한 이상적인 현상이 나타나는 것을 오류 또는 에러라고 이야기합니다.

이러한 프로그램에서 에러는 발생되는 시점에 따라 에러의 종류를 3가지로 분류할 수 있습니다.


 - 컴파일 에러 : 컴파일할 때 발생한 에러

 - 런타임 에러 : 프로그램이 실행하는 도중에 발생한 에러

 - 논리적 에러 : 컴파일과 실행이 잘되었는데 결과적으로 의도와 다르게 동작하는 에러


컴파일 에러와 같은 경우는 컴파일러가 소스코드에 대한 오타나 잘못된 문법, 자료형 등을 체크해서 프로그램이 실행되기 전에 에러 체크가 가능합니다.

하지만 컴파일러가 발달되었다 하더라도 실행 도중에 발생하는 잠재적인 오류까지는 해결할 수 없기 때문에 개발자는 런타임 오류에 대한 대비를 해야 합니다.


이렇게 런타임 오류에 대비하기 위해 만들어진 기능이 예외처리입니다.






2. Exception handling : Exception and Error Hierarchy?



 자바에서는 런타임 오류에 대비하기 위해 프로그램 오류를 에러(Error)와 예외(Exception)로 나누어서 구분하였습니다. 

여기서 말하는 Error는 위에서 소개할 때 사용한 에러와는 다른 개념입니다.


 자바에서의 에러(Error)는 오류가 발생했을 때 코드상에서 오류를 잡을 수 없는 심각한 오류를 나타내고 예외(Exception)는 코드상에서 오류를 잡을 수 있는 다소 경미한 오류라고 볼 수 있습니다.


Error와 Exception 클래스는 Throwable 클래스를 상속받아 설계되어 있고, 각각의 예외나 에러 상황에 따라 Exception 혹은 Error 클래스를 상속하여 상황별 클래스가 정의되어 있습니다.



[Exception 클래스를 상속한 클래스]



[Error 클래스를 상속한 클래스]



실제로 설계되어 있는 Exception은 굉장히 많고 또한 자바의 버전이 올라가면서 상황에 따른 수많은 Exception이 생성되기 때문에 전부 다 숙지하고 있는 것은 거의 불가능입니다.


 하지만 많은 Exception 클래스을 두 가지로 구분하여 숙지하고 있는 것이 좋습니다.

Exception 클래스는 크게 RuntimeException과 그 이외의 다른 Excpetion들로 구분할 수 있습니다.


RuntimeException을 상속한 클래스는 코드상에서의 개발자 실수에 의해서 발생할 수 있는 예외들로 자바의 프로그래밍 요소들과 관련있습니다. 예를 들어 클래스 간의 형 변환을 잘못했다든지(ClassCastException), 값이 null인 참조변수의 멤버를 호출하려 했다든지(NullPointerException) 등입니다.


 하지만 RuntimeException을 제외한 클래스들은 주로 외부의 영향을 받아 발생할 수 있는 Exception입니다. 주로 사용자들의 입력이나 동작에 의해서 발생하는 경우가 많은데 존재하지 않는 파일의 이름을 적은 경우(FileNotFoundException), 클래스 이름을 잘못 적은 경우(ClassNotFoundException) 등의 Exception이 존재합니다.





3. Exception handling : try-catch?


 예외처리를 하기 위해 try-catch 구문을 사용합니다.

try{
...
}catch(IOException e) {
...
}catch(NullPointerException e) {
...
}

try 구문은 예외가 발생할 가능성이 있는 소스코드를 작성하는 곳입니다. 

try의 뜻처럼 예외가 발생할 수 있는 코드를 실행했을 때(시도했을 때) 예외가 발생한다면 예외가 발생한 시점을 기점으로 이후의 코드는 실행되지 않고 바로 Exception에 해당하는 catch 문으로 이동합니다.

catch 구문은 try에서 해당 Exception이 발생했을 때 처리할 동작을 작성하는 곳입니다.

만일 try 구문에서 NullPointException이 발생하는 구간이 생겼을 경우에 해당하는 catch 구문에 작성된 코드를 작업하게 되는 것입니다.


또한 try 구문에서 아무런 Exception이 발생하지 않는 경우에는 catch 구문을 생략하고 다음 코드를 순차적으로 실행하게 됩니다.



위와 같이 발생할 수 있는 Exception은 두 개인데 Exception이 발생할 시 실행되는 동작이 같다면 다음과 같이 멀티 catch 블럭으로 사용할 수 있습니다.

try{
...
}catch(IOException | NullPointerException e) {
...
}




4. Exception handling : try-catch-finally?


  try-catch 구문에서 try에서 Exception이 발생한다면 그 이후의 작업은 일어나지 않고 바로 catch 구문으로 이동한다 했습니다.

하지만 만약 Exception이 발생한 이후의 동작이 Exception이 발생하더라도 무조건 작동해야 하는 작업일 경우 난감해집니다.


이럴 때 사용하는 구문이 finally입니다.

try{
...
}catch(IOException | NullPointerException e) {
...
}finally{
...
}

마지막 catch 구문 뒤에 바로 finally 구문을 사용해줌으로써 Exception이 발생하더라도 무조건 동작해야 하는 작업을 finally에 지정할 수 있습니다.


이렇게 Exception의 형태에 따라서 프로그램이 비정상 종료되지 않고 안정적으로 작동하게 catch 구문에 정의할 수 있는데 

이러한 예외처리를 반드시 해주어야 하는 경우와 예외처리를 안 해주어도 되는 경우가 나뉩니다.


예외처리를 반드시 해주지 않아도 되는 경우는 RuntimeException을 상속한 Exception입니다. 

앞에서 말했다시피 RuntimeException은 프로그래머가 코드상에서 제어할 수 있고 JVM에 악영향을 끼치지 않는 Exception이기 때문에 예외처리를 하지 않아도 무관합니다.


하지만 RuntimeException을 제외한 Exception들은 무조건 예외처리를 해주어야합니다.

RuntimeException을 제외한 Exception들은 실제 프로그램이 가동되어서 결과로만 아는 예외들로, 프로그램적으로 막아내지 못하고 외부 환경에 따라 발생하기 때문입니다. 예를 들어 네트워킹하기 위해 다른 컴퓨터와 통신하기 위한 작업을 한다면 그 컴퓨터와 통신을 할 수 있는지 아닌지는 직접 프로그램을 작동시켜봐야 알기 때문에 예외처리를 필수적으로 해주어야 합니다.





5. Exception handling : throw, throws?


 발생할 수 있는 Exception은 기본적으로 설계되어있는 Exception뿐만 아니라 개발자가 직접 만들어서 처리할 수도 있습니다.


만약 어떠한 메소드를 호출했을 경우나 특정한 상황에서 Exception이 발생하도록 하고 싶다면 throw 키워드를 사용하면 됩니다.

throws new RuntimeException;


throw와 try-catch를 이용한 예제를 보도록 하겠습니다.

class InvalidException extends RuntimeException {   // try catch를 안해도 되는 Exception

}

class ImpossibleException extends RuntimeException {

}

class Triangle {
int x,y,z;

Triangle(int x, int y,int z){
if(x<=0 || y<=0 || z<=0) {
throw new InvalidException();
}
int a = Math.max(Math.max(x, y), z);
int b = x+y+z - a;
if(b<=a) {
throw new ImpossibleException();
}

this.x = x;
this.y = y;
this.z = z;
}

}

public class Source03_Exception {
public static void main(String[] args) {
try {
// new Triangle(1, 2, -2); // ImpossibleException 발생
new Triangle(1, 2, 1);
} catch(InvalidException e) {
System.out.println("InvalidException...");
} catch(ImpossibleException e) {
System.out.println("ImpossibleException...");
}

try { // 다음과 같이 처리가능
// new Triangle(1, 2, -2); // ImpossibleException 발생
new Triangle(1, 2, 1);
} catch(InvalidException | ImpossibleException e) {
System.out.println("InvalidException... | ImpossibleException...");
}
}
}


삼격형 객체를 생성할 때 세 변의 길이를 받아서 생성할 수 있도록 정의했습니다.


이때 삼각형의 세 변의 길이 중 가장 긴 변은 나머지 두 변의 합보다 무조건 작아야 합니다. 이 때문에 삼각형의 조건에 맞지 않는 세 변으로 삼각형 객체가 생성되었을 경우 삼각형이라고 할 수 없겠죠.


이럴 때 조건에 따른 InvalidException 클래스와 ImpossibleException 클래스를 만들어 throw 연산자를 통해 넘겨줄 수 있습니다.

객체를 생성할 때 조건에 맞지 않는 경우 Exception이 발생하게 되고 Exception에 따라 catch 구문에 있는 동작이 시작되는 것입니다.



반대로 위와 다르게 필수로 try-catch을 처리해야 하는 경우에는 다음과 같이 해당 메소드에 throws 키워드를 반드시 붙어주어야 합니다.

class CriticalException extends Exception {       // 필수처리 Exception extends Exception으로 객체 설계
}

class InvalidException extends RuntimeException { // try catch를 안해도 되는 Exception
}

class Account {
int balance;

public void addBalance(int a) throws CriticalException { // 필수처리 Exception은 다음과 같이 처리해야함.
if(a<0) {
throw new CriticalException();
}
if(a%100 != 0) {
throw new InvalidException();
}
balance += a;
}
}
public class Source04_Exception {
public static void main(String[] args) {
Account ac = new Account();
try {
ac.addBalance(1000); // 필수 처리 Exception이기 때문에 Exception 발생시 처리할 내용을 반드시 설계해야함.
System.out.println("try success");
}catch(CriticalException e) {
System.out.println("CriticalException..");
System.out.println("try fail");
}
try {
Thread.sleep(1000, 10);
System.out.println("try success");
}catch(InterruptedException e) {
System.out.println("try fail");
}

}
}


InvalidException과 달리 CriticalException은 Exception 클래스를 상속하고 있기 때문에 필수로 예외처리가 일어나야 합니다.

그렇기 때문에 addBalance() 메소드에서 필수로 처리해야 할 예외가 발생할 가능성을 메소드 끝에 throws 키워드를 사용하여 알려주어야 합니다.


이렇게 사용자가 임의로 Exception을 만들어 원하는 Exception을 터트릴 수도 있습니다.

 





6. Exception handling : try-with-resources?



 try-with-resources는 주로 입출력과 관련된 클래스를 사용할 때 유용한 구문입니다.

try-catch-finally 구문을 변형한 것으로 사용한 후에 꼭 close() 해야 하는 클래스를 사용할 때 try-with-resources 구문을 이용하면 유용합니다.


public class Source06_Exception {
public static void main(String[] args) {
/*
* 객체 사용이 완료되고 나면, 안정성을 위해 특정작업을 꼭 처리하는 걸 권장하는 것들이 있다.
*/
Scanner cin = new Scanner(System.in);
try {
System.out.print("SYSTEM> ");
String line = cin.nextLine();
String[] ar = line.split(" ");
int s=0;
for(String a : ar) {
s += Integer.parseInt(a);
}
System.out.println("SUM = " + s);
} finally {
cin.close(); // 중간에 입력된 값이 의도치 않는 값이 아니라면 오류발생시에도 무조건 일어나야 할 작업을 설계할 수 있음.
System.out.println("Scanner closed");
}
}
}


Scanner 객체도 일종의 입출력 객체로 사용 후에 close() 하는 것을 권장합니다.

그렇기 때문에 finally 구문에 close() 메소드를 따로 호출해주어야 하는 불편함이 많았습니다.


하지만 try-with-resources 구문을 사용하면 다음과 같이 try 옆 괄호 안에서 생성된 객체는 자동으로 close() 메소드가 호출되도록 합니다.

public class Source06_Exception {
public static void main(String[] args) {
// 입출력 객체들은 보통 Closeable을 토대로 만들어져 있기 때문에 close()작업을 해주는 경우가 대반사
try(CustomScanner cs = new CustomScanner();) { // try ()에서 생성하여 사용한 객체들은 자동으로 close()까지 콜됨.
cs.nextLine();
cs.nextLine();
if(Math.random()>0.0) {
throw new RuntimeException(); // Exception이 발생해도 자동으로 close 작업이 일어남.
}
}
}
}

class CustomScanner implements AutoCloseable {

public String nextLine() {
return "....";
}

@Override
public void close() {
System.out.println("CustomScanner.close()");
}


}


'Java > 문법' 카테고리의 다른 글

11. 제네릭스 : Generics  (0) 2018.11.01
10. 인터페이스 : interface  (0) 2018.10.14
8. 오버로딩, 오버라이드 : Overloading, Override & 다형성  (3) 2018.10.12
9. 추상화 : abstraction  (31) 2018.10.11
7. 상속 : extends  (31) 2018.10.11
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함