프로그램 에러 또는 오류
- 프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료되는 경우
- 발생시점에 따라 ‘컴파일 에러(compile-time error)’와 ‘런타임 에러(runtime error)’
- 이 외에도 ‘논리적 에러(logical error)’
컴파일 에러: 컴파일 시에 발생하는 에러
- 소스코드를 컴파일 하면 컴파일러가 소스코드(*.java)에 대해 오타나 잘못된 구문, 자료형 체크 등의 기본적인 검사를 수행하여 오류가 있는지 알려준다.
- 성공적으로 컴파일시 클래스 파일(*.class)이 생성된다. (생성된 클래스 파일 실행가능)
런타임 에러: 프로그램의 실행도중에 발생하는 에러
- 정상적으로 class파일이 생성되어도 컴파일러가 실행 도중에 발생할 수 있는 잠재적인 오류까지 검사하지 못한다. (실행 중에 에러에 의해서 잘못된 결과를 얻거나 프로그램이 비정상적으로 종료될 수 있다.)
논리적 에러: 실행은 되지만, 의도와 다르게 동작하는 것
Java에서는 실행 시(runtime) 발생할 수 있는 프로그램 오류를 ‘에러(error)’와 ‘예외(exception)’으로 구분한다.
- 에러(error): 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
- 메모리 부족(OutOfMemoryError), 스택오버플로우(StackOverflowError)와 같이 발생하면 복구할 수 없는 오류
- 예외(exception): 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
예외 클래스의 계층구조
Java에서 모든 클래스의 부모는 Object클래스 이므로 Exception과 Error클래스 역시 Object클래스의 자식이다.
모든 예외의 최고 부모는 Exception클래스이며, 모든 에러의 최고 부모는 Error클래스
예외 클래스들은 두 그룹으로 나눠질 수 있다.
- Exception: RuntimeException클래스와 그 자식들 제외한 나머지 클래스들
- RuntimeException: RuntimeException클래스와 그 자식들 다포함해서 말한다.
Exception과 RuntimeException
- Exception: 사용자의 실수, 주로 외적인 요인에 의해 발생하는 예외
- 보통 프로그램의 사용자들의 동작에 의해서 발생하는 경우가 많다.
- 존재하지 않는 파일의 이름을 입력(FileNotFoundException)
- 클래스의 이름을 잘못 적었을 때(ClassNotFoundException)
- 입력한 데이터 형식이 잘못되었을 때(DataFormatException)
- RuntimeException: 프로그래머의 실수로 발생하는 예외(Java의 프로그래밍 요소들과 관계가 깊다.)
- 배열의 범위를 벗어난 예외(ArrayIndexOutOfBoundsException)
- 값이 null인 참조변수의 멤버를 호출(NullPointException)
- 클래스간의 잘못된 형변환(ClassCastException)
- 정수를 0으로 나눈 예외(ArithmeticException)
예외 처리하기(Exception handling) - try, catch문
프로그램의 실행도중 발생하는 에러는 어쩔 수 없지만, 예외는 프로그래머가 이에 대한 처리를 미리 해주어야 한다.
예외처리(exception handling)
정의: 프로그램 실행시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것
목적: 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것
- 에러와 예외는 모두 실행 시(runtime) 발생하는 오류이다. (컴파일 에러 제외)
💡 발생한 예외를 처리하지 못하면, 프로그램은 비정상적으로 종료되며, 처리되지 못한 예외(uncaught exception)는 JVM의 ‘예외처리기(UncaughtExceptionHandler)’가 받아서 예외의 원인을 화면에 출력한다.
예외처리 하는 방법
try-catch문을 사용하자!
try {
//예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch (RuntimeException e1){
//RuntimeException 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
} catch (Exception e2){
//Exception 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
}
}
하나의 try블럭 다음에는 여러 종류의 예외를 처리할 수 있도록 하나 이상의 catch블럭이 올 수 있다.
- 발생하는 예외의 종류와 일치하는 단 한 개의 catch블럭만 수행된다.
- 발생한 예외의 종류와 일치하는 catch블럭이 없으면 예외는 처리되지 않는다.
💡 if문과 달리, try블러이나 catch블럭 내에 포함된 문장이 하나뿐이어도 괄호{}를 생략할 수 없다.
catch블럭은 괄호()와 블럭{} 두 부분으로 나눠져 있다.
- 괄호()내에는 처리하고자 하는 예외와 같은 타입의 참조변수를 선언해야 된다.
- 예외가 발생하면, 발생한 예외에 해당하는 클래스의 인스턴스가 생성된다.
- Error클래스를 제외한 모든 예외클래스는 Exception클래스의 자식이므로, catch블럭의 괄호()에 Exception클래스 타입의 참조변수를 선언해 놓으면 어떤 종류의 예외가 발생하더라도 catch블럭에 의해서 처리된다.
- 블럭{} 내에 문장들이 수행된다.
try블럭 내에서 예외가 발생한 경우
- 예외가 try블럭에 포함되어 있으면, 이 예외를 처리할 수 있는 catch블럭이 있는지 찾게 된다.
- 첫 번째 catch블럭부터 차례로 내려가면서 catch블럭의 괄호()내에 선언된 참조변수의 종류와 생성된 예외클래스의 인스턴스에 instanceof연산자를 이용해서 검사한다.
- 검사결과가 true인 catch블럭을 만날 때까지 검사는 계속된다.
- 일치하는 catch블럭을 찾게 되면, 그 catch블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다.
- 일치하는 catch블럭을 찾지 못하면, 예외는 처리되지 못한다. (catch문 블럭내 문장들 실행X)
- try문에서 예외가 발생한 경우 try문 예외가 발생한 다음문장들은 실행 안되고 catch문으로 가게 된다.
public static void main(String[] args) {
try {
System.out.println(1);
System.out.println(0/0);
System.out.println(2);
System.out.println(3);
} catch (ArithmeticException e2){
//Exception 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
System.out.println(4);
System.out.println(5);
}
System.out.println(6);
System.out.println(7);
}
//출력 1,4,5,6,7
try블럭 내에서 예외가 발생하지 않은 경우
- catch블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.
public static void main(String[] args) {
System.out.println(1);
try {
System.out.println(2);
System.out.println(0 / 0); //0으로 나눠서 ArithmeticException 발생시키기
System.out.println(3); //실행되지 않는다.
} catch (ArithmeticException ae) {
if(ae instanceof ArithmeticException)
System.out.println("true");
System.out.println(4);
} catch (Exception e) { //ArithmeticException을 제외한 모든 예외가 처리된다.
//Exception 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
System.out.println(4);
System.out.println(5);
}
System.out.println(6);
System.out.println(7);
}
/*
1
2
true
4
6
7
*/
에러가 중복되서 계속 처리되는 것이아니라 instanceof연산자를 통해서 catch블럭을 하나씩 차례대로 검사하고 첫 번째 검사에서 일치하는 catch블럭을 찾았기 때문에 두번째 catch블럭은 검사하지 않게 된다.
- try블럭 내에서 ArithmeticException이 아닌 다른 종류의 예외가 발생한 경우에는 두 번째 catch블럭인 Exception타입의 참조변수를 선언한 곳에서 처리
try {
System.out.println(2);
System.out.println(0 / 0); //0으로 나눠서 ArithmeticException 발생시키기
System.out.println(3); //실행되지 않는다.
catch (Exception e) { //ArithmeticException을 제외한 모든 예외가 처리된다.
//Exception 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
System.out.println(4);
System.out.println(5);
} /* catch (ArithmeticException ae) {
if(ae instanceof ArithmeticException)
System.out.println("true");
System.out.println(4);
}*/
/*
Exception은 최상위 부모로 ArithmeticException을 포함하기 때문에 ArithmeticException이
위에 catch블럭으로 올라와야 ArithmeticException 발생했을 때 확인하고 ArithmeticException
제외한 예외를 Exception이 처리할 것이다. (Exception보다는 발생할 수 있는 예외 확인하고 쪼개기)
"Exception 'java.lang.ArithmeticException' has already been caught"
*/
예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨져 있다. (중요!!)
- catch블럭의 괄호()에 선언된 참조변수를 통해서 이 인스턴스에 접근할 수 있다.
- catch블럭의 괄호()에 선언된 참조변수는 catch블럭 내에서만 사용 가능하다.
- 자주 사용되는 메서드 getMessage()와 printStackTrace()를 통해서 예외 정보들을 얻을 수 있다.
- printStackTrace():
- 예외발생 당시의 호출스택(call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력해준다.
- getMessage():
- 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.
try {
System.out.println(0 / 0); //0으로 나눠서 ArithmeticException 발생시키기
} catch (ArithmeticException ae) {
ae.printStackTrace();
System.out.println("예외 메시지 : "+ae.getMessage());
}
/* 출력
java.lang.ArithmeticException: / by zero
at Anonymous.main(Anonymous.java:10)
예외 메시지 : / by zero
*/
위 출력 결과를 보면 예외가 발생해서 비정상적으로 종료되었을 때의 결과와 비슷하지만 예외는 try-catch문에 의해 처리되었으며 프로그램은 정상 종료되었다.
- 예외가 발생해도 비정상적으로 종료하지 않도록 해주는 동시에 getMessage()와 printStackTrace()와 같은 메서드를 통해서 예외의 발생원인을 알 수 있다.
멀티 catch블럭
JDK1.7부터 여러 catch블럭을 ‘|’ 기호를 이용해서 하나의 catch블럭으로 합칠 수 있게 되었다.
- 중복된 코드를 줄일 수 있다.
- ‘|’ 기호(논리 연산자 아님)로 연결할 수 있는 예외 클래스의 개수에는 제한이 없다.
catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {
ae.printStackTrace();
System.out.println("예외 메시지 : "+ae.getMessage());
}
멀티 catch블럭 ‘|’ 기호로 연결된 예외 클래스가 부모와 자식의 관계가 있으면 컴파일 에러가 발생한다.
- catch (ArithmeticException | RuntimeException ae)
- Types in multi-catch must be disjoint: 'java.lang.ArithmeticException' is a subclass of 'java.lang.RuntimeException’
- 부모와 자식의 관계가 있으면 부모 클래스만 써주는 것과 똑같기 때문에 불필요한 코드를 제거하라는 의미
- 하나의 catch블럭으로 여러 예외를 처리하는 것이기 때문에 발생한 예외를 멀티 catch블럭으로 처리하게 되었을 때, 실제로 어떤 예외가 발생한 것인지 알 수 없다.
- 참조변수 e로 멀티 catch블럭에 ‘|’ 기호로 연결된 예외 클래스들의 공통 분모인 부모 예외 클래스에 선언된 멤버만 사용할 수 있다.
catch (ArithmeticException | ArrayIndexOutOfBoundsException ae) {
RuntimeException e1 = (IOException)ae; // 에러
RuntimeException e2 = (ClassCastException)ae; // 공통 분모라 가능
- ArithmeticException | ArrayIndexOutOfBoundsException → RuntimeException
- ClassCastException → RuntimeException
- IOException | RuntimeException → Exception
다음글
[java] 예외처리(2)
예외 발생시키기 throw를 사용해서 프로그래머가 고의로 예외를 발생시킬 수 있다. 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 다음 Exception e = new Exception(”고의로 발생시킴
uhanuu.tistory.com
이미지 참조
01. [자바] 프로그램 오류, Exception 클래스의 계층구조
프로그램 오류 프로그램이 어떤 원인으로 인해 오작동, 비정상적으로 종료되는 경우가 있는데 이것을 프로그램 에러 또는 에러라고 한다. 이런 에러를 발생시점에 따라 '컴파일 에러', '런타임
staticclass.tistory.com
'책 > Java의 정석' 카테고리의 다른 글
[java] 예외처리(3) (0) | 2023.02.07 |
---|---|
[java] 예외처리(2) (0) | 2023.02.07 |
[java] instanceof 연산자 (0) | 2023.02.07 |
[java] 내부 클래스(inner class)와 익명 클래스(anonymous class) (0) | 2023.02.04 |
[java] 인터페이스 && default 메서드와 static 메서드 (0) | 2023.02.04 |