티스토리 뷰
안녕하세요
Shiny Ocean 입니다 : )
이번 포스팅에서 다루어볼 내용은 지난 포스팅에 이어서 "예외"입니다.
개요
지난 포스팅에서 우리는 예외란 무엇인지, 예외는 어떠한 클래스로 구성되고 어떠한 방식으로 처리되는지 기본적인 내용들에 대하여 다루어 보았습니다. 이번 포스팅에서는 그 내용을 그대로 이어서 자동 리소스 닫기, 예외 떠넘기기, 사용자 정의 예외, 예외 정보 얻기 등을 다루어 보겠습니다.
자동 리소스 닫기
자바 버전7 부터 생겨난 try-with-resources 는 예외 발생의 여부와 상관 없이 사용했던 리소스 객체의 close()메소드를 호출하여 리소스를 닫아줍니다. (입출력을 위해 사용하는 Scanner 클래스의 close()메소드가 대표적인 예입니다)
여기서 리소스 객체라 함은 각종 입출력 스트림, 서버소켓, 소켓, 각종 채널들을 의미합니다.
각각의 리소스에 대한 세세한 내용은 나중에 알아보고 여기서는 try-with-resources를 이용하면 리소스의 close()메소드를 호출해 안전하게 리소스를 닫을수 있다는것이 중요합니다.
그런데 리소스의 close()메소드를 호출하여 리소스를 안전하게 닫기 위해서는 리소스 객체가 java.lang.autoCloseable인터페이스를 구현하고 있어야 한다.
해당 조건을 만족하지 않는 리소스를 try with resources구문에 사용한다면 close가 자동으로 호출되지 않는다.
파일을 오픈하는 간단한 예제를 통해 리소스 자동닫기와 수동닫기?를 다루어 보겠습니다.
만약 수동닫기의 방식을 택한다면 try구문에 파일인풋스트림을 다룰 객체를 생성하고 finally구문에 해당 객체를 닫아줘야 할것입니다. 아래와 같습니다.
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("file.txt");
}catch(IOException e) {
// 파일 객체 생성 과정중 발생 예외처리
}finally {
if(fis != null) { //파일 입력 스트림 객체가 정상적으로 초기화되었다면
try {
fis.close(); //객체를 닫는 메소드를 수동적으로 사용한다.
}
catch(IOException e) {
// 파일 객체를 닫는 과정중 발생 예외처리
}
}
}
}
파일인풋스트림 객체를 생성하고 닫는 과정에서 예외가 발생할수 있기 때문에 try catch를 단계적으로 총 2번사용하는것을 확인할수 있습니다.
하지만 이를 아래와 같이 try with resources를 사용한다면 조금더 코드를 직관적으로 간출일수 있습니다. try 와 함꼐 괄호를 사용하여 close()를 할 리소스 객체를 함께 선언해주는것입니다. 이는 세미콜론 (;)을 사용하여 여러개의 리소스객체 선언도 가능합니다.
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("file.txt")){
// fis객체를 사용후 자동으로 close매소드를 호출함, finally 구문 생략가능
}catch(IOException e) {
}
}
여기서 위처럼 close메소드가 자동으로 호출되게 하기위해서 인터페이스를 구현해야 하는것이 중요하기 때문에 그 과정을 보이겠습니다.
public class FileInputStream implements AutoCloseable{
private String file;
public FileInputStream(String file) {
this.file = file;
}
public void read() {
System.out.println(file + "경로의 파일을 열음");
}
@Override
public void close() throws Exception {
System.out.println(file +"경로의 파일을 닫음");
}
}
위의 인터페이스 구현체에서 중요한것은 맨마지막 블럭인 close() 메소드를 오버라이딩하는것입니다.
위처럼 AutoCloseable 인터페이스의 구현체로 만들어진 리소스 클래스의 객체는 try with resources 구문을 사용한 자동 리소스닫기를 수행할수 있으며 이는 코드를 조금더 간결하고 기능성있게 만들어주는 예외처리 방법입니다.
예외 떠 넘기기
두번째로 다룰 주제는 예외 떠 넘기기 입니다.
메소드 내에서 발생한 예외이지만 자체적으로 처리하기 번거로운 예외를 메소드를 호출한 곳으로 넘겨주는것이 핵심 아이디어입니다 이때 사용되는 키워드가 throws입니다.
이는 메소드 선언부의 끝에 사용합니다. 백문이 불여일견 간단한 예제를 통해 다루어 보겠습니다.
makeException 메소드는 classnotfound예외를 발생시킵니다. 이때 해당 예외를 자신을 호출한 곳으로 떠넘길수 있도록 throws 키워드를 사용하였고 떠맡은 곳에서는 try catch 를 사용하여 예외를 처리하여 주었습니다.
class Method{
static void makeException() throws ClassNotFoundException {
Class clazz = Class.forName("java.lang.String2");
}
void startTest() {
try {
makeException();
}catch (ClassNotFoundException e) {
System.out.println("예외 처리함");
}
}
}
public class Exam3 {
public static void main(String[] args) {
Method me = new Method();
me.startTest();
}
}
이때는 정상적으로 "예외 처리함" 문자열이 출력되고 메인스레드가 종료됩니다. 하지만 예외를 throws하지 않아준경우에는 어김없이 예외가 발생했다는 오류가 콘솔창에 출력됩니다. 컴파일시에도 코드라인에 붉은 밑줄이 나타날것입니다.
사용자 정의 예외
예외는 자바의 라이브러리의 예외클래스에 기본적으로 탑제된 처리가능한 예외 뿐만 아니라, 사용자가 정의하여 예외를 발생시킬수도 있습니다. 이번에는 사용자 정의 예외를 선언하는 방법과 어떻게 발생되는지 알아보겠습니다.
사실 이부분이 웹 개발에서 테스트 코드를 작성하거나 할때 가장 중요합니다... 사용자 정의 예외를 정확하게 다루고 예외 정보를 얻는 방법을 다룬 이후에는 다음 포스팅에서는 실질적으로 테스트 코드를 작성해보며 실습해보겠습니다.
사용자 정의 예외란?
자바 표준 API에서 제공하지 않는 예외로서 애플리케이션 서비스와 관련하여 개발자가 만들어내는 예외입니다. 예를 들면 결제 비지니스 로직에서 잔고부족예외, 송금 로직에서 이체 실패 예외, 회원가입실패 등등 입니다.
사용자 정의 예외 클래스 선언 방법
< super(message)는 부모클래스인 Exception | RuntimeException에게 해당 값을 넘겨주는 것이다, 이는 예외의 정보를 담아 넘겨주는것이 관례적이다.>
public class 예외이름Exception extends [Exception | RuntimeException] {
public 예외이름Exception() { }
public 예외이름Exception(String message {super(message);}
}
예외 발생시키는 방법
< throws 와 throw는 전혀 다른 기능이니 헷갈리지 말자 throw는 단순히 예외를 발생시키는 키워드이다. >
throw new 예외이름Exception();
throw new 예외이름Exception("메세지");
// 일방적으로 메소드 안에서 어떠한 조건이 맞지 않을때 예외를 발생시킨다.
public void method() throws 예외이름Exception {
throw new 예외이름Exception("메세지");
}
지금까지는 try구문 안에서 조건에 따른 예외가 발생했다면 사용자 지정예외는 throw키워드를이용하여 직접적으로 예외를 발생시킬수 있다는 점이 다르다. 이를 조건문과 같이 사용한다면 여러 방면으로 활용도가 높아질수 있다.
간단한 예제를 통해 또 다루어보겠습니다.
만약 코인 매도 프로그램을 만든다하겠습니다. 코인의 가격이 1원씩 점점 오르다가 100원이 되면 예외를 발생시키고 이를 처리한다 하겠습니다 예외의 이름은 CoinPriceOutOfBoundException 이라 해보죠
예외 클래스 먼저 정의해보겠습니다. 기본적인 사용자 정의 예외 클래스의 양식으로 작성해보았습니다,
package exceptionExam;
public class CoinPriceOutOfBoundException extends Exception{
public CoinPriceOutOfBoundException() { }
public CoinPriceOutOfBoundException(String message) {
super(message);
}
}
다음은 메인메소드에서 예외를 발생시키는 조건을 성립시키게 로직을 완성해보겠습니다.
package exceptionExam;
public class Exam {
public static void main(String[] args) throws CoinPriceOutOfBoundException {
int coinPrice = 0;
for(int i = 0; i<101; i++) {
if(coinPrice == 100) {
try {
throw new CoinPriceOutOfBoundException();
}catch (CoinPriceOutOfBoundException e) {
System.out.println("CoinPriceOutOfBoundException 발생");
}
}
else {
System.out.println(coinPrice);
coinPrice++;
}
}
}
}
메인문이 예외를 throws 하게는 잘 작성하지 않지만 직관적인 예를 보이기 위해 위처럼 해보았습니다.
결과는 아래와 같습니다.
예외 정보 얻기
예외에 대한 정보를 얻을때 사용하는 것이 message라고 위에 언급한 바 있습니다.
예외를 발생시킬 때 생성자 매개값으로 사용한 메세지를 리턴받기 위해서는 getMessage() 메소드를 이용합니다.
이는 좀더 상세 원인을 세분화하기위해 사용합니다.
catch절 에서 활용하며 아래와 같습니다.
} catch (Exception e) {
String message = e.getMessage();
}
또, 조금더 자세하게 예외를 추적하기 위해서는 printStackTrace() 메소드를 사용하여 예외 발생 코드를 추적한 모든 내용을 콘솔에 출력하기도 합니다.
위에서 message는 예외를 발생시킬때 throw 키워드 이후 생성자에 메세자를 포함하여 예외 객체를 만들게 되는데 그때 생성자로 던진 메세지가 message 스트링이 됩니다. 이전에 예제를 조금 활용해보면 아래와 같이 코드를 변경해도 같은 결과를 얻어낼수 있을 것입니다.
package exceptionExam;
public class Exam {
public static void main(String[] args) throws CoinPriceOutOfBoundException {
int coinPrice = 0;
for(int i = 0; i<101; i++) {
if(coinPrice == 100) {
try {
throw new CoinPriceOutOfBoundException("CoinPriceOutOfBoundException 발생");
}catch (CoinPriceOutOfBoundException e) {
System.out.println(e.getMessage());
}
}
else {
coinPrice++;
System.out.println(coinPrice);
}
}
}
}
물론 캐치하지 않고 예외를 발생해도 해당 메세지를 확인할수 있습니다. 아래와 같습니다.
'Language > Java - 심화' 카테고리의 다른 글
Java(심화) - 예외(Exception) part1 (0) | 2022.01.19 |
---|---|
Java(심화) - 어노테이션 (0) | 2022.01.16 |
Java(심화) - 싱글톤 (0) | 2022.01.16 |
Java(심화) - 추상클래스 VS 인터페이스 (1) | 2022.01.16 |
Java(심화) - 객체 지향 프로그래밍 (0) | 2022.01.04 |