Exception 클래스
슈퍼클래스
Checked Exception
/ Unchecked Exception
으로 구분RuntimeException
을 상속 받는다. (강제적이지 않아서 Unchecked Exception이라고 함)예외 | 일반예외 (Checked Exception) | 실행예외(Unchecked Exception) |
---|---|---|
정의 | Exception의 상속받는 하위 클래스 중 RuntimeException을 제외한 모든 Exception | RuntimeException을 상속받는 모든 Exception |
처리 여부 | try-catch문 필수 | try-catch문 생략가능 (필요에 따라 처리) |
확인 시점 | Compile Time 이미 컴파일 시점 에러가 표시되기 때문에 확인하여 예외처리 가능 | Runtime 컴파일 이후 런타임 도중 예외를 확인 (로직상으로는 생략이 가능하지만 미리 막아두는 것이 좋다.) |
트랜잭션 여부 | 예외 발생 시 롤백(rollback) 진행 X | 예외 발생 시 롤백(rollback) 진행 |
IOException, SQLException 등 | NullPointerException, NumberFormatException 등 |
- Error
- 시스템 레벨의 심각한 오류
- 시스템 수정으로 문제를 해결
- Exception
- 프로그램 레벨의 일반적인 오류
- 코드 수정으로 문제를 해결
- 미리 예측하여 예외를 잡을 수 있다.
일반적으로 발생하는 문제는 Error가 아닌 Exception
이다.
try-catch문
을 이용해서 예외 발생으로 인해 프로그램이 종료되는 것을 회피할 수 있다.
- null값을 갖는 참조변수로 객체 접근 연산자인 도트(.)를 사용했을 때 발생
- 객체 참조가 없는 상태일 때 발생
- 객체가 없는 상태에서 객체를 사용할 때 발생
public class Main {
// static은 static만 부를 수 있다.
// non-static은 아직 만들어진 상태가 아니라서 호출이 안된다.
public static void m1() {
// NullPointerException : null값이 어떤 메소드를 호출할 때 발생
String[] hobbies = new String[5];
// hobbies[0]은 값을 할당하지 않아서 null 값
hobbies[1] = "수영";
hobbies[2] = "골프";
hobbies[3] = "영화";
hobbies[4] = "집콕";
for(int i = 0; i < hobbies.length; i++) {
if(hobbies[i].equals("수영")) {
System.out.println("취미가 나와 같군요");
}
}
}
}
public static void main(String[] args) {
m1();
}
hobbies[0]
이 null값
이라서 실행하면 아래와 같이 오류가 뜬다
NullPointerException 회피
public class Main {
public static void m2() {
String[] hobbies = new String[5];
hobbies[1] = "수영";
hobbies[2] = "골프";
hobbies[3] = "영화";
hobbies[4] = "집콕";
for(int i = 0; i < hobbies.length; i++) {
if(hobbies[i] != null && hobbies[i].equals("수영")) {
// if(hobbies[i].equals("수영") && hobbies[i] != null) { // 잘못된 코드 조합이다.
System.out.println("취미가 나와 같군요");
}
}
}
}
public static void main(String[] args) {
m2();
}
반환값
취미가 나와 같군요
String을 Number타입으로 변환하지 못하는 경우
문자열인 데이터를 숫자로 변경하는 경우가 많은데, 매개값인 문자열이 숫자로 변환될 수 있다면 숫자를 정상적으로 리턴하지만, 숫자로 변환할 수 없는 문자열이 포함되어 있으면 발생
import java.util.Scanner;
public class Main {
public static void m3() {
// NumberFormatException : String을 Number타입으로 변경할 때 발생
Scanner sc = new Scanner(System.in);
System.out.println("이름 입력(필수) >>> ");
String name = sc.nextLine() ;
System.out.println("나이 입력(선택) >>> ");
String strAge = sc.nextLine(); // 입력 없이 Enter만 누르면 strAge는 빈 문자열을 가짐
int age = Integer.parseInt(strAge);
System.out.println("이름 : " + name + ", 나이 : " + age + "살");
}
}
public static void main(String[] args) {
m3();
}
이름만 입력하고, 나이 입력할 때 공백으로 하면 아래와 같이 오류 난다.
NumberFormatException 회피
import java.util.Scanner;
public class Main {
public static void m4() {
Scanner sc = new Scanner(System.in);
System.out.println("이름 입력(필수) >>> ");
String name = sc.nextLine() ;
System.out.println("나이 입력(선택) >>> ");
String strAge = sc.nextLine(); // 입력 없이 Enter만 누르면 strAge는 빈 문자열을 가짐
int age;
if(strAge.isEmpty()) {
age = 0;
} else {
age = Integer.parseInt(strAge);
}
System.out.println("이름 : " + name + ", 나이 : " + age + "살");
}
}
public static void main(String[] args) {
m4();
}
반환값
이름 입력(필수) >>>
박은빈
나이 입력(선택) >>>
이름 : 박은빈, 나이 : 0살
배열에서 인덱스 범위를 초과하여 사용할 때 발생
산술 연산의 문제로 인해 발생 (정수 0으로 나누는 경우)
정수를 입력해야 하는데 문자나 실수를 입력할 때 발생 (데이터 타입을 다르게 입력할 때)
객체배열에 배열 유형이 허락하지 않는 객체를 저장하려는 경우
배열을 참조하는 인덱스가 잘못된 경우
어떤 객체를 변환할 수 없는 클래스 타입으로 변환(Casting)하는 경우
배열의 크기가 음수인 경우
클래스를 찾을 수 없는 경우
사용 가능한 메모리가 없는 경우
배열의 인덱스가 범위를 벗어난 경우
메서드에 유형이 일치하지 않는 매개변수를 전달하는 경우
스레드가 스레드에 속하지 않는 객체를 모니터 하려고 기다리는 경우
적절하지 않은 때에 매서드를 호출하는 경우
입출력 스트림과 관련한 명령어를 사용할 때 사용하는 예외처리
입출력 동작이 실패하는 경우에 발생
클래스 이름을 찾을 수 없는 경우
데이터베이스 처리가 실패하는 경우
파일을 찾을 수 없는 경우
자원(Resource)의 이름을 확인할 수 없는 경우
- 모든 예외의 부모가
Exception
이다.자식-부모
순서로 배치해야 한다.
try {
실행코드
} catch(예외 타입 선언) {
예외 처리 코드
}
여러 개의 catch 블록
이 추가될 수 있다.try {
예외 1이 발생할 가능성이 있는 코드
예외 2가 발생할 가능성이 있는 코드
} catch(예외1 e) {
예외1을 발생했을 때 처리할 코드
} catch(예외2 e) {
예외2가 발생했을 때 처리할 코드
}
NullPointerException과 try-catch문
public class Main {
public static void m1() {
try {
String[] hobbies = new String[3];
hobbies[1] = "swimming";
hobbies[2] = "running";
for(String hobby : hobbies) {
System.out.println(hobby.substring(0, 2));
}
} catch(Exception e) { // RunTimeException, NullPointerException 가능
System.out.println("NullPointerException 발생");
}
}
}
public static void main(String[] args) {
m1();
}
NullPointerException 회피
public class Main {
public static void m2() {
try {
String input = "20,21,,22,23,24,25";
String[] inputs = input.split(",");
int[] ages = new int[inputs.length];
for(int i = 0; i < inputs.length; i++) {
ages[i] = Integer.parseInt(inputs[i]);
System.out.println("변환값 : " + ages[i]);
}
} catch(NumberFormatException e) { // RunTimeException, Exception 가능
System.out.println("NumberFormatException 발생");
} catch(Exception e) {
System.out.println("Exception 발생");
}
}
}
public static void main(String[] args) {
m2();
}
콤마(,) 기준에 분리하면 21 다음은 공백이 생기기 때문에 NumberFormatException 발생한다.
반환값
변환값 : 20
변환값 : 21
NumberFormatException 발생
close
할 때 사용try {
실행 코드
} catch(예외클래스 e) {
예외 처리 코드
} finally {
무조건 실행되는 코드
}
아래 코드는 Scanner를 닫아주는 것을 표현했다.
실제로 Scanner는 close()
처리로 닫아주지 않아도 정상적으로 잘 실행이 되지만, 다른 메소드 중에서 close()
처리를 안해주면 데이터를 손실낼 수 있기 때문에 주의해야 한다.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try {
System.out.println("나이 입력 >>> "); // null이나 다른 데이터 타입 입력하면 예외 발생
String strAge = sc.nextLine(); // 여기서 오류나면 sc.close()하지 못하니 finally 작성
int age = Integer.parseInt(strAge);
System.out.println(age >= 20 ? "성인" : "미성년자");
} catch (Exception e) {
System.out.println("예외 발생");
} finally {
sc.close(); // 실제로 finally는 자원을 반납할 때 주로 사용
System.out.println("finally 블록 실행");
}
}
}
RuntimeException
을 사용하는 경우가 일반적아래 2줄은 암기해라.
Exception e = new RuntimeException("이 나이는 잘못 되었습니다.");
System.out.println(e.getMessage());
나이에 따른 신분 출력
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try {
System.out.println("나이 입력 >>> ");
String strAge = sc.nextLine(); // 여기서 오류나면 sc.close()하지 못하니 finally 작성
int age = Integer.parseInt(strAge);
if(age < 0 || age > 100) {
throw new RuntimeException("나이는 0 이상 100 이하만 가능합니다.");
} // throw가 던진 걸 catch가 받는다. String message는 객체 e에 들어있다.
System.out.println(age >= 20 ? "성인" : "미성년자");
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
sc.close(); // 실제로 finally는 자원을 반납할 때 주로 사용
System.out.println("finally 블록 실행");
}
}
}
메소드 내부에서 예외 처리
public class ThrowEx {
public static void method() {
try{
메소드 내부 코드
} catch {
예외 처리 코드
}
}
public static void main(String[] args) {
method();
}
}
메소드 외부로 예외를 던지고, 메소드 호출 영역에서 예외 처리
public class ThrowEx {
public static void method() throws Exception {
메소드 내부 코드
}
public static void main(String[] args) {
try{
method();
} catch {
예외 처리 코드
}
}
}
InputMismatchException, InputMismatchException 예외 2개
public class Car {
}
import java.util.InputMismatchException;
import java.util.Scanner;
public class ParkingLot {
private Car[] cars;
private int idx;
private Scanner sc;
public ParkingLot() {
cars = new Car[10];
sc = new Scanner(System.in);
}
public void addCar() throws RuntimeException {
if(idx == cars.length) {
throw new RuntimeException("FULL");
}
}
public void deleteCar() throws RuntimeException {
if(idx == 0) {
throw new RuntimeException("EMPTY");
}
}
public void findCar() throws RuntimeException {
if(idx == 0) {
throw new RuntimeException("EMPTY");
}
}
public void printAllCars() throws RuntimeException {
if(idx == 0) {
throw new RuntimeException("EMPTY");
}
}
public void manage() {
while(true) {
try {
System.out.print("1.추가 2.제거 3.조회 4.전체목록 0.종료 >>> ");
int choice = sc.nextInt();
switch(choice) {
case 1: addCar(); break;
case 2: deleteCar(); break;
case 3: findCar(); break;
case 4: printAllCars(); break;
case 0: return;
default: throw new RuntimeException("Bad Request");
}
} catch (InputMismatchException e) {
sc.next();
System.out.println("처리 명령은 정수(1 ~ 4, 0)입니다.");
} catch (RuntimeException e) {
System.out.println(e.getMessage());
}
}
}
public static void main(String[] args) {
new ParkingLot().manage();
}
}