JAVA_예외 (Exception)

JW__1.7·2022년 8월 4일
0

JAVA 공부일지

목록 보기
20/30

Exception 클래스

  • 모든 예외 클래스의 슈퍼클래스
  • 명시적 예외 처리 여부에 따라 Checked Exception / Unchecked Exception으로 구분

  • 일반 예외의 경우 개발자가 반드시 예외처리를 직접 진행해야 한다. (예외처리를 하지 않으면 실행되지 않는다. 그래서 Checked Exception이라고 함)
  • 실행 예외의 경우 개발자가 예외처리를 직접하지 않아도 된다. RuntimeException을 상속 받는다. (강제적이지 않아서 Unchecked Exception이라고 함)
예외일반예외 (Checked Exception)실행예외(Unchecked Exception)
정의Exception의 상속받는 하위 클래스 중 RuntimeException을 제외한 모든 ExceptionRuntimeException을 상속받는 모든 Exception
처리 여부try-catch문 필수try-catch문 생략가능 (필요에 따라 처리)
확인 시점Compile Time 이미 컴파일 시점 에러가 표시되기 때문에 확인하여 예외처리 가능Runtime 컴파일 이후 런타임 도중 예외를 확인 (로직상으로는 생략이 가능하지만 미리 막아두는 것이 좋다.)
트랜잭션 여부예외 발생 시 롤백(rollback) 진행 X예외 발생 시 롤백(rollback) 진행
IOException, SQLException 등NullPointerException, NumberFormatException 등

Error와 Exception

  • Error
    • 시스템 레벨의 심각한 오류
    • 시스템 수정으로 문제를 해결
  • Exception
    • 프로그램 레벨의 일반적인 오류
    • 코드 수정으로 문제를 해결
    • 미리 예측하여 예외를 잡을 수 있다.

일반적으로 발생하는 문제는 Error가 아닌 Exception이다.

예외 (Exception)

  • 프로그램의 실행 중 발생하는 여러 가지 오류를 의미
  • 예외가 발생하면 프로그램이 강제로 종료 된다.
  • 예외가 발생하더라도 프로그램이 종료되지 않도록 예외처리를 해야 한다.
  • try-catch문을 이용해서 예외 발생으로 인해 프로그램이 종료되는 것을 회피할 수 있다.
  • 예외발생 예시

실행 예외 종류 (Unchecked Exception)

NullPointerException

  • 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();
		
}

반환값

취미가 나와 같군요

NumberFormatException

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

ArrayIndexOutOfBoundsException

배열에서 인덱스 범위를 초과하여 사용할 때 발생

ArithmeticException

산술 연산의 문제로 인해 발생 (정수 0으로 나누는 경우)

InputMismatchException

정수를 입력해야 하는데 문자나 실수를 입력할 때 발생 (데이터 타입을 다르게 입력할 때)

ArrayStoreException

객체배열에 배열 유형이 허락하지 않는 객체를 저장하려는 경우

ArrayIndexOutOfBoundsException

배열을 참조하는 인덱스가 잘못된 경우

ClassCastException

어떤 객체를 변환할 수 없는 클래스 타입으로 변환(Casting)하는 경우

NegativeArraySizeException

배열의 크기가 음수인 경우

NoClassDefFoundException

클래스를 찾을 수 없는 경우

OutOfMemoryException

사용 가능한 메모리가 없는 경우

IndexOutOfBoundsException

배열의 인덱스가 범위를 벗어난 경우

Illegalargumentexception

메서드에 유형이 일치하지 않는 매개변수를 전달하는 경우

IllegalMonitorStateException

스레드가 스레드에 속하지 않는 객체를 모니터 하려고 기다리는 경우

IllegalStateException

적절하지 않은 때에 매서드를 호출하는 경우

일반 예외 종류 (Checked Exception)

IOException

입출력 스트림과 관련한 명령어를 사용할 때 사용하는 예외처리
입출력 동작이 실패하는 경우에 발생

ClassNotFoundException

클래스 이름을 찾을 수 없는 경우

SQLException

데이터베이스 처리가 실패하는 경우

FileNotFoundException

파일을 찾을 수 없는 경우

NamingException

자원(Resource)의 이름을 확인할 수 없는 경우

예외처리

  • 모든 예외의 부모가 Exception 이다.
  • 자식-부모 순서로 배치해야 한다.

try ~ catch문

  • 예외를 처리할 때 사용하는 코드
  • 실행할 코드는 try 블록에 두고 예외를 처리할 코드는 catch 블록에 두는 방식
  • try 블록의 코드를 실행하다가 예외가 발생되면, 발생된 예외는 자동으로 catch 블록으로 전달
  • 모든 예외는 자바 클래스로 만들어져 있다.
try {
	실행코드
} catch(예외 타입 선언) {
	예외 처리 코드
}

다중 catch문

  • 하나의 try 블록에 여러 개의 catch 블록이 추가될 수 있다.
  • try 블록에서 발생된 예외는 우선 첫 번째 catch 블록을 방문
  • 첫 번째 catch 블록이 처리하지 못하면 다시 두 번째 catch 블록을 방문
  • 형식
try {
	예외 1이 발생할 가능성이 있는 코드
    예외 2가 발생할 가능성이 있는 코드
} catch(예외1 e) {
	예외1을 발생했을 때 처리할 코드
} catch(예외2 e) {
	예외2가 발생했을 때 처리할 코드
}
  • 예외 클래스들의 상속 관계를 파악한 뒤 catch 블록을 작성해야 한다.
  • 슈퍼클래스 타입인 Exception 이나 RuntimeException의 경우에는 마지막 catch 블록에서 처리

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 발생

finally

  • try-catch문 마지막에 추가할 수 있는 블록
  • 예외 발생 여부와 상관 없이 항상 마지막에 실행되는 블록
  • 필요 없는 경우 finally 블록은 생략 가능
  • 주로 어떤 자원을 반납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 블록 실행");
		}
		
	}

}

throw

  • 개발자가 직접 예외를 던지는 경우에 사용
  • 자바는 예외로 인식하지 못하지만, 실제로는 예외인 경우 개발자가 직접 예외를 발생시켜서 던진다.
  • 개발자가 직접 예외를 발생시킬 때는 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 블록 실행");
		}
		
	}

}

throws

  • 메소드에서 예외를 처리하는 방식
    • 메소드 내부에 try-catch문을 두고 직접 처리하는 방식
    • 메소드 외부로 예외를 던져서 메소드를 호출하는 곳에서 try-catch문으로 처리하는 방식
  • 메소드 외부로 예외를 던질 때 throws문을 이용해 던지는 예외를 명시
  • 2개 이상의 예외를 명시할 수 있기 때문에 throw가 아닌 throws라고 한다.

메소드 내부에서 예외 처리

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();
	}
		
}

0개의 댓글