자바를 자바 13(Exception Handling, Errors)

TonyHan·2020년 11월 5일
0

20) 자바

목록 보기
12/27

Errors in Programs

자바에서 발생할 수 있는 에러들

  • Compile-time error
    컴파일이 만들어 내는 에러로 object files를 생성 못함
    Syntax error
  • Run-tume error
    컴파일이 에러나 object files 생성에 문제는 없음
    하지만 프로그램은 실행불가능
  • Logical error
    에러없이 프로그램이 작동함, 하지만 우리가 원했던 결과가 나오지 않음

Run-time error

Run-time error은 errors와 exceptions으로 분류된다.

  • Error
    프로그램이 더 이상 진행을 할 수 없는 상태
    ex> out of memory, stack overflow

  • Exceptions
    에러가 발생하였으나 해결할 방법이 존재하는 경우
    ex> arithmetic exception(0으로 나누는 경우), class cast exception, null pointer exception, index out-of-bounds exception
    그래서 프로그래머는 handler을 선언하여서 exception이 발생하였을 때를 대처할 수 있다.

Erros and Exceptions


자바에서는 이러한 예외도 객체화 되어 있다. 그래서 위의 표와 같이 Throwa ble 클래스 밑에 Error과 Exception 그리고 RuntimeException이 존재

RuntimeException :
ArrayIndexOutOfBoundsException, NullPointerException, ClassCastException, Arithmetic Exception 등이 존재

Exception :
FileNotFoundException, ClassNotFoundException, DataFormatException

Exception Handling: try-catch

자바에서 에러를 처리하기 위해 try-catch블록을 사용하여 try 한다음 에러가 발생하면 catch에서 처리하여 프로그램을 계속 진행시키어 준다.

try {
	// 에러가 날 수 있는 것을 여기에 삽입
} catch (Exception1 e1) {
	// Exception1에 해당되는게 발생시 여기에서 처리
} catch (Exception2 e2) {
	// statements that will be executed when Exception2 occurs.
	try { } catch (Exception3 e3) { }
	// try-catch blocks can be nested. In that case, the parameters (e2 and e3) must be different.
} catch (ExceptionN eN) {
	// statements that will be executed when ExceptionN occurs.
}

Exception Handling: Example

public class Lecture {
	public static void main(String args[]) {
		int number = 100;
		int result = 0;
		for(int i=0; i<10; i++) {
			result = number / (int)(Math.random() * 10);
			System.out.println(result);
		}
	}
}


위와 같이 0~90 사이의 숫자로 나누어 주다가 0으로 잘 못 나누어 준 경우 위와 같이 에러 발생 출력문이 나올 수 있다.

그래서 이를 해결하기 위해 다음과 같이 작성해 주어야 한다.

public class Lecture {
	public static void main(String args[]) {
		int number = 100;
		int result = 0;
		for(int i=0; i<10; i++) {
			try {
				result = number / (int)(Math.random() * 10);
				System.out.println(result);
            // 예외 발생시 여기에서 처리
			} catch (ArithmeticException e) {
				System.out.println("0");
			}
		}
	}
}

The catch Block

catch 블락에 대해서 다시금 생각해 보자

public class Lecture {
	public static void main(String args[]) {
		try {
			System.out.println(3);
			System.out.println(0/0); // ArithmeticException!
			System.out.println(4);
		} catch (Exception e) {
			System.out.println(5);
		}
	}
}

지금 Exception 객체를 받는 catch문이 선언되어 있다. 그런데 발생한건 ArithmeticException이다. 즉 subclass에 해당하는 것은 superclass에서 모두 받아 줄 수 있다는 것을 이야기 한다.

그렇다면 만약 예외경우가 여러개이면 어떻게 할까?

public class Lecture {
	public static void main(String args[]) {
		try {
			System.out.println(3);
			System.out.println(0/0); // ArithmeticException!
			System.out.println(4);
		} catch (ArithmeticException e) {
			System.out.println("ArithmeticException occurred!");
		} catch (Exception e) {
			System.out.println("Exception occurred!");
		}
	}
}

이 경우에는 위와 같이 작성하고 첫번째 catch 블락부터 에러를 처리 할 수 있는지 확인한다. 여기에서 중요하게 봐야하는 점은 subclass는 가급적 superclass 위에 위치시키는 것이 좋다.

printStackTrace() and getMessage()

에러가 났을 때 우리는 무엇을 하는가 인데, 보통의 printStackTrace()와 getMessage()를 많이 한다.

public class Lecture {
	public static void main(String args[]) {
		try {
			System.out.println(3);
			System.out.println(0/0); // ArithmeticException!
			System.out.println(4);
		} catch (ArithmeticException ae) {
			ae.printStackTrace();
			System.out.println("exception message: " + ae.getMessage());
		}
	}
}

printStackTrace : 에러가 생기기 까지 어떤 경로를 따라서 이 위치까지 왔는지를 확인하는 함수
getMessage : 객체로 부터 에러 메세지를 가져온다.

Generating an Exception

예외 경우를 일부러 만들 수도 있다.

public class Lecture {
	public static void main(String args[]) {
		try {
			Exception e = new Exception("I created the exception."); // becomes the exception message.
			throw e;
		} catch (Exception e) {
			System.out.println("exception message: " + e.getMessage());
			e.printStackTrace();
		}
		System.out.println("The program terminated normally.");
	}
}

