일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 백준#BOJ#1939#중량제한
- 백준#BOJ#8012#한동이는영업사원
- 백준#boj#16932#모양만들기
- 백준#boj#12755
- 백준#BOJ#14501#퇴사#브루트포스
- 백준#BOJ#2615#오목
- 백준#BOJ#12865#평범한배낭
- Today
- Total
순간을 성실히, 화려함보단 꾸준함을
9주차 과제: 예외 처리 본문
목표
자바의 예외 처리에 대해 학습하세요.
학습할 것 (필수)
- Exception과 Error의 차이는?
- 자바에서 예외 처리 방법 (try, catch, throw, throws, finally) + (catch-with-resource)
- 자바가 제공하는 예외 계층 구조와 checked exception과 unchecked exception의 차이
- 커스텀한 예외 만드는 방법
1. Exception과 Error의 차이는?
먼저 본격적인 내용에 들어가기 앞서 예외란 무엇인지? 그리고 에러와 어떤 차이점이 있는지 부터 알아보도록 하자
- 예외(Exception) : 입력값의 처리가 불가능하거나, 프로그램 실행 중에 참조된 값이 잘못된 경우. 정상적인 프로그램의 흐름을 어긋나는 것을 말함
- 에러(Error) : 시스템에 무엇인가 비정상적인 상황이 발생한 경우, 주로 JVM에서 발생시키는 것으로 어플리케이션에서 잡을 수 없다.
예외(Exception)과 에러(Error)의 가장 큰 차이는 system level의 문제과 Application level의 문제의 차이이다.
2. 자바에서 예외 처리 방법 (try, catch, throw, throws, finally) + (catch-with-resource)
예외처리를 하는 기본적인 방식은 try
키워드를 이용해 원래 작업하려는 소스들을 감싸고 이 소스들 중에서 발생할 수 있는 예외들을 catch
키워드를 사용하여 잡아낸다. 이와 별개로 반드시 실행시키고 싶은 소스들은 finally
로 처리를 해주면 된다.
try-catch문
try {
// 예외가 발생할 가능성이 있는 문장들
} catch (Exception1 e1) {
// Exception1이 발생햇을 경우
} catch (Exception2 e2) {
// Exception2가 발생햇을 경우
} catch (Exception3 e3) {
// Exception3이 발생했을 경우
}finally {
// 예외 발생 여부에 관계없이 항상 실행됨
// try-catch문의 마지막에 위치함.
}
try
문에서 예외가 발생한다면 예외가 발생한 시점 이후의 소스들은 전혀 실행이 되지 않는다.
또한, 반드시 주의할 점이 있는데 catch
문을 사용하여 예외들을 잡아줄때 무조건 범위가 낮은 예외들부터 작성해야한다.
예외는 상속 관계를 가지고 있기 때문에 이를 항상 유의해야 한다.
public class Main extends Thread{
public static void main(String[] args){
try{
}catch(RuntimeException e){
}catch(IllegalArgumentException e){ //Compile Error 발생
}
}
}
IllegalArgumentException
을 한번 확인해 보자
public class IllegalArgumentException extends RuntimeException {.......}
RuntimeException
을 상속하고 있는 것을 확인 할 수 있다.
printStackTrace()와 getMessage()
예외가 발생하였을때 해당 인스턴스에는 발생한 예외의 정보들이 담겨져 있다. 이에 대해 알고 싶을때 printStackTrace()
와 getMessage()
를 사용하여 해당 정보들을 확인할 수 있다.
try {
...
} catch (ExceptionA e) {
e.printStackTrace();
} catch (ExceptionB e2) {
e2.getMessage();
}
멀티 catch-block
자바7(JDK 1.7) 버전에 도입된 문법이다. 여러 catch
블록을 |
기호를 사용하여 하나로 합칠 수 있다. 단 주의할 점은 상속관계에 있는 예외들은 같이 사용하지 못한다.
try {
...
} catch (ExceptionA | ExceptionB e) {
e.printStackTrace();
}
상속관계에 있으면 컴파일 에러 발생
public class Main{
public static void main(String[] args){
try{
}catch(RuntimeException | IllegalArgumentException e){ //Compile Error 발생
}
}
}
throw
연산자 new
를 사용하여 발생시키는 예외 클래스의 객체를 만든다.
public class Main{
public static void main(String[] args){
try{
RuntimeException e = new RuntimeException("예외발생");
throw e;
}catch(RuntimeException e){
System.out.println("Error message : " + e.getMessage());
e.printStackTrace();
}
}
}
Error message : 예외발생
java.lang.RuntimeException: 예외발생
at Main.main(Main.java:11)
exception
인스턴스를 생성할때, 생성자에 String
형 변수를 넣어주면 메세지로 저장이 된다. 이를 getMessage()
를 사용하여 확인할 수 있다.
throws
메소드에 예외를 선언함으로써 메소드를 사용하려는 사람이, 어떤 예외가 발생될 수 있고 처리하는지를 알 수 있다.
throws
키워드를 사용하는 목적은 예외발생시 처리하는 것이 아닌, 자기 자신을 호출한 메소드에게 발생한 예외를 처리하도록 책임을 전가하는 역할을 한다.
주의할 점은 계속 책임을 전가하다 보면 결국 끝에는 main()
함수에게 가는데 이는 프로그램이 죽을 수 있는 위험성을 가지고 있다.
public class Main{
public static void main(String[] args){
try{
method1();
}catch(RuntimeException e){
System.out.println("Error message : " + e.getMessage());
e.printStackTrace();
}
}
static void method1() throws RuntimeException{
throw new RuntimeException("예외발생");
}
}
Error message : 예외발생
java.lang.RuntimeException: 예외발생
at Main.method1(Main.java:18)
at Main.main(Main.java:11)
try-with-resource
try
구문에서 선언된 자원들을 자동으로 해제해 주는 기능을 제공하는 문법이다.
입출력에 사용되는 클래스들 중 close()
라는 키워드를 사용하여 반드시 자원을 해제해야 하는 것들이 존재한다.
그러나 이를 매번 사용하려고 하면 코드도 더러워 지고 가독성도 자연스럽게 나빠질 수 밖에 없게 된다.
import java.io.*;
public class Main{
static void copy(String src,String dest) throws IOException{
InputStream in = null;
OutputStream out = null;
try{
in = new FileInputStream(src);
out = new FileOutputStream(dest);
byte[] buf = new byte[1024];
int n;
while((n=in.read(buf))>=0)
out.write(buf,0,n);
}finally {
if(in!=null)in.close();
if(out!=null)out.close();
}
}
}
위 소스를 봐보자. 소스를 설명하자면 src
파일을 열어서 dest
에 복사하는 소스이다.(이는 중요하지 않고)
어떤 문제점이 발생하는지 알겠는가??
위 소스에서 throws IOEXception
을 빼버리면 빨간줄이 여러개 뜨는 걸 확인할 수 있을 것이다.
바로 close()
에서도 예외가 발생할 수 있다는 점이다.
만약에 if(in!=null)in.close();
이 문장에서 예외가 발생했다면 그 다음줄인 if(out!=null)out.close();
은 실행이 되지 않는다.
그러면 out
은 닫혀지지 않는 문제점이 발생할 수 있게 된다. 그럼 어떻게 해줘야 될까? 간단하다 또 다시 try-catch
문으로 감싸는 거다.
import java.io.*;
public class Main{
static void copy(String src,String dest) throws IOException{
InputStream in = null;
OutputStream out = null;
try{
in = new FileInputStream(src);
out = new FileOutputStream(dest);
byte[] buf = new byte[1024];
int n;
while((n=in.read(buf))>=0)
out.write(buf,0,n);
}finally {
if(in!=null){
try{
in.close();
}catch(IOException e){
//...
}
}
if(out!=null){
try{
out.close();
}catch(IOException e){
//...
}
}
}
}
}
이렇게 수정해 주었다. 자, 이러면 해결이 된 것일까??이 또한 완벽하게 해결이 되었다고 볼 수가 없다. 그 이유는 현재 close()
에 대해 IOException
만 잡고 있지만 만약 close()
를 수행할때 RuntimeException
이 발생하게 된다면????마찬가지로 다음 조건문을 수행하지 않고 그냥 튕겨져 버린다. 결국 수정전이나 차이가 없다는 뜻이다.
import java.io.*;
public class Main{
static void copy(String src,String dest) throws IOException{
InputStream in = null;
OutputStream out = null;
try{
out = new FileOutputStream(dest);
try{
in = new FileInputStream(src);
byte[] buf = new byte[1024];
int n;
while((n=in.read(buf))>=0)
out.write(buf,0,n);
}finally {
if(in!=null){
try{
in.close();
}catch(IOException e){
//...
}
}
}
}finally{
try{
out.close();
}catch(IOException e){
//...
}
}
}
}
결국 이렇게 전체적인 소스를 try-catch
로 잡아야 된다. 어떤가??? 보기만해도 난 신물이 나온다 ㅋㅋㅋㅋ
이런 고질적인 문제점을 해결하기 위해 try-with-resource
를 사용하는 것이다.
import java.io.*;
public class Main{
static void copy(String src,String dest) throws IOException{
try(InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest)){
byte[] buf = new byte[1024];
int n;
while((n=in.read(buf))>=0)
out.write(buf,0,n);
}
}
}
너무 아름다운 코드이다...적어도 자바8이상을 사용한다면 반드시 try-with-resource
를 사용하도록 하자.
+추가로 try-with-resource
구문에서 만약 finally
키워드를 사용한다면 try-with-resource
문이 실행이 된 후에 finally
구문이 실행이 된다.
public class Main{
public static void main(String []args){
try(MyStrem my = new MyStrem()){
}finally {
System.out.println("hello");
}
}
}
class MyStrem implements AutoCloseable{
@Override
public void close() throws RuntimeException{
System.out.println("close my stream");
}
}
//출력
close my stream
hello
3. 자바가 제공하는 예외 계층 구조와 checked exception과 unchecked exception의 차이
자바의 예외는 크게 2가지로 구분할 수 있다.
checked Exception
RuntimeException을 상속하지 않은 예외들을 칭한다. checked Exception은 반드시 catch 문으로 예외를 잡거나 throws 키워드를 사용하여 자신을 호출한 메소드에게 예외를 던져야한다. 이 두가지 중 하나라도 하지 않으면 컴파일 에러가 발생한다. 대표적으로 IOException, SQLException, ClassNotFoundExceoption 이 있다. (백준에서 자바로 문제를 풀려고 하면 입력을 메소드를 사용하기 위해 InputStream 을 사용하신 경험이 있으셨을 겁니다. 이때 readline() 메소드를 사용하기 위해서는 반드시 IOExcetion을 명시해줘야 하며 이를 어길시 컴파일 에러가 발생하는 것을 확인 하실 수 있습니다)
public class Main {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String input;
input = br.readLine();
StringTokenizer st = new StringTokenizer(input);
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
System.out.println("x+y : " + (x+y));
}
}
x와y 값을 받고 두 수를 더하는 소스이다. 이때 throws IOException
을 제외해버린다면 br.readline()
에 빨간줄이 그어지는 것을 확인할 수 있다.
IOException : IOException 는 스트림, 파일 및 디렉터리를 사용 하여 정보에 액세스 하는 동안 throw 되는 예외에 대 한 기본 클래스이다.
현재 프로젝트가 있는 경로에 input.txt
파일이 없는 상태인데 이를 열어보려고 시도해 보자
public class Main {
public static void main(String[] args) {
try{
FileInputStream in = new FileInputStream("input.txt");
System.out.println("This is text file");
}catch(IOException exception){
exception.printStackTrace();
}
}
}
SQLException : SQL Server에서 경고 또는 오류를 반환할 때 throw되는 예외이다.이 클래스는 상속될 수 없다.
ClassNotFoundException : 정의한 클래스를 찾을 수 없을 때 발생하는 예외이다.
public class Main {
public static void main(String[] args) {
try{
Object object = Class.forName("MaintoMain");
System.out.println("ClassNotFoundException");
}catch(ClassNotFoundException exception){
exception.printStackTrace();
}
}
}
InterruptedException : Thread가 waiting, sleeping 또는 어떤 처리를 하고 있을 때 interrupt가 되면 발생하는 예외이다.
public class Main extends Thread{
public static void main(String[] args){
Main m = new Main();
m.start();
m.interrupt();
}
@Override
public void run(){
try{
Thread.sleep(100);
System.out.println("currnet");
}catch(InterruptedException exception){
exception.printStackTrace();
}
}
}
unchecked Exception
RuntimeException을 상속하는 예외들을 칭한다. 일반적으로 발생을 예측할 수 없고 복구할 수 없는 예외들을 칭한다. 주로 외부API 와 소스코드상의 에러(프로그래머의 실수) 때문에 발생한다. 컴파일 단계에서 확인되지 않은 예외이며 예외를 처리하도록 컴파일러가 강제하지도 않는다.
Arithmetic exception : 예외적인 산술조건이 발생하면 발생하는 예외이다. 대표적으로 '0'으로 나누려고 하면 발생한다.
public class Main {
public static void main(String[] args) {
try{
int x=4;
int y=0;
System.out.println(x/y);
}catch(ArithmeticException exception){
exception.printStackTrace();
}
}
}
ArrayIndexOutOfBounds Exception : 배열에서 할당되지 않은 메모리 공간에 접근하려고 할때 발생하는 예외이다. 대표적으로 음수인 인덱스에 접근하거나 배열 크기를 넘어서서 접근하려는 경우이다.
public class Main {
public static void main(String[] args) {
try{
int []arr = new int[5];
arr[5]=5;
}catch(ArrayIndexOutOfBoundsException exception){
exception.printStackTrace();
}
}
}
NullPointerException : 사용할 객체를 생성후 인스턴스를 생성하지 않고 null 인 상태의 객체를 사용하려고 할때 발생한다.
public class Main extends Thread{
public static void main(String[] args){
try{
Main m = null;
m.getNull();
}catch (NullPointerException exception){
exception.printStackTrace();
}
}
public void getNull(){
System.out.println("Here Null");
}
}
NumberFormatException : 잘못된 형식의 문자열을 수로 변환할때 발생한다.
public class Main extends Thread{
public static void main(String[] args){
try{
String x = "o1o";
System.out.println(Integer.parseInt(x));
}catch (NumberFormatException exception){
exception.printStackTrace();
}
}
}
4. 커스텀한 예외 만드는 방법
커스텀 예외란?
- 자바에서 제공되는 표준 API 예외 말고 내가 직접 예외상황을 만들어 이를 구현하고 싶을때도 존재할 것이다. 이를 가능케 해주는 게 커스텀예외이다.
checked Exception
과 unchecked Exception
둘다 정의를 할 수 있으며, 생성자를 선언하여 예외 메세지를 보낼 수 있다.
class YuumiException extends RuntimeException{
public YuumiException(String message,Throwable cause){
super(message,cause);
}
}
커스텀 예외를 사용할때 excetion
의 생성자에서 예외를 꼭 받는 걸 권장한다. 그 이유는 전달하지 않고 메세지만 전달하게 된다면 너무나 피상적인 메세지가 되버린다. 근본적인 예외상황의 정보가 없어져 버린다.
public class Main{
public static void main(String []args){
try(YuumiException yummi = new YuumiException()){
}catch(Throwable e){
throw new YuumiException("유미는 사랑",e);
}
}
}
class YuumiException extends RuntimeException{
public YuumiException(String message,Throwable cause){
super(message,cause);
}
}
reference
[https://wisdom-and-record.tistory.com/46]
[https://scshim.tistory.com/238]
[https://velog.io/@youngerjesus/%EC%9E%90%EB%B0%94-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC]
'백기선님과 함께 하는 자바 스터디' 카테고리의 다른 글
[리뷰] 10주차 : Critical Path (0) | 2021.07.03 |
---|---|
10주차 과제: 멀티쓰레드 프로그래밍 (1) | 2021.07.03 |
8주차 과제: 인터페이스 (0) | 2021.06.22 |
7주차 과제: 패키지 (0) | 2021.06.19 |
6주차 과제: 상속 (0) | 2021.06.12 |