예외처리(exception handling)
프로그램 오류
컴파일 에러(compile-time error) | 컴파일 할 때 발생하는 에러(고치기 전까지 실행 xx) |
런타임 에러(runtime error) | 실행 할 때 발생하는 에러(컴파일 실행, 프로그램 종료) |
논리적 에러(logical error) | 작성 의도와 다르게 동작(프로그램이 종료 xx) |
컴파일은 잘되었어도 실행 중에 에러에 의해서 잘못된 결과를 얻거나 비정상적으로 프로그램이 종료될 수 있다.
문법은 맞지만 JVM이 코드를 실행할 때 에러를 발생 -> 런타임 에러
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
system cannot be resolved
Java의 런타임 에러
에러(error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
예외(exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
-> 실행할 때 생기는 에러는 어쩔 수 없지만, 예외는 처리하자!
예외처리의 정의와 목적
정의: 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것
목적: 프로그램 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것
예외클래스의 계층 구조
-> 모든 클래스의 조상은 Object클래스이므로 Exception과 Error클래스도 Object 클래스의 자손들
-> Exception과 Error는 Throwable의 자손들이기도 하다!
-> 모든 예외의 최고 조상은 Exception클래스이며, 상속계층도를 Exception클래스부터 도식화하면 다음과 같다.
Exception클래스(와 자손클래스)들 | RuntimeException(와 자손클래스)들 |
사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외 | 프로그래머의 실수로 발생하는 예외 |
IOException: 입출력예외(Input, Output) |
ArithmeticException: 산술계산(정수를 0으로 나눔) |
ClassNotFoundException: 클래스의 이름을 잘못 적음 (클래스가 존재 xx) |
ClassCastException: 클래스 간의 형변환을 잘못함 |
FileNotFoundException: 존재하지 않는 파일의 이름을 입력함 | NullPointerException: 값이 null인 참조변수의 멤버를 호출( String str = null; -> str.length xx) |
IndexOutOfBoundsException: 배열의 범위를 벗어남 |
예외 처리하기. try-catch 문
-> 예외를 처리하기 위해서 try-catch문을 사용
-> if문과 달리 try 블록이나 catch 블록 내에 포함된 문장이 하나여도 괄호{}를 생략할 수 없음
-> catch 블록은 괄호( )와 블록 { } 두 부분으로 나눠져 있는데, 괄호( )내에는 처리하고자 하는 예외와 같은 타입의 참조변수 하나를 선언, 블록 { }내에는 처리하고자 하는 예외가 발생한 경우에 나타나는 문장
-> 예외가 발생한 문장이 try 구문안에 있다면, 이 예외를 처리할 수 있는 catch블록을 찾게 된다.
-> 발생한 예외의 종류와 일치하는 catch 블럭이 없으면 예외는 처리되지 않는다.
-> 예외가 발생하면, 발생한 예외에 해당하는 클래스의 인스턴스가 만들어진다. 예) ArithmeticException이 발생하면 ArithmeticException인스턴스가 생성된다. 참조변수는 a ( catch ( ArithmeticException a ) { } )
예제1) 변수 number에 저장되어 있는 값 100을 0~9사이의 임의의 정수로 나눈 결과를 출력하는 일(10번 반복)
public class Ex8_1 {
public static void main(String[] args) {
int number = 100;
int result = 0;
for(int i=0;i<10;i++) {
result = number / (int)(Math.random()*10); // 9번째 라인
System.out.println(result);
}
}
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Ex8_1.main(Ex8_1.java:9)
- random을 사용했기 때문에 결과는 다르지만 만약 10번 for문을 돌렸을 때 0이 나오지 않는다면 오류가 안남 하지만 그럴 확률은 적기 때문에 예외가 발생한다. 예외가 발생하는 이유는 정수를 0으로 나누려 했기 때문에!
- 이 메시지를 보면 ArithmeticException이 나타는 것을 알 수 있고, 발생위치는 main메서드의 9번째 라인이라는 것을 알 수 있다.
예제1-2) 변수 number에 저장되어 있는 값 100을 0~9사이의 임의의 정수로 나눈 결과를 출력하는 일(10번 반복)
public class Ex8_1 {
public static void main(String[] args) {
int number = 100;
int result = 0;
for(int i=0;i<10;i++) {
try {
result = number / (int)(Math.random()*10);
System.out.printf("%d ", result);
} catch (ArithmeticException a) {
System.out.printf("%s ", "예외처리성공"); // ArithmeticExcpetion이 발생되면 실행되는 코드
} // try-catch문의 끝
}//for문의 끝
}//main의 끝
}
<출력값>
난수에 0이 없을 시 | 50 16 100 14 33 12 16 14 25 25 |
난수에 0이 있을 시 | 16 예외처리성공 33 33 예외처리성공 14 50 12 33 100 |
try-catch문에서의 흐름
1. try블럭 내에서 예외가 발생한 경우.
1) 발생한 예외와 일치하는 catch블록이 있는지 확인한다.
2) 일치하는 catch블록을 찾게 되면, 그 catch블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다. 만일 일치하는 catch블록을 찾지 못하면 예외는 처리되지 못한다.
(1) System.out.println(1)이 먼저 출력
(2) System.out.println(0/0) 예외가 발생 -> System.out.println(2) 출력 안됨
(3) Catch문으로 가서 발생한 예외와 같은 타입인지 체크
(4) 같은 타입이라서 Catch문 안의 System.out.println(3) 출력 -> 예외처리!
(5) Catch문을 빠져나와서 System.out.println(4) 출력
-> try 블록에서 예외가 발생하면, 예외가 발생한 위치 이후에 있는 try 블록의 문장들은 수행되지 않기 때문에 try블록에 포함시킬 코드의 범위를 잘 선택해야 함
2. try블럭 내에서 예외가 발생하지 않은 경우.
1) catch블록을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.
(1) System.out.println(1)이 먼저 출력
(2) try구문으로 들어가서 System.out.println(2) 출력, System.out.println(3) 출력
(3) 예외처리가 발생하지 않았기 때문에 Catch문을 건너 뛰어서 System.out.println(4)는 출력 안됨
(4) try문을 빠져나와서 System.out.println(5) 출력
예외의 발생과 catch블럭
- 예외가 발생하면, 이를 처리할 catch블록을 찾아 내려감
- 일치하는 catch블록이 없으면, 예외는 처리 안됨. Exception이 선언된 catch블록은 모든 예외처리가능함(Exception은 모든 예외의 조상이기때문) 그래서 되도록이면 마지막 catch블럭으로 사용!!
예제1) 예외가 발생되어서 첫 번째 구문에서 예외처리!
public class Ex8_2 {
public static void main(String[] args) {
System.out.printf("%d ",1);
System.out.printf("%d ",2);
try{
System.out.printf("%d\n",3);
System.out.println(0/0); // 예외 발생
System.out.println(4); // 실행되지 않는다.
} catch (ArithmeticException ae) { // 예외 발생해서 첫 번째 catch블럭으로 이동
if (ae instanceof ArithmeticException) // ae가 ArithmeticException의 객체라면?
System.out.println("true"); // true
System.out.println("ArithmeticException");
} catch (Exception e) { // 첫 번째 catch 블럭에서 예외 처리 되었기 때문에 실행 안됨
System.out.println("Exception");
} // try-catch의 끝
System.out.println(6);
}// main의 끝
}
예제2) 첫 번쨰 catch문에서 예외처리가 안된 경우(발생한 예외와 다른 예외여서)
public class Ex8_2 {
public static void main(String[] args) {
System.out.println(1);
System.out.println(2);
try {
System.out.println(3);
System.out.println(args[0]); // 예외 발생(ArrayIndexOutOfBoundsException e)
System.out.println(4); // 실행되지 않는다.
} catch (ArithmeticException ae) { // 예외 발생해서 첫 번째 catch블럭으로 이동
if (ae instanceof ArithmeticException) // 예외 처리 안됨(발생한 예외와 달라서)
System.out.println("true");
System.out.println("ArithmeticException");
} catch (Exception e) { // 첫 번째 catch 블럭에서 예외 처리 안되어서 두 번째 catch에서 예외처리됨
System.out.println("Exception"); // Exception은 모든 예외의 조상이라 가능!
} // try-catch의 끝
System.out.println(6);
}// main의 끝
}
두 번째 catch 구문에서 catch (ArrayIndexOutOfBoundsException a) { } 으로 변경가능!
Exception은 모든 예외의 조상이기 때문에 예외처리 가능
-> 이처럼 try-catch 문의 마지막에 Exception클래스 타입의 참조변수를 선언한 catch 블록을 사용하면, 어떤 종류의 예외가 발생하더라도 이 catch블록에 의해 처리되도록 할 수 있음
printStackTrace( )와 getMessage( )
- 예외가 발생했을 때 생성되는 예외 클래스의 객체에는 발생한 예외에 대한 정보가 담겨있음
-> getMessage( )와 printStackTrace( )메서드를 통해서 이 정보들을 얻어올 수 있음(자주 사용되는 메서드)
printStackTrace( ) | getMessage( ) |
예외발생 당시의 호출스택(call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력 | 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있음 |
public class ExceptionTest {
public static void main(String[] args) {
System.out.println(1);
System.out.println(2);
try {
System.out.println(3);
System.out.println(0/0); // 예외 발생!!
System.out.println(4); // 실행되지 않음
} catch (ArithmeticException ar) {
ar.printStackTrace(); // 참조변수 ar를 통해, 생성된 ArithmeticExcpetion인스턴스에 접근 가능
System.out.println("예외메시지: " + ar.getMessage());
} // try-catch문의 끝
} // main메서드의 끝
}
<출력값>
1
2
3
java.lang.ArithmeticException: / by zero
at ExceptionTest.main(ExceptionTest.java:28) -> printStackTracge( )
예외메시지: / by zero
-> try-catch문으로 예외처리를 해서 예외가 발생해도 비정상적으로 종료하지 않도록 해주는 동시에, printStackTrace( ) 또는 getMessage( )와 같은 메서드를 통해서 예외의 발생원인을 알 수 있다!
멀티 catch블럭
- 내용이 같은 catch블록을 하나로 합친 것(JDK1.7부터)
-> 코드의 중복 제거
예외사항
1) ‘ | ‘ 기호로 연결된 예외 클래스가 조상과 자손의 관계라면 컴파일 에러 발생!
try {
...
} catch (ParentException | ChildExcpetion e) { // 에러!
e.printStackTrace();
}
-> 두 예외 클래스가 조상과 자손의 관계면, 아래코드 처럼 조상 클래스만 써주는 것과 똑같음
try {
...
} catch (ParentException e) {
e.printStackTrace();
}
2) 참조변수로 멀티 catch블록에 ‘|’ 기호로 연결된 예외 클래스들의 공통분모인 조상 예외 클래스에 선언된 멤버만 사용할 수 있음
try {
...
} catch (ExceptionA | ExceptionB e) {
e.methodA(); // 에러! ExceptionA에 선언된 methodA()는 호출 불가(Excpetion B가 들어올 수도 있어서)
// 만약 methodA를 사용하고 싶으면 형변환을 해야함!
} if(e instanceof ExceptionA) {
ExceptionA e1 = (ExceptionA)e;
e1.method1(); // OK! ExceptionA에 선언된 메서드 호출 가능
} else { // if(e instanceof ExceptionB)
...
}
-> ExceptionA와 ExcpetionB의 공통멤버만 사용 가능
-> 하지만 이렇게 주로 하지는 않음!
'멀티캠퍼스 풀스택 과정 > Java의 정석' 카테고리의 다른 글
자바의 정석5-3 사용자 정의 예외, 예외 되던지기, 연결된 예외 (0) | 2022.01.06 |
---|---|
자바의 정석5-2 예외 발생, 예외 처리, Finally블럭 (0) | 2022.01.06 |
자바의 정석4-3 내부클래스와 익명클래스 (0) | 2022.01.06 |
자바의 정석4-2 인터페이스(interface), 디폴트와 static 메서드 (0) | 2022.01.05 |
자바의 정석4-1 추상 클래스(abstract class)와 추상메서드 (0) | 2022.01.05 |