A. 예외 🚨 Exception
A-1. 오류의 종류
- 시스템 에러 : Ex) 메모리 부족 (OOM - Out Of Memory)
- 수정이 매우 힘들거나 불가능
- 최적화나 성능 상향하는 방식으로 해결이 가능하기는 함
- 컴파일 에러
- 논리적인 오류
- 프로그램이 정상적으로 컴파일되고 실행/종료가 되지만, 원하는 결과가 나오지 않는 경우
- 예외(exception) : 프로그램 실행 중에 발생할 수 있는 비정상적인 상황 → 반드시 수정
- if - else 문장
- try-catch 문장
A-2. 예외 종류 & 해결 방법
- 참조형 객체 NullPointerException
public static void main(String[] args) {
// 예외종류 & 해결 방법
String s = null;
if (s != null) {
System.out.println("문자열 길이 : " + s.length());
} else {
System.out.println("null");
}
}+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- public static void main(String[] args) { // 예외종류 & 해결 방법 String s=null; System.out.println("문자열 길이 : " + s.length()); }
- 나누기 연산자를 사용할 때, 나누는 수가 0이면 ArithmeticException 발
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.itwill.exception01.ExceptionMain01.main(ExceptionMain01.java:24)
public static void main(String[] args) {
int x = 10;
int y = 0;
if (y != 0) {
System.out.println("몫 = " + (x / y));
} else {
System.out.println("0으로는 나눌 수 없습니다.");
}
}
- public static void main(String[] args) { int x = 10; int y = 0; System.out.println("몫 = " + (x / y)); }
B. 예외처리
B-1. try-catch-finally 구문
try {... 정상적인 상황에서 실행할 코드(블록) ...} // 1
catch (Exception 타입 변수 선언) { ... 예외 발생 시 실행할 코드들 ... } // 2
finally { ... 예외 발생 여부와 상관 없이 반드시 실행할 코드(블록)... } // 3
- 예외가 발생하지 않을 경우 : (1) ⇒ (3)
- 예외가 발생하는 경우 : (1) ⇒ 예외발생 : 해당 코드에서 break; ⇒ (2) ⇒ (3)
- try 내에 return 문장이 있더라도 finally 코드는 반드시 실행해야 함.
- try-catch 문장에서 catch 블록은 1개 이상이 있어야 함.
- finally 블록은 선택
- catch 블록은 처리하려고 하는 예외 종류에 따라서 여러개를 작성할 수 있음.
- catch 블록이 여러개 일 때는 하위 타입 에러들을 먼저 작성하고, 상위 타입의 에러들은 나중에 catch 해야 함.
- finally block : 예외 발생 여부와 상관없이 항상 실행되는 블록
- try 또는 catch 블록에 return 문장이 있어도, finally 블록이 실행된 후에 return 문장이 실행
🚨위에서 본 ArithmeticException을 try-catch구문으로 예외처리하기
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("x = ");
int x = Integer.parseInt(scanner.nextLine());
System.out.println("y = ");
int y = Integer.parseInt(scanner.nextLine());
int result = x / y;
System.out.println("result = " + result);
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.itwill.exception02.ExceptionMain02.main(ExceptionMain02.java:23)
- 예외처리 구문
- 위 구문에서 ArithmeticException 의 인스턴스인 경우만 catch하므로 다음과 같이 catch 구문을 더 넣어주는 방법이 있음.
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.println("x = ");
int x = Integer.parseInt(scanner.nextLine());
System.out.println("y = ");
int y = Integer.parseInt(scanner.nextLine());
int result = x / y;
System.out.println("result = " + result);
} catch (ArithmeticException e) {
System.out.println("y는 0이 될 수 없습니다.");
} catch (NumberFormatException e) {
System.out.println("x와 y는 정수로만 입력해야 합니다.");
}
}
사용자로부터 NumberFormatException 처리하기
package com.itwill.exception03;
import java.util.Scanner;
public class ExceptionMain {
Scanner scanner = new Scanner(System.in);
public ExceptionMain() {
// TODO Auto-generated constructor stub
}
static ExceptionMain app = new ExceptionMain();
public static void main(String[] args) {
int num = app.inputInteger();
System.out.println(num);
}
public int inputInteger() {
int result = 0;
while (true) {
try {
System.out.println("정수 입력 >> ");
result = Integer.parseInt(scanner.nextLine());
break;
} catch (NumberFormatException e) {
System.out.println(e.getClass());
}
}
return result;
}
public int inputInteger2() {
try {
int x = Integer.parseInt(scanner.nextLine());
return x;
} catch (Exception e) {
System.out.println("정수를 입력하세요!");
return inputInteger2();
}
}; // 재귀 함수 처리법
}
B-2. Multi-catch Block
- Java 7 버전부터 하나의 catch 구문에서 여러 개의 Exception 클래스를 선언하고 예외를 처리
- 문법
- 여러 클래스 타입의 예외를 하나의 변수를 선언하여 사용할 수 있음
- try { ... ; } catch (ExceptionClass1 | ExceptionClass2 | ... 변수 ) { ...; }
- 🚨주의 : multi-catch 블록에서는 상속 관계가 있는 예외 클래스들을 함께 사용할 수 앖음!
- 가능한 예
- catch (NumberFormatException | ArithmeticException e) {...;}
- 컴파일 에러 발생 예
- catch (NumberFormatException | Exception e) {...;}
public static void main(String[] args) {
try {
int x = 100;
int y = 7;
System.out.println("몫 = " + (x + y));
int[] arr = new int[0];
arr[0] = 123;
System.out.println(arr[0]);
} catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {
System.out.println("message : " + e.getMessage());
}
}
B-3. 선언문에서 사용하는 throws/ 발생한 에러를 던지는 throw
package com.itwill.exception06;
public class Calculator {
public int divide(int x, int y) throws Exception {
if (y != 0)
return x / y;
// 메서드를 호출한 곳에 값을 반환
throw new Exception("y는 0이될 수 없습니다.");
// 메서드를 호출한 곳에 예외를 던짐.
}
}
package com.itwill.exception06;
public class ExceptionMain06 {
public static void main(String[] args) {
// Calculator 타입 객체 생성
Calculator calculator =new Calculator();
// Calculator 타입 객체의 메서드 호출
int result = calculator.divide(100, 8);
}
}
- throws Exception을 사용하여 선언한 메서드를 메인에서 호출할 때, 해당 메서드는 예외가 발생할 수 있으므로 위와 같이 컴파일 에러가 남.
- Intelli J에서 이를 해결하기 위해 두 가지 대안을 주는데, Add throws declaration 과 Surround with try/catch 중 선택할 수 있음.
- 하지만 throws 선언을 메인에서 하게 되면 에러가 발생하는 순간 프로그램이 비정상적 종료를 하게 되므로 이에 주의해서 try catch 구문을 사용하는 것이 일반적.
예외 클래스들의 상속
예외 클래스들의 상속 관계 :
- Object
- Exception
- RuntimeException, IOException, …
- ArithmeticException, NullPointerException, NumberFormatException, …
- throws 선언문이 있는 메서드들 중에서 RuntimeException과 그 하위 예외 클래스들을 던지는(throw) 메서드들은 try-catch를 사용하지 않아도 컴파일 가능.
- RuntimeException이 아닌 예외를 던진다고 선언한 메서드들은 try-catch 구문이 필수가 아님