-
Java 항해일지 - 9. 예외 처리공부일기/자바 스터디 2021. 1. 17. 19:57
목표
자바의 예외 처리에 대해 학습하세요.
학습할 것 (필수)
- 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
- 자바가 제공하는 예외 계층 구조
- Exception과 Error의 차이는?
- RuntimeException과 RE가 아닌 것의 차이는?
- 커스텀한 예외 만드는 방법
프로그램을 사용하다가 프로그램이 비정상적으로 종료되는 결과를 초래하는 원인을 프로그램 에러(Error) 또는 오류라고 한다.
에러는 크게 컴파일 에러와 런타임 에러로 구분할 수 있다.
컴파일 에러는 기본적으로 자바 컴파일러가 문법 검사를 통해서 오류를 잡아내 준다. 우리는 컴파일러가 알려주는 오류를 수정하면 성공적으로 컴파일을 해서 프로그램을 실행할 수 있다. 그러나 컴파일이 문제없이 되더라도 실행 과정(runtime)에서 오류가 발생할 수 있는데, 이런 런타임 에러를 방지하기 위해서는 프로그램 실행 도중 일어날 수 있는 모든 경우의 수를 고려하여 대비할 필요가 있다.
자바에서는 런타임 에러를 예외(Exception)와 에러(Error) 두 가지로 구분하여 대응하고 있다.
에러는 메모리 부족(OutOfMemoryError), 스택오버플로우(StackOverFlowError)처럼 JVM이나 하드웨어 등의 기반 시스템의 문제로 발생하는 것이다. 발생했을 때를 대비해서 프로그래머가 할 수 있는게 없다. 발생하는 순간 무조건 프로그램은 비정상 종료되기 때문에 애초에 발생하지 않도록 주의를 기울여야 한다.
예외는 발생하더라도 프로그래머가 미리 적절한 코드를 작성해서 프로그램이 비정상적으로 종료되지 않도록 핸들링 해줄 수 있다.
자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
* try-catch, finally
예외를 처리하기 위해 try-catch문을 사용하게 되며 기본적인 구조는 아래와 같다.
try {
// 예외가 발생할 가능성이 있는 문장
} catch (Exception1 e1) {
// Exception1이 발생했을 경우, 이를 처리하기 위한 문장
} catch (Exception2 e2) {
// Exception2가 발생했을 경우, 이를 처리하기 위한 문장
} finally {
// 항상 처리할 필요가 있는 코드
}하나의 try블럭 다음에는 여러 종류의 예외를 처리할 수 있도록 하나 이상의 catch 블럭이 올 수 있고, 이 중 발생한 예외의 종류와 일치하는 단 한 개의 catch 블럭만 수행하게 된다. 발생한 예외의 종류와 일치하는 catch 블럭이 없는 경우 예외는 처리되지 않는다.
finally 블록은 필수 블록은 아니며, 사용하게 되면 해당 블록내에 작성된 코드는 예외 발생 유무와 관계없이 무조건 수행된다.
주로 데이터베이스나 파일을 사용 후 닫는 기능과 같이 항상 수행해야 할 필요가 있는 경우 사용된다고 한다.
예제)
package javastudy; public class TryCatch { public static void main(String[] args) { System.out.println(1); try { System.out.println(2); System.out.println(3); } catch (Exception e) { System.out.println(4); } finally { System.out.println("끝"); } System.out.println(5); } }
위의 예제에서 try블럭안에 예외가 발생하지 않았기 때문에 catch블럭의 문장이 실행되지 않은 것을 확인할 수 있따. 예제를 변경해서 try블럭에서 예외가 발생하도록 하면 아래와 같다.
package javastudy; public class TryCatch { public static void main(String[] args) { System.out.println(1); try { System.out.println(2/0); System.out.println(3); } catch (Exception e) { System.out.println(4); } finally { System.out.println("끝"); } System.out.println(5); } }
try블럭에서 예외가 발생해 catch블럭으로 넘어가게 되고, 4를 출력하고 catch블럭을 빠져나와 finally 블록 내의 코드를 실행하고 순서대로 나머지 명령을 실행한 후 프로그램이 정상적으로 종료되는 것을 확인할 수 있다.멀티 catch
JDK 1.7 부터 추가된 기능으로 '|' 기호를 사용해 여러 catch 블럭을 하나로 합칠 수 있는데, 이것을 멀티 catch라고 한다.
이때 합칠 수 있는 예외 클래스의 개수는 제한이 없다. 그러나 연결되어있는 예외 클래스가 부모/자식 관계에 있다면, 컴파일 에러가 발생한다.try { .. do something } catch (IllegalStateException | IllegalArugmentException e) { // 여러개의 catch 블럭이 생성되었고, 그 예외의 처리가 동일한 행위인 경우 사용한다. }
* throw
throw를 통해 인위적으로 예외를 발생시킬 수 있다. 방법은 아래와 같다.
1. 연산자 new를 이용해 발생시키려는 예외 클래스의 객체를 만들고
2. 키워드 trhow를 이용해 예외를 발생시킨다.
아래 예제에서는 두 줄의 코드를 한 줄로 줄여서 실행시켰다.
예제
package javastudy; public class Throw { public static void main(String[] args) { try { throw new Exception("고의로 발생시킴"); // Exception e - new Exception("고의로 발생시킴"); // throw e; } catch (Exception e) { System.out.println("에러: " + e.getMessage()); e.printStackTrace(); } System.out.println("프로그램 종료"); } }
printStackTrace() : 예외 발생 당시의 호출스택에 있었던 메서드의 정보와 예외 메시지를 화면에 출력한다.
getMessage() : 발생한 예외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있다. 위 예제에선 "고의로 발생시킴"에 해당한다.* throws
메서드에 예외를 선언하는 방법으로 메서드의 선언부에 예외를 선언함으로써 메서드를 사용하려는 사람이 메서드의 선언부를 봤을 때, 이 메서드를 사용하기 위해서는 어떠한 예외들이 처리되어져야 하는지 쉽게 알 수 있다.
예외가 발생하는 메서드를 호출한 곳에 예외 객체를 넘긴다.
예제)
package javastudy; public class Throws { public static void main(String[] args) { try { File f = createFile(""); System.out.println(f.getName() + "파일 생성"); } catch (Exception e) { System.out.println(f.getMessage() + "파일 생성 실패"); } } static File createFile(String fileName) throws Exception { if (fileName==null || fileName.equals("")) { throw new Exception("올바르지 않은 파일 이름"); } File f = new File(fileName); return f; } }
위와 같은 코드가 있다고 가정해볼 때, 파일의 이름이 유효하지 않아 예외를 발생시킨다. createFile()에 throws로 예외가 선언되어 있으므로, 파일 이름이 올바르지 않은 경우 예외를 main()으로 전달하고 main()의 try-catch문을 통해 예외가 처리된다.
자바가 제공하는 예외 계층 구조
자바에서는 실행 시 발생할 수 있는 오류를 크래스로 정의하였으며, 모든 클래스의 조상은 Object 클래스이므로 Exception과 Error 클래스 역시 Object 클래스 역시 Object 클래스의 자손들이다.
모든 예외의 최고 조상은 Exception 클래스이며, 상속계층도를 Exception 클래스부터 나타내게 될 경우 위와 같다.
파란 점선 안에 있는 부분들은 Exception 클래스와 그 자손들
주황 점선 안에 있는 부분들은 RuntimeException 클래스와 그 자손들이다.
Exception과 Error의 차이는?
자바에서는 실행 시 발생할 수 있는 프로그램 오류를 Error와 Exception 두 가지로 구분했다.
예외(Exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
ex) Exception클래스와 자손들(CheckedException), RuntimeException클래스와 자손들(UncheckedException)
에러(Error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류ex) 메모리 부족(OutOfMemoryError)이나 스택오버플로우(StackOverflowError)
RuntimeException과 RE가 아닌 것의 차이는?
자바의 예외 계층 구조에서 주황색 점선 부분을 제외한 나머지 부분들을 RuntimeException이 아닌 것으로 정의 할 수 있다.
RuntimeException에 해당하는 예외의 경우 프로그래머의 실수로 발생하는 것들이기 때문에 예외처리를 강제하지 않는다.
그러나 그 외의 것들은 예외처리를 해주지 않으면 컴파일조차 되지 않는다.
커스텀한 예외 만드는 방법
커스텀 예외를 만들때 참고해야 할 4가지 Best Practices
1. Always Provide a Benefit
자바 표준 예외들에는 포함되어 있는 다양한 장점을 가지는 기능들이 있다.
이미 JDK가 제공하고 있는 방대한 수의 예외들과 비교했을 때 만들고자 하는 커스텀 예외는 어떠한 장점도 제공하지 못한다면? 커스텀 예외를 만드는 이유를 다시 생각해볼 필요가 있다.
어떠한 장점을 제공할 수 없는 예외를 만드는 것 보다 오히려 UnsupportedOperationException 이나, IllegalArugmentException 과 같은 표준 예외 중 하나를 사용하는 것이 낫다.
2. Follow the Naming Convention
JDK가 제공하는 예외 클래스들을 보면 클래스의 이름이 모두 "Exception" 으로 끝나는 것을 알 수 있다.
이러한 네이밍 규칙은 자바 생태계 전체에 사용되는 규칙이다.
즉, 만들고자 하는 커스텀 예외 클래스도 이 네이밍 규칙을 따르는 것이 좋다.
3. Provide javadoc Comments for Your Exception Class
많은 커스텀 예외들이 어떠한 javadoc 코멘트도 없이 만들어진 경우들이 있다.
기본적으로 API의 모든 클래스, 멤버변수, 생성자들에 대해서는 문서화 하는 것이 일반적인 Best Practices 이다.
잘 알겠지만 문서화되지 않은 API들은 사용하기 매우 어렵다.
예외 클래스들은 API에 크게 드러나지 않는 부분일 수 있으나 사실상 그렇지 않다.
클라이언트와 직접 관련된 메소드들 중 하나가 에외를 던지면 그 예외는 바로 예외의 일부가 된다.
그렇다는 것은 잘 만들어진 JavaDoc와 문서화가 필요하다는 뜻이다.
JavaDoc은 예외가 발생할 수도 있는 상황과 예외의 일반적인 의미를 기술한다.
목적은 다른 개발자들이 API를 이해하고 일반적인 에러상황들을 피하도록 돕는 것이다.
4. Provide a Constructor That Sets the Cause
커스텀 예외를 던지기 전에 표준 예외를 Catch 하는 케이스가 꽤 많다.
이 사실을 관과하지 말자.
보통 캐치된 예외에는 제품에 발생한 오류를 분석하는데 필요한 중요한 정보가 포함되어 있다.
class MyException extends Exception { MyException(String msg) { super(msg); } }
Exception클래스로부터 상속받아서 만들어진 MyException클래스에 필요에 따라 멤버변수나 메서드를 추가할 수도 있다.
Exception클래스는 생성 시에 String값을 받아서 메시지로 저장할 수 있는데, 이처럼 String을 매개변수로 받는 생성자를 추가하여 메시지를 저장할 수 있도록 만들었다.class MyException extends Exception { private final int ERR_CODE; MyException(String msg, int errCode) { // 생성자 super(msg); ERR_CODE = errCode; } MyException(String msg) { // 생성자 this(msg, 100); // ERR_CODE를 100(기본값)으로 초기화 } public int getErrCode() { // 에러 코드를 얻을 수 있는 메서드 return ERR_CODE; // getMessage()와 함께 사용될 것 } }
자바가 제공하는 기본 예외들
Arithmetic Exception
산술연산에서 예외 조건이 발생했을 때 발생
- 대표적인 / by zero
ArrayIndexOutOfBounds Exception
잘못된 인덱스로 Array에 엑세스 했을 경우 발생.
인덱스가 음수이거나 배열 크기보다 크거나 같을 때 발생
ClassNotFoundException
정의한 클래스를 찾을 수 없을 때 발생하는 예외
FileNotFoundException
파일에 엑세수 할 수 없거나 열리지 않을 경우 발생
IOException
입출력 작업이 실패하거나 중단될 때 발생
InterruptedException
Thread가 waiting, sleeping 또는 어던 처리를 하고 있을 떄 interrupt가 되면 발생하는 예외
NoSuchMethodException
찾을 수 없는 메서드에 엑세스 할 때 이 예외가 발생
NullPointerException
null 객체의 멤버를 참조할 때 발생
NumberFormatException
메서드가 문자열을 숫자 형식으로 변환할 수 없는 경우 발생
StringIndexOutOfBoundsException
문자열에 엑세스 하는 인덱스가 문자열보다 큰 경우거나
음수일 때 발생
참고.
www.notion.so/3565a9689f714638af34125cbb8abbe8
http://www.tcpschool.com/java/java_exception_class
leegicheol.github.io/whiteship-live-study/whiteship-live-study-09-exception-handling/
https://velog.io/@youngerjesus/자바-예외-처리
https://m.blog.naver.com/sthwin/221144722072, https://dzone.com/articles/implementing-custom-exceptions-in-java?fromrel=true
'공부일기 > 자바 스터디' 카테고리의 다른 글
Java 항해일지 11. Enum (0) 2021.01.26 Java 항해일지 10. 멀티스레드 프로그래밍 (0) 2021.01.24 Java 항해일지 - 8. 인터페이스 (0) 2021.01.08 Java 항해일지 - 7. 패키지 (0) 2020.12.28 Java 항해일지 - 6. 상속 (0) 2020.12.24