백기선 자바스터디

백기선 자바스터디 09주차

까마귀코딩 2023. 1. 31. 16:36
더보기
왜? 라는 생각을 달고 살자!

<< 총 15주차 까지 진행되며 주어지는 키워드를 가지고 블로그에 정리하며 공부하는 스터디 입니다 ! >>


목표

자바의 예외 처리에 대해 학습하세요.

학습할 것 (필수)

  • 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
  • 자바가 제공하는 예외 계층 구조
  • Exception과 Error의 차이는?
  • RuntimeException과 RE가 아닌 것의 차이는?
  • 커스텀한 예외 만드는 방법

 

 

 

 


자바에서의 예외처리법  (  try,  catch,  throw,  throws,  finally  )


1. try, catch

 

예외가 발생했을때 우리는 try ... catch ... finally 라는 키워드로 예외를 처리할 수 있거나 메소드를 호출한 곳으로 던질 수 있습니다.

한 가지 중요한 점은 자바에서 모든 예외는 Exception이라는 클래스를 상속받습니다.

Exception의 상속 트리를 아래에 간략하게 나타내었습니다.

 

 

 

 

 

 

 

예외 처리하는 방식은 이렇다 !

try{
	//예외가 발생될만한 코드
}catch(FileNotFoundException e){	//FileNotFoundException이 발생했다면

}catch(IOException e){ //IOException이 발생했다면

}catch(Exception e){	//Exception이 발생했다면

}finally{	
	///어떤 예외가 발생하던 말던 무조건 실행

 

try 블록 : 이 블록에서 예외가 발생할만한 코드가 쓰여집니다.

catch (예외 종류) 블록 : 이 부분에서 예외가 발생되었을때 처리하는 동작을 명시합니다.

catch블록은 여러 개가 있을 수 있습니다. 맨 처음 catch 블록에서 잡히지 않는 예외라면 다음 catch의 예외를 검사합니다.

이때 상속관계에 있는 예외 중 부모가 위의 catch, 그리고 그 자식 예외 클래스가 아래의 catch로 놓일 순 없습니다.

예를 들어 아래와 같이말이죠.

try{
	//.. 중략 ..//
} catch (Exception e){
	//컴파일 오류 발생
} catch (IOException e){

}

Exception 클래스는 모든 예외의 부모이기 때문에!!!

Exception을 IOException보다 위에서 처리할 수는 없다는 뜻입니다.

왜냐면 IOException의 catch블록은 도달할 수 없는 코드이기 때문이죠.

 

 

 

finally 블록 : 여기서는 예외가 발생하건 발생하지 않건 공통으로 수행되어야할 코드를 씁니다 !!

임시 파일의 삭제 등 뒷정리 코드같은것들이 쓰입니다 !!

 

이것을 이용해서 우리는 위의 코드를 예외처리할 수 있습니다 ㅎㅎ

 

 

public static void main(String[] ar){
	int a,b;
	a=10;
	b=0;
	try {
		int c=a/b;
		System.out.println(c);	//예외발생으로 실행 불가한 코드
	}catch(ArithmeticException e) {
		System.out.println("ArithmeticException 발생");
		System.out.println("0으로 나눌 수는 없습니다");
		e.printStackTrace();
	}finally {
		System.out.println("finally 실행");
	}
}

 

printStackTrace()라는 메소드는 어느 부분에서 예외가 발생했는지 알려주는 추적로그를 보여줍니다.

Exception이 발생했을때 기본 동작!!!!

 

결과는 아래와 같은 것을 알 수 있습니다.

 

ArithmeticException 발생
java.lang.ArithmeticException: / by zero
	at aa.Main.main(Main.java:11)		//Main.java에서 11번째 줄에서 발생했다는 printStackTrace
finally 실행

 

 

 

2. throws

아까전에 예외를 그냥 던질 수 있다고 했죠??

그 의미가 어떤 의미냐???

예외를 여기서 처리하지 않을테니,  나를 불러다가 쓰는 녀석에게 에러 처리를 전가하겠다는 의미이며

코드를 짜는 사람이 이 선언부를 보고 어떤 예외가 발생할 수 있는지도 알게 해줍니다.

어떤 뜻인지 모르겠다구요? 아래의 코드를 통해서 알아보도록 합니다. 

 

public static void divide(int a,int b) throws ArithmeticException {
	if(b==0) throw new ArithmeticException("0으로 나눌 수는 없다니까?");
	int c=a/b;
	System.out.println(c);
}
public static void main(String[] ar){
	int a=10;
	int b=0;
		
	divide(a,b);
}

divide()메소드는 a와 b를 나눈 후에 출력하는 역할을 하는데,

이 나누기 부분에서 우리는 예외가 발생할 수 있음을 알수있다 !

그래서 try, catch로 예외 처리를 해야하지만, divide()를 호출하는 부분에서 처리하기를 원한다 ~!@

왜냐면 divide()를 호출한 곳에서 예외가 발생한 다음의 처리를 divide() 메소드가 정하지 않기 때문!

 예를 들어 main메소드에서는 예외가 발생하면 ??? 

다시 divide()를 호출하거나, 프로그램을 끝내거나, b의 값을 다시 입력받거나 해야하기 때문이고,

divide()메소드가 그 결정을 할 수 없다는 의미 !!

 그래서 throws ArithmeticException을 divide를 호출한 main에다가 던지는 것(throw)이다

여기서 예외를 던지는 방법은 아래와 같다 !

 

(아, 참고로 Exception 생성자 중에서 메시지를 받는 생성자가 있는데,

메시지를 보려면 getMessage()메소드를 이용할 수 있습니다.

아래에서 그 메소드를 사용함.)

 

throw 예외객체
ex) throw new Exception("예외 발생!")

 

 

