Skip to content

Latest commit

 

History

History
298 lines (257 loc) · 12.9 KB

10장.md

File metadata and controls

298 lines (257 loc) · 12.9 KB

10. 예외처리

10.1 예외와 예외 클래스

자바의 에러란?

  • 컴퓨터 하드웨어의 오동작/고장으로 인해 응용프로그램 실행 오류가 발생하는 것
  • JVM 실행에 문제가 생겼다.
  • 실행 불능

개발자는 이런 에러를 대처할 방법이 없다. 그러나 예외(exception) 라고 부르는 오류가 있다.

예외란?

  • 사용자의 잘못된 조작/개발자의 잘못된 코등으로 발생하는 프로그램 오류
  • 예외가 발생되면 프로그램이 종료된다.
  • 그러나 예외 처리를 통해 프로그램을 종료하지 않고 정상 실행 상태가 유지될 수 있도록 할 수 있다.

예외의 종류

  • 일반 예외(Exeption) 컴파일러 예외라고도 한다. 자바 소스 컴파일 과정에서 예외 처리 코드가 필요한지 검사한다. Exeption을 상속 받지만, Runtime Exeption은 상속받지 않음

  • 실행 예외(Runtime Exception) 컴파일하는 과정에서 예외 처리 코드를 검사하지 않는 예외를 말한다. RuntimeException을 상속받는다.

자바는 예외를 클래스로 관리한다. JVM은 프로그램 실행 도중에 예외가 발생하면 해당 예외 클래스로 객체를 생성한다. 그리고나서, 예외 처리 코드에서 예외 객체를 이용할 수 있도록 한다. 모든 예외 클래스는 java.lang.Exception 클래스를 상속 받는다. RuntimeException 역시 Exception을 상속받는다. 그러나 JVM은 RuntimeExeption을 상속했는지 여부를 보고 실행 예외를 판단한다.

10.2 실행 예외

실행 예외는 자바 컴파일러가 체크하지 않는다. 개발자 경험에 의해서 예외 처리 코드를 삽입해야한다. 만약 실행예외에 대한 예외 처리 코드를 넣지 않은 경우, 프로그램이 종료된다.

10.2.1 NullPointerException

object.getValue() 외않되?

자바 프로그램에서 가장 빈번하게 일어나는 실행 예외. 객체 참조가 없는 상태(null)을 참조변수로 객체 접근 연산자인 도트(.)를 사용할 때 발생한다. 객체가 없는 상태에서 객체를 사용하려 해서 나는 에러이다.

UserDto userDto = null;
userDto.getName();				//NPE

10.2.2 ArrayIndexOutOfBoundsException

배열에서 인덱스 범위를 초과하여 사용할 경우의 실행 예외

String str = "012345";
str[8]='9';				//ArrayIndexOutOfBoundsException

10.2.3 NumberFormatException

문자열로 되어 있는 데이터를 숫자로 변경하는 경우가 자주 발생한다. 자바에서 문자열 $\leftrightarrow$ 숫자로 변환하는 방법은 여러가지가 있지만 다음이 많이 사용된다.

변환 타입 메소드명(매개 변수) 설명
int Integer.parseInt(String s) 주어진 문자열을 정수로 변환해서 리턴
double Double.parseDouble(String s) 주어진 문자열을 실수로 변환해서 리턴
String Integer.toString(int number) 주어진 정수를 문자열로 변환해서 리턴

Integer, Double은 포장(Wrapper) 클래스라고 한다. 정적 메소드 parse* 메소드를 이용하면 문자열 $\rightarrow$ 숫자로 변환할 수 있다. 그런데 만약 변환 할 수 없는 문자가 포함되어 있다면? NumberFormatException을 발생시킨다.

String str1 = "100";
String str2 = "a100";

int value1 = Integer.parseInt(str1);
int value2 = Integer.parseInt(str2);	//NumberFormatException

10.2.4 ClassCastException

타입 변환(Casting)은 상위 $\leftrightarrow$ 하위 클래스 간에 발생한다. 혹은 구현 클래스와 인터페이스 간에도 발생한다. 이런 관계가 아니라면 클래스는 다른 클래스로 타입변환 할 수 없다. 억지로 변환을 시도할 경우 ClassCastException이 발생한다.

//ok
Animal animal = new Dog();
Dog dog = (Dog) animal;

//not ok
Animal animal = new Dog();
Cat cat = (Cat) animal;

