[JAVA] 예외처리

노을지는·2022년 6월 5일
0

JAVA

목록 보기
4/7

에러와 예외

에러는 코드만으로는 대응할 수 없는 오류를 말한다. 가장 자주 볼 수 있는 에러는 컴파일 에러인데, 이러한 에러들은 IDE에서 대부분 잡아주기 때문에 디버깅이 그나마 수월하다.

예외는 코드 차원에서 따로 처리가 가능한 오류들이다. 예외가 발생할만한 구간에 미리 장치를 해놓고, 예외가 뜨면 이러이러하게 처리하라고 길을 내어주는 식이다. 자주 볼 수 있는 예외로는 NullPointerException이나 IndexOutOfBoundsException이 있다.

IDE는 매우 친절해서 오류가 발생하면 무슨 오류가 어느 라인에서 발생했는지 상세하게 알려준다. 오류가 났을 때 콘솔 창을 잘 확인해보면 대응이 한결 쉬워진다.

try-catch문 + finally

try {
	// 예외가 발생할만한 코드들
} catch (예외) {
	// 예외가 발생했을 때 처리하는 코드
} finally {
	// 발생하든 안하든 실행되어야 하는 코드
}

기본적인 구조는 이러하다.

try 블럭 안에서 예외가 몇 가지고 발생할 수 있기 때문에 그에 맞춰서 catch 블럭도 여러 개를 만들 수 있다. 그래서 try 블럭을 주욱 수행하다가 예외가 떴다! 하면 그에 해당하는 catch 블럭을 찾아가서 그 블럭에 있는 코드를 수행한다. catch 블럭이 없으면 예외가 처리되지 못하고 비정상적으로 종료된다.

catch 블럭 괄호에는 예외 클래스와 참조변수 하나를 넣어준다. 예외가 뜨면 그 클래스의 참조변수를 통해 인스턴스가 생성되어 catch 블럭 안에서 그와 관련된 메소드를 사용할 수 있게 된다.

finally 블럭은 선택적인데 만약 존재한다면 예외가 뜨든 안뜨든 무조건 거쳐서 나오게 된다.

throw와 throws

throw는 일부러 예외를 띄울 때 쓰는 키워드이고 throws는 예외를 던질 때 쓰는 키워드이다.
예외를 굳이 일부러 띄워야되나 싶지만 분산해서 처리하고 싶을 때는 사용할 수 있다. throws는 예외를 상위 메소드로 던져서 처리를 떠넘겨버린다. 그럼 이걸 받은 상위 메소드는 처리하는 코드를 통해서 처리를 해주어야 하고 그게 안되면 비정상 종료가 일어나게 된다.

public class testClass {

	public static void main(String[] args) {
		testMethod();
	} // main
	
	public void testMethod() {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		br.read();
		br.close();
	} // order
	
} // end class

가장 예외처리를 볼 확률이 높은 구간이 바로 입출력이다. 입력을 받는데 이상한게 들어오면 안되니까 그냥 저 상태에서 실행을 시켜보면 IOException이 처리가 안됐다고 testMethod()와 main 두 곳에서 오류가 뜬다.

IDE의 수정 제안에서 throws문 선언을 고르면 메소드 선언부 바로 옆에 throws IOException을 붙일 수 있는데 이러면 testMethod()를 호출한 메소드(main)가 IOException을 처리해주어야 예외처리가 정상적으로 된다. 처리를 상위 메소드로 떠넘겼기 때문에 testMethod() 안에는 예외처리 코드가 전혀 없어도 되고 붙인 채로 실행을 시켜보면 testMethod()는 이제 오류가 안뜨고 main에서만 뜬다.

main에서도 throws를 사용할 수 있는데 main에서 다른 데로 던져봤자 의미가 없다. 예외처리를 하지 않은 거랑 똑같은 셈이다. 그런데 백준같이 거의 main만 사용하는 곳에서는 그냥 main에다가 붙여서 오류만 안나게 할 수도 있긴 하다.

throws를 통해서 발생할 수 있는 예외를 명시해서 메소드를 호출하는 쪽이나 코드를 보는 다른 사람한테 알려줄 수 있게 되어 조금 더 오류를 줄일 수 있는 장점도 있다.

try-with-resource

위에서 입출력에서 예외처리를 자주 볼 수 있다고 했는데, 입출력을 할 때 작업이 끝나면 꼭 닫아서(close) 리소스를 반환해줘야 하는 것이 몇 개 있다. 닫지 않으면 메모리 누수가 발생할 수 있기 때문에 잊지 말고 닫아주어야 한다.

근데 닫는 것을 까먹거나 닫는 코드를 주렁주렁 달다보면 난잡해지는 경우가 있다. 그럴 때 사용하는 것이 바로 try-with-resource 구문이다.

try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))){
			br.read();
		} catch (IOException e) {
			e.printStackTrace();
		}

이런 식으로 try 바로 옆 괄호에다 객체 생성 문장을 넣어주면 try 블럭을 벗어났을 때 알아서 닫아주니 편하고 깔끔하다.

0개의 댓글