bdfgdfg

자바의 예외처리(exception handling) 본문

웹프로그래밍/Java

자바의 예외처리(exception handling)

marmelo12 2023. 7. 26. 15:21
반응형

예외처리란 런타임에 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것을 의미.

 -> 프로그램의 갑작스런 비정상종료를 막고, 다시 정상적인 상태로 복구 시키기 위함.

 

자바는 런타임에 발생할 수 있는 프로그램 오류를 에러와 예외 두 가지로 분류한다.

에러는 메모리 부족(OutOfMemoryError)나 스택오버플로우(StackOverFlowError)와 같이 발생 시 복구할 수 없는 심각한 오류

예외는 발생되더라도 수습가능한 상대적으로 덜 심각한 오류.

 

여기서 위의 예외를 핸들링하는 방법인 예외처리를 배워본다.

 

예외 클래스 계층구조

남궁성 - 자바
남궁성 - 자바

RuntimeException 클래스들은 프로그래머의 실수에 의해 발생가능한 예외들과 연관이 있다.

 -> ex) 배열 크기를 넘어선 인덱스 접근, null 접근등.

Exception클래스(RuntimeException클래스와 자식클래스들을 제외한 클래스들)는 외부의 영황으로 발생할 수 있는 것들.

 -> ex) 존재하지 않는 파일의 이름, 입력한 데이터 형식이 잘못된 경우 등.

 

예외 처리의 기본 문법은 밑과 같다.

import java.util.*;

public class Hello {
	public static void main(String[] args) 
	{
		// TODO Auto-generated method stub
		try
		{
			// 예외 발생 가능한 코드를 작성.
		}
		catch( Exception e1)
		{
			// 예외 처리
		}
	}
}

 

대표적인 0으로 나누어 발생하는 에러를 보자.

import java.util.*;

public class Hello {
	public static void main(String[] args) 
	{
		// TODO Auto-generated method stub
		
		int number = 100;
		int result = 0;
		
		ArrayList<Integer> lst = new ArrayList();
		
		for(int i = 0; i < 10; ++i)
		{
			lst.add((int)Math.random() * 10);
		}
		
		int lstSize = lst.size();
		for(int i = 0; i < lstSize; ++i)
			result = ( number / lst.get(i) );
	}
}

실행 결과

Exception in thread "main" java.lang.ArithmeticException: / by zero

at Hello.main(Hello.java:20)

위와 같은 예외가 발생한다.

 

실제 계산을 수행하는 부분을 try catch로 묶어주면

import java.util.*;

public class Hello {
	public static void main(String[] args) 
	{
		// TODO Auto-generated method stub
		
		int number = 100;
		int result = 0;
		
		ArrayList<Integer> lst = new ArrayList();
		
		for(int i = 0; i < 10; ++i)
		{
			int a = (int)Math.random() * 10;
			lst.add(a);
		}
		
		int lstSize = lst.size();
		try
		{
			for(int i = 0; i < lstSize; ++i)
				result = ( number / lst.get(i) );
		}
		catch(Exception e)
		{
			System.out.println(e.getClass().getName());
			System.out.println("Occur Exception : " + e.getMessage());
		}
	}
}

java.lang.ArithmeticException

Occur Exception : / by zero

위와 같이 예외처리가 가능하고, 만약 예외처리를 하지않았다면 프로그램은 비정상 종료 되었을 것이다.

Exception클래스는 모든 예외 클래스의 부모이므로 어떤 종류의 예외가 발생하더라도 catch문 안으로 들어감.

 

위에서 ClassName과 getMessage를 통해서 예외 클래스 이름과 이유를 좀 더 정확히 알 수 있었는데

더 유용한 방법은 printStackTrace 메소드(호출스택에 있떤 메서드 정보와 예외 메세지 발생)를 사용하는것도 좋다.

 

예외 던지기(throw)

throw 키워드를 통해 예외를 직접 발생시킬수 있다.

import java.util.*;

public class Hello {
	public static void main(String[] args) 
	{
		// TODO Auto-generated method stub
		
		try
		{
			throw new Exception("Exception Throw");
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
}

java.lang.Exception: Exception Throw

at Hello.main(Hello.java:10)

또한 메소드에도 예외 선언이 가능하다

메소드에 예외를 선언한다는 것은 예외를 선언한 메소드에서 예외를 처리한다는 의미가 아니라,

예외가 선언된 메소드를 호출한 메소드 쪽에서 예외처리를 떠맡기는 것.

void method() throws Exception
{
	// 메소드 내용
}

예외처리에서 쉽게 테스트를 위해 상위 부모인 Exception을 사용헀지만, 코드를 볼 때 가독성을 위해서 어떤 예외가 발생할지를 잘 명시해주는게 좋다.

 

예시)

Object 클래스의 wait 메소드는 예외가 발생한다는것을 명시. 실제로 코드 정의부를 타고가보면

해당 예외를 던질 수 있음을 명시해놓았다. 즉 이 메소드를 호출하고자 하는 메소드에서 InterruptedException을 처리해주어야 한다는 의미.

  -> IllegalMonitorStateException은 RuntimeException 클래스의 자식이며 메소드 선언부에는 일반적으로 RuntimeException 클래스들은 적지 않는다. (선언해도 상관없으나 예외 선언은 반드시 필요한것만 선언하는게 일반적)

import java.util.*;

public class Hello {
	
	public static void ThrowMethod() throws Exception
	{
		if( true )
			throw new Exception();
	}
	
	public static void main(String[] args) 
	{
		// TODO Auto-generated method stub
		
		try
		{
			ThrowMethod();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
}

java.lang.Exception

at Hello.ThrowMethod(Hello.java:8)

at Hello.main(Hello.java:17)

 

설명하지 않은 finally블럭도 존재하는데, try->catch->finally순으로 실행되며 예외가 발생하여 catch문을 실행하던 아니던 finally는 무조건 실행된다.

 

자동자원반환(try-with-resources)

주로 OS로 부터 할당받은 자원(resource)들은 사용후에 반납해줘야 하는것들이 있다.

 -> ex) 파일,소켓등

물론 자원의 반납은 보통 close메소드를 호출하면 그만이고, 예외가 발생한다면 try catch로도 가능하지만, 코드가 지저분해보인다.

 

try(
	FileInputStream fin = new FileInputStream("score.dat"); )
{
	
}
catch(Exception e)
{
	
}

위 코드에서 FileInputStream 객체를 생성했고 해당 객체는 close를 통해 자원을 반납해주어야 하는데,

그럴 필요없이 try안에서 위와 같이 선언과 동시에 객체를 생성시 따로 close를 호출하지 않아도 try를 벗어날 때 close를 자동으로 호출해준다.

 

이게 가능한 이유는 AutoCloseable이라는 인터페이스를 통해 해당 클래스에서 자원의 반납에 대한 처리가 이루어지기 때문.

 

반응형
Comments