예외 경우를 다룰 객체를 만들어 놓고 throw를 사용하여서 Exception을 처리한다.

Mandatory and Optional Exception Handling

File Not Found와 같은 경우처럼 반드시 Exception을 하여야만 하는 경우가 존재한다.

  • Mandatory(checked exceptions) : 반드시 Handling 하여야 하는 Exception
public class Lecture {
	public static void main(String args[]) {
		throw new Exception("My exception.");
	}
} // compile error!
  • Optional(unchecked exceptions) : 선택적으로 Handling 하여야 하는 Exception
public class Lecture {
	public static void main(String args[]) {
		throw new ArithmeticException("My exception.");
	}
} // no error!

그럼 어떻게 위의 경우를 구분할까?
Exception 클래스의 subclass 중에서 Runtime Exception을 제외하고 나머지는 Mandatory이다.

Runtime Exception의 대표적인 경우가 ArithmeticException이다.

Methods that throw exceptions

예외 처리를 하지 않는 방법도 존재한다.

public class Lecture {
	public static void main(String args[]) {
		method1();
	}
	static void method1() {
		method2(); // Error: must handle Exception or throw Exception.
	}
	static void method2() throws Exception {
		throw new Exception();
	}
} // compile error!

throws Exception은 에러가 난 경우를 처리하지 않겠다는 의미이다. 그렇다면 method2에서는 에러처리를 하지 않아도 되지만 그 경우에는 method2를 호출한 method1이 에러처리를 해주어야만 한다.

그러면 method1에서도 throws Exception을 한다면??

public class Lecture {
	public static void main(String args[]) {
		method1(); // Error: must handle Exception or throw Exception.
	}
	static void method1() throws Exception {
		method2();
	}
	static void method2() throws Exception {
		throw new Exception();
	}
} // compile error!

결국 main에서 에러가 나게 된다.

만약 main까지 throws Exception을 한다면??

public class Lecture {
	public static void main(String args[]) throws Exception {
		method1();
	}
	static void method1() throws Exception {
		method2();
	}
	static void method2() throws Exception {
		throw new Exception();
	}
} // no error at compile time but will cause program to crash at run time.

실행은 되지만 crash가 난다.


그래서 위의 문제를 해결하기 위해 다음과 작성해주어야지 문제가 발생하지 않는다.

public class Lecture {
	public static void main(String args[]) {
		method1();
	}
	static void method1() {
		try {
			method2();
		} catch(Exception e) {
			System.out.println("Exception handled in method1");
			e.printStackTrace();
		}
	}
	static void method2() throws Exception {
		throw new Exception();
	}
} // OK! method1 handles the Exception

여기에서 한가지 더 짚고 넘어가자. 우리는 전 시간에 파일을 다루기 위한 메소드를 선언하면서 throws IOException을 선언했던것을 기억할 것이다. 그런데 이렇게 하면 좋지 않은 것이 FileOutputStream 내부에는 throws FileNotFoundException이 선언되어 있기 때문이다.

import java.io.FileOutputStream;
import java.io.IOException;
public class Lecture {
	public static void main(String[] args) throws IOException {
		FileOutputStream output = new FileOutputStream("src/cse3040/out.txt");
		String str = "hello world";
		byte[] bytes = str.getBytes();
		output.write(bytes);
		output.close();
	}
}

그래서 위와 같은 코드에도 가급적 try-catch문 선언이 필요하다.

The finally Block

이제 finally Block에 대해서 알아보자
finally block은 try block에서 exception이 발생하든 안하든 상관없이 반드시 실행되는 block이다.

try {
	// statements that can cause exceptions.
} catch (Exception1 e1) {
	// statements for handling Exception1
} finally {
	// this block is executed whether or not an exception occurs in the try block.
	// this block must be placed at the end of a try-catch block.
}

하지만 아래와 같이 try 안에 return을 수행하여야 하는 경우는 어떻게 될 것인가? 결론은 finally를 수행한다음 return을 하게 된다.

public class Lecture {
	public static void main(String args[]) {
		Lecture.method1();
		System.out.println("returned to main method after calling method1.");
	}
	static void method1() {
		try {
			System.out.println("the try block of method 1 is being executed.");
			return;
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("the finally block of method 1 is being executed.");
		}
	}
}

위와 유사하게 이러한 코드를 File IO 에도 사용하여야 한다. 기존에는 BUfferedReader을 사용하기 위해서 main에 throws IOException을 적어 주었던 것이 기억나는가? 이거 역시 사실은 try-catch문으로 모두 잡아 주어야 한다.

여기에서 한가지 주의할점!! try-catch 내에 선언된 변수는 지역변수이다. 파일 IO 변수는 try-catch 외부에 선언하고 나중에 try-catch 내부에서 사용하자

import java.io.FileInputStream;
import java.io.IOException;
public class Lecture {
	public static void main(String[] args) {
		byte[] b = new byte[1024];
		FileInputStream input = null;
		try {
			input = new FileInputStream("src/kr/ac/sogang/icsl/aaa.txt");
			input.read(b);
			System.out.println(new String(b));
		} catch(IOException e) {
			e.printStackTrace();
		} finally {
			try {
				input.close();
			} catch(Exception e) {
				e.printStackTrace();
			}
		}
		System.out.println("The program exited normally.");
	}
}
profile
신촌거지출신개발자(시리즈 부분에 목차가 나옵니다.)

0개의 댓글