예외를 발생시키는 키워드는 throw

이때 main은 그 예외를 처리하기 위해 try, catch블록을 쓰면 된다

 

try {
	divide(a,b);
}catch(ArithmeticException e) {
	e.getMessage();
	e.printStackTrace();
}

 

 

throws 키워드로 처리되어야할 예외가 여러개가 존재한다면 ???

쉼표로 끊어서 예외를 넘겨줄 수 있따 !!

그 결과는 아래와 같다 ㅎㅎ

 
java.lang.ArithmeticException: 0으로 나눌 수는 없다니까?
	at aa.Main.divide(Main.java:8)
	at aa.Main.main(Main.java:17)

 

 


예외처리를 잘하는 9 가지 방법 !


 

 

1. 리소스 정리한다. 

2. 더 자세한 예외를 쓴다. 

3. javadoc으로 설명한다. 

4. 메시지를 자세하게 적는다. 

5. catch 절 순서를 지킨다. (부모예외 - 자식예외)

6. Throwable catch 하지마라 !

7. 읽씹하지 마라 ! ( 무시마라 ! )

8. 로그 찍고 다시던지지 마 !

9. 예외를 랩핑할경우 Cause 예외를 담아서 던져 !

 

 

 

 

 

파일을 열고 512 바이트 버퍼를 생성한 다음, 

파일로부터 512 바이트만큼 데이터를 읽은 후

파일 스트림을 닫는코드이다. 

 

정상적으로 진행되면 문제없겠지... 하지만 

inputStream.read(buffer, 0, 512) 에서 IOException 이 발생하면...?

 

프로그램은 문제없이 정상적으로 에러로그를 찍고 진행하겠지만 

파일 스트림은 열려있는채로 남아있겠다.... ㅠ

 

만약 초당 수백명의 사용자가 접속해 사용하는 시스템이라면 어떨까???

 

제때 정리되지 않은 리소스가 문제를 일으키겠지 뭐...ㅎ

 

 

try - catch 구문에서 리소스를 열었다면 ?? 

fianally 블록에서 리소스를 정리하거나 