ClassCastException을 방지하기 위해선 instanceof 연산자로 타입 변환이 가능한지 확인하는 것이 좋다.

instanceof 좌항 객체를 우항 타입으로 변환 가능 여부를 판단한다.

10.3 예외 처리 코드

프로그램 예외 발생시, 갑작스러운 종료를 막고, 정상 실행을 유지하도록 처리하는 코드

자바 컴파일러는 컴파일 할 때 일반 예외가 발생할 가능성이 있는 코드에 컴파일 오류를 발생시켜, 개발자로 하여금 강제적으로 예외 처리 코드를 작성하도록 요구한다. 그러나 실행 예외는 컴파일러가 체크해주지 않기 때문에 예외 처리 코드를 개발자의 경험을 바탕으로 작성해야 한다.

try-catch-finally를 이용하여 일반/실행 예외 처리를 한다.

  • try 예외 발생 가능 코드가 위치한다. try에서 예외 발생 시 실행을 멈추고 catch블록으로 이동하여 예외 처리 코드를 실행한다.
  • catch 예외 처리 코드를 실행하는 부분
  • finally (optional) 예외 발생 여부와 상관없이 항상 실행할 내용이 있을 경우에만 finally 블록을 작성한다. try, catch에서 return문을 사용해도 실행된다. 어?
try {
	Class clazz = Class.forName("java.lang.String2");
} catch (ClassNotFoundException e) {
	System.out.println("클래스가 존재하지 않습니다.");
}

책에서는 개발자의 경험으로 작성해야 한다고 서술했지만, 우리 짱텔리킹이에서는 다 알아서 잡아준다. (단비꺼 외치는 단비마냥 빨간줄긋고 빨리 try-catch쓰라고 난리지긴다.) surrounds with 단축키를 사용하면 발생할 수 있는 Exception Class에 대하여 try-catch를 자동 작성해준다. 윈도우에서 단축키는 ctrl+alt+T이다.

10.4 예외 종류에 따른 처리 코드

10.4.1 다중 catch

try 블록 내부에 다양한 오류들이 폭죽처럼 터지면 어떡할까? 이 경우, 발생되는 예외 별로 다중 catch문을 작성하면 된다. 해당 type 에러의 catch 블록을 실행한다.

여러개의 catch에도 하나의 catch블록만 실행된다. (try 블록에서 동시다발적 예외가 터지는 경우가 없고, 즉시 catch문으로 이동하기 때문이다.)

만약 어떤 타입의 에러가 발생할지 모른다면, catch(Exeption e)로 작성하면 된다. 모든 예외는 Exception을 상속받기 때문이다.

10.4.2 catch 순서

다중 catch 블록 작성시 주의점은 상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야한다. 상위 예외 클래스의 catch블록이 위에 있기 때문에, 하위 클래스의 catch블록은 실행되지 않는다.

//x
try{
	//Exception happens
} catch(Exception e){
	//All of error goes here.
} catch(NullPointerException e){
	//ain't gonna happen.
}

//o
try{
	//Exception happens
} catch(NullPointerException e){
	//NPE handling.
} catch(Exception e){
	//other error handling.
}

10.4.3 멀티 catch

자바 7부터 하나의 catch 블록에서 여러 개의 예외를 처리 할 수 있도록 멀티 catch기능을 추가했다.

try{
	//Exception happens.
}
catch(NullPointerException | NumberFormatException e){
	//NPE, NFE handling.
}
catch (Exception e){
	//other error hadnling.
}

10.5 자동 리소스 닫기

자바 7에서 추가된 try-with-resources를 사용하면 예외 발생 여부와 상관없이 사용했던 리소스 객체(입출력 스트림, 서버 소켓, 소켓, 각종 채널)의 close() 메소드를 호출해서 안전하게 리소스를 닫아준다.

//Before java7
FileInputStream fis = null;
try{
	//IO handling
} catch(IOException e){
	//IO exception handling.
} finally{
	if(fis!=null){
    	try{
        	fis.close();
		} catch(IOException e){
        	//IO exception handling.
        }
    }
}

finally에서 다시 try-catch를 사용해서 복잡해 보인다. try-with-resources를 사용하면 아래와 같이 간단해진다.

try(FileInputStream fis = new FileInputStream("file.txt")){
	//IO handling.
} catch(IOException e){
	//IO exception handling.
}

