위로 아래

예외처리

잘못된 값 입력 등으로 프로그램이 비정상 종료되지 않도록, 미리 조건을 걸어주는 것.

오류 (error)

  1. 컴파일 에러(compile error) : 문법적 오류가 있어서 컴파일 과정에서 에러가 나는 경우
  2. 런타임 에러 (runtime error) : 문법적 오류는 없는데 사용자의 입력에 따라 에러가 나는 경우
    1. 예측 불가능한 에러 (시스템 에러) : 메모리 부족, 운영체제 에러 등의 경우
    2. 예측 가능한 에러 : 예측 가능한 런타임 에러를 '예외'라고 부른다.

 

예외 (exception)

  1. 예측 가능한 런타임 에러.
  2. java.lang 패키지에 있는 Throwable 클래스의 자식 객체
  3. 컴파일러는 일반 예외만 잡아준다. 실행예외는 개발자가 코드로 잡아주어야 한다.

 

 


예외 종류

실행 예외(unckecked exception)

예외처리를 하지 않아도 컴파일할 수 있는 비검사형 예외 

  1. ArithmeticException : 0으로 나누기와 같은 부적절한 산술 연산을 수행할 때 발생
  2. ArrayIndexOutOfBoundsException : 배열에서 인덱스 범위를 초과할 때 발생
  3. NullPointerException : null값을 가진 참조 변수에 접근할 때 발생
  4. NumberFormatException : 숫자로 바꿀 수 없는 문자열을 숫자로 변환하려할 때 발생
  5. IllegalArgumentException : 메서드에 부적절한 매개변수를 전달할 때 발생
  6. IndexOutOfBoundException : 배열에서 범위를 벗어난 인덱스를 사용할 때 발생
  7. NoSuchElementException : 요구한 요소가 없을 때 발생

 

 

일반 예외(checked exception)

예외처리를 하지 않으면 컴파일 오류가 발생하는 검사형 예외 

  1. ClassNotFoundException : 존재하지 않는 클래스를 사용하려할 때 발생
  2. InterruptedException : 인터럽트 되었을 때 발생
  3. NoSuchFieldException : 클래스가 명시한 빌드를 포함하지 않을 때 발생
  4. CoSuchMethodException : 클래스가 명시한 메소드를 포함하지 않을 때 발생
  5. IOException : 데이터 읽기 쓰기 같은 입출력 문제가 있을 때 발생

 

risk

  1. 에러가 날 위험이 있는 코드는 리스크가 있다고 한다.
  2. 리스크는 미연에 방지할 수 있기 때문에, 리스크가 있는 코드는 좋지 않다.

 

 

실행 예외 예시

더보기
import java.util.StringTokenizer;

public class Unchecked1Demo {
	public static void main(String[]args) {
		String s = "Time is money";
		StringTokenizer st = new StringTokenizer(s);
		
		while(st.hasMoreTokens()) {
			System.out.println(st.nextToken());
		}
		System.out.println(st.nextToken());   // 더이상 나올 토크이 없는데 토큰 호출
	}
}
public class Unchecked2Demo {
	public static void main(String[]args) {
		int[] arr = {0,1,2};
		System.out.println(arr[3]);   // 존재하지 않는 인덱스의 배열 호출
	}
}

일반 예외 예시

더보기
public class CheckedDemo {
	public static void main(String[]args) {
		Thread.sleep(100);   // 아예 컴파일이 안 됨
	}
}

 

 

 


Try~catch문

Try{
	예외가 발생할 수 있는 실행문;
} catch (예외클래스1 | 예외클래스2 참조변수){
	핸들러;
} finally {
	예외 발생 여부와 관계 없이 수행할 실행문;
}
  1. 예외 객체를 이용
  2. 핸들러 : catch 블록에 포함된 예외 처리 코드
  3. 더 상위의 예외 객체일 경우 더 밑으로 가야한다. 

 

Throwable 클래스의 메소드

getMessage()   // Throwable 객체의 자세한 메시지를 반환
toString()   // Throwable 객체의 간단한 베시지를 반환
printStackTrace()   // Throwable 객체와 추적 정보를 화면에 출력

 

 

예시

더보기
public class TryCatch1Demo {
	public static void main(String[]agrs) {
		int[] arr = {0,1,2};
		
		try {
			System.out.println("배열의 첫번째 원소 : " + arr[0]);
			System.out.println("배열의 마지막 원소 : " + arr[3]);
			System.out.println("배열의 중간 원소 : " + arr[1]);   // 위에서 예외가 발생해서 실행되지 않는다.
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("원소가 존재하지 않습니다.");
		}finally {
			System.out.println("어이쿠!");
		}
	}
}
public class TryCatch2Demo {
	public static void main(String[] args) {
		int dividend = 10;
		try {
			int divisor = Integer.parseInt(args[0]);
			System.out.println(dividend / divisor);   // 10을 args에 입력받은 숫자로 나누기
		} catch (ArrayIndexOutOfBoundsException e) {   // args에 아무것도 입력하지 않았을 경우
			System.out.println("원소가 존재하지 않습니다.");
		} catch (NumberFormatException e) {   // args에 문자를 넣었을 경우
			System.out.println("숫자가 아닙니다.");
		} catch (ArithmeticException e) {   // args에 0을 넣었을 경우
			System.out.println("0으로 나눌 수 없습니다.");
		} catch (Exception e) {    // 최상위 예외 객체라 맨 밑으로 와야 한다.
			System.out.println("몰라"+e.getMessage());
		} finally {   // 예외 유무와 상과 없이 실행
			System.out.println("항상 실행됩니다.");
		}
		System.out.println("종료.");
	}
}

 

 

 


Try ~ with ~ resource문

try (자원) {
} catch (...) {
}
  1. try 블록에서 자원을 사용했다면, try 블록 실행 후 자원을 닫아주어야 메모리 낭비가 없다.
  2. 자바 7부터는, 자원에 AutoCloseable 인터페이스를 구현시키면, 예외 발생 여부와 상관 없이 자동으로 닫아준다.

예시

더보기
public class TryCatch4Demo {
	public static void main(String[] args) {
		Resource resource = new Resource();
		
		try(resource){
			resource.show();
		} catch (Exception e) {
			System.out.println("예외 처리");
		}
	}
}

class Resource implements AutoCloseable {
	void show() {
		System.out.println("자원 사용");
	}
	
	public void close() throws Exception {
		System.out.println("자원 닫기");
	}
}

 

 


throws 문

throws

  1. 예외를 상위 코드 블록으로 양도해서 떠넘긴다
  2. 예외를 한 곳에 다 몰아놓고 한 번에 해결하도록 할 수 있다
  3. 하위 메소드가 상위로 떠넘겨서 main 메소드를 넘어 JVM까지 떠넘긴다.

 

예시

더보기
import java.util.Scanner;

public class ThrowsDemo {
	public static void main(String[] args) {
		Scanner in = new Scanner (System.in);
		System.out.print("정수 입력 : ");
		try {
			square(in.nextLine());
		} catch(NumberFormatException e) {
			System.out.println("정수가 아닙니다.");
		}
	}
	
	private static void square(String s) throws NumberFormatException {
		// 예외가 발생하면 자신을 호출한 곳으로 예외 처리를 떠넘긴다.
		// throws 문이 없었다면 여기서 try catch문을 사용했어야 했다. 그럼 코드가 지저분해진다.
		int n = Integer.parseInt(s);
		System.out.println(n*n);
	}
}