try - with - resource 구문을 이용해야한다. 

 

 

이렇게 finally 블록에 리소스를 정리하는 코드를 넣어주면 된다.

 

 

만약 리소스가 AutoCloseable 인터페이스를 구현하고 있다면 ???? 

try - with - resource 구문을 이용해서 좀더 깔끔한 코드를 작성해보자 !@!@

 

 

 

 

 

두 메소드 모두 예외발생시킨다.

 

첫번째는 Exception 클래스를 사용해서 뭔지 모르겠지만 

예외가 발생할수 있다는 것을 명시했고, 

 

두번째는 좀더 자세한 예외클래스인

NumberFormatException 을 명시했다. 

 

 

Exception 을 클래스로 퉁쳐버리면 ??? 

 

이 메소드를 호출하는 쪽에서 예외처리하는 코드가 복잡해진다. 

 

Exception 으로 받은 예외가

NumberFormatExeption 인 경우도 있고, 

illgalArgumentException 이거나 

IOException 일수도 있다. 

 

따라서 모든 경우에 대한 코드를 작성할 수밖에 없어진다 ....ㅠㅠ !!!!!

 

 

 

 

자바 독 문서에 @throw선언으로 예외에 대한 설명을 글로 적어줘야 한다!!! 최대한 !! 자세히 !!

 

 

 

 

예를 들어 IOException 의 경우, 왜 ? IO를 실패 했는지 부가적인 정보가 필요하다. 

반면 NumberFormatException 예외의 경우 ??? 

숫자 포맷이 아닌 문자열을 숫자로 변환하려고 했음을 이름에서 짐작할수 있지.

 

이 경우 어떤 문자열을 변환하려했는지만 적당히 적어주자.

 

 

ㅇㅣ 코드를 실행한 경 우 ?

 

 

이런 에러가 발생한다 !

 

문자열을 숫자로 변경할수 없습니다 

같은 내용이 예외 메세지에 없어도 충분히 상황을 파악할수가 있다. 

 

 

 

 

이렇게 처리해버리면 ???

 

 

모든 예외가 Exception 절에서 걸려버리기 때문에 

IllegalArgumentException 처리 블록은 실행조차 안되겠지 .... 

부모 예외 >  자식예외 처리 잘 하자 !!

 

따라서 순서를 이렇게 상세한 예외부터 처리해줘야 한다 !!!

 

 

 

 

 

 

Throwable 을 catch 절에서 잡아 처리해 버리면 ?? 

 

예외 뿐만이 아니라 에러도 잡아서 처리해버린다. 

예외와 다르게 에러의 경우 ..... ?

JVM 이 프로그램을 실행을 못할 정도의 심각한 문제라고 판단될 경우 발생한다 !!

 

사용자가 이런 에러까지 잡아서 처리해버리면 JVM 에서 예상치 못한 동작들을 할수도 있다.

 

 

예를들어... OutOfMemoryError, StackOverflowError 같은건 사용자가 catch 절에서 잡아도 처리할수있는게 없어 !

따라서 Throwable 은 예외처리 하지 않는것이 좋다

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


자바가 제공하는 예외 계층 구조


 

 

체크예외와 언체크예외가 있다 !

 

 

1. 체크예외 

빨간박스 애들이 체크예외    ==   RuntimeException 을 상속하지 않는 예외들을 말함 !

 

체크예외가 발생할수 있는 메소드를 사용할 경우, 복구가 가능한 예외들이기 때문에 

반드시 예외를 처리하는 코드를 작성해야 함 !!!

 

 

해결 어떻게 ? 

-> catch 문으로 예외를 잡거나, 

throw로 예외를 자신을 호출한 클래스로 던지는 방법을 써서 해결해야함 

 

이때 해결안하면 어떻게 되나?

-> 컴파일시 체크예외가 발생 !

 

 

체크예외는 ???  

 

= JAVA 컴파일러와 JVM이 규칙을 준수하는지 확인함 