// 또는 다음과 같이 여러 파일을 사용할 수도 있다.
try(
	FileInputStream fis = new FileInputStream("file1.txt");
    FileOutputStream fos = new FileOutputStream("file2.txt")
){
	//IO handling.
} catch ...

try-with-resources는 java.lang.AutoCloseable 인터페이스를 구현해야 사용 가능하다.

10.6 예외 떠넘기기

메소드 내부에서 예외가 발생할 수 있는 코드를 작성할 때 try-catch 블록으로 예외를 처리하는 것이 기본이지만, 메소드를 호출한 곳으로 예외를 떠넘길 수도 있다.

API를 작성한다고 하자. 모든 예외 처리를 Controller에 떠넘기는 거다!

이 때 사용되는 키워드가 throws이다. throws는 메소드 선언부 끝에 작성되어 메소드에서 처리하지 않은 예외를 호출한 곳으로 떠넘긴다.

리턴타입 메소드명(매개변수, ...) throws 예외클래스1, 예외클래스2, ...{
	//your code here.
}

발생할 수 있는 예외의 종류별로 throws 뒤로 나열하는 것이 일반적이지만, throws Exception만으로 모든 예외를 간단히 떠넘길 수도 있다.

그렇다면 실제로 받는 Exception 클래스는 하위(NPE, ClassNotFoundException, ... 일까 그냥 Exception일까?

public static void findClass() throws Exception {
    Class clazz = Class.forName("java.lang.class2");
}

try {
    findClass();
} catch (ClassNotFoundException e) {
	System.out.println("CNFE code");
} catch (Exception e) {
	System.out.println("Exception code");
	e.printStackTrace();
}

하위 클래스이다!

물론 throws를 받은 메소드에서도 다시 throws를 던질 수 있다. main 메소드에서도 thorws 키워드를 사용해서 예외를 떠넘길 수 있다. 결국 JVM이 최종적으로 예외 처리를 한다. JVM은 예외의 내용을 콘솔에 출력하는 것으로 처리한다. 당연하게도, main메소드에서 thorws 키워드를 사용하는 것은 좋지 못한 예외 처리 방법이다. 되도록 main에서는 try-catch를 사용하도록 하자.

10.7 사용자 정의 예외와 예외 발생

자바 표준 API에서 제공하는 예외 클래스뿐아니라, 개발자가 애플리케이션 예외(애플리케이션 서비스와 관련된 예외)를 직접 정의해서 만들수 있다. 이를 사용자 정의 예외라고 한다.

10.7.1 사용자 정의 예외 클래스 선언

사용자 정의 예외 클래스는 일반 예외/컴파일러가 체크하지 않는 실행예외로 선언할 수 있다. 일반 예외는 Exception, 실행 예외는 RuntimeException을 상속 받으면 된다.

public class XXXException extends [ Exception | RuntimeException] {
	public XXXExeption(){}
    public XXXException(String message){
    	super(message);
	}
}

사용자 정의 예외 클래스

  • 컨벤션에따라 사용자 정의 예외의 클래스명의 끝도 Excetion으로 끝나는 것이 좋다.
  • 대부분 생성자만 선언한다.
  • 생성자는 두개로, 하나는 매개 변수가 없는 생성자와 예외 발생 원인 메시지를 전달하기 위해 String타입의 매개 변수를 갖는 생성자를 선언한다.

10.7.2 예외 발생시키기

사용자 정의 예외 또는 자바 표준 예외를 발생시키는 방법은 아래와 같다.

throw new XXXException();
throw new XXXException("your code sucks");
  • 예외 객체 생성시 기본 생성자와 예외 메시지를 갖는 생성자 둘 다 사용 가능하다.
  • 다만 catch블록에서 예외 메시지가 필요하다면 예외 메시지를 갖는 생성자를 호출한다.
  • 예외 발생 코드를 가지고 있는 메소드 내부에서 try-catch 블록으로 예외를 처리할 수 있지만, 대부분 호출한 곳에서 예외 처리를 하도록 throws키워드로 예외를 떠넘기다.

10.8 예외 정보 얻기

try 블록에서 예외가 발생되면 catch 블록의 매개 변수에서 참조된다. 모든 예외 객체는 Exception을 상속하기에, Exception의 메소드를 모든 예외 객체에서 호출 할 수 있다.

자주 사용되는 메소드는 아래와 같다.

  • getMessage() : 왜 예외가 발생했는지에 대한 간단한 설명이 포함된다.
  • printStackTrace() : 예외 발생 코드를 추적해서 모두 콘솔에 출력한다.