그래서 Exception 이 호출된다. 

 

대표적인익셉션 - IOException, SQLException

 

 

2. 언체크예외 

파란박스 애들이 언체크예외 !!

 

RuntimeException 을 상속한 예외들을 말하는데, 

언체크예외라고 불리는 이유는 ?  명시적으로 예외처리를 강제하지 않기 때문이다. 

 

 

언체크 예외라고 불리는 이유가 뭘ㄲㅏ? 

-> 왜냐면 명시적으로 예외처리를 강제하지 않기 때문이다 !

 

언체크 예외는

따로 catch 문으로 예외를 잡거나,

throw로 선언하지 않아도 된다 !

 

프로그램에 오류가 있을때 발생하도록 의도된것이다. 

 

대표적인익셉션 - NullPointerException, IllegalArgumentException

 

 

 

 

 

 

 

 

 

 


Exception과 Error 의 차이 ??


 

 

익셉션은 ? 

예외를 말한다 ! 

발생하더라도 수습할수 있는 비교적 덜 심각한 오류이다

 

 

 

예외는??

프로그래머가 적절히 코드를 작성해주면 비정상적인 종류를 막을수 있다. 

 

에러는 메모리 부족이나 스텍오버플로우와 같이 발생하면 복구할수 없는 심각한 오류이다 !

 

 

 

 

 

예외(Exception) ??? 

프로그래밍에서 마이크로프로세서가 수행 중에 있는 작업이 중단된 상태며, 그 상황을 별도의 루틴을 사용해서 처리하는 방식

별도의 처리 루틴이 있다는 측면만을 제외하면 예외는 인터럽트(interrupt)의 개념과 비슷함.

 

오류(Error) ???

미리 기대한 값을 얻을 수 없거나 혹은 어떤 상태가 일관적이지 못한 상황

 

 

 

 

 

 

 

 

 

 

 

 


RuntimeException 과 RuntimeException 아닌것의 차이 ??


 

1. RuntimeException 실행중에 따로 명시적으로 예외처리를 강제하지 않는다. 

2. RuntimeException이 아닌것은 명시적으로 예외처리를 강제로 해줘야한다. 

3. 이 예외처리는 컴파일시에 체크한다.

 

같은 Exception인데 예외처리를 강제하지 않는 이유는 무엇일까 ?

  • 런타임 예외는 어느곳에서나 발생할수 있는 이슈이다 !
  • 이것을 일일히 런타임 익셉션을 추가하거나 예외처리를 하는것은 프로그램의 명확성을 떨어뜨리기때문 ㅠ

 

 

 

 

 

 


커스텀한 예외 만드는 방법 ??


자신이 원하는예외를 상속받아서 구현하면 된다 !

 

일반 예외로 선언할 경우 Exception을 상속받아 구현

실행 예외로 선언할 경우에는 RuntimeException을 상속받아 구현

 

 

 

- 사용자 정의 예외클래스는 컴파일러가 체크하는 일반예외로 선언할 수도 있고, 

컴파일러가 체크하지 않는 실행예욀 선언할수도 있다 !

 

- 일반예외로 선언할 경우 Exception 을 상속하면 된다. 

실행 예외로 선언할 경우에는 RuntimeException 을 상속하면 된다. 

 

- 사용자 정의 예외 클래스 이름은 Exception 으로 끝나는 것을 권장한다. 

 

- 사용자 정의 예외 클래스 작성시 ??  생성자는 두개를 선언하는것이 일반적이다. 

1. 매 변수가 없는 기본생성자 

2. 예외발생원인 ( 예외메시지 ) 전달하기 위해 String 타입의 매개변수를 갖는 생성자 

 

- 예외 메시지 용도는 catch {} 블록의 예외처리 코드에서 이용하기 위함이다. 

 

 

 

 

커스텀한 예외 작성하기 예시 

 

내가 원하는 예외 ?  : RuntimeException