-
Java 예외 처리Java 2024. 2. 25. 15:27728x90반응형
예외처리(Exception Handling)
예외의 발생으로 인한 실행중인 프로그램의 비정상 종료를 막음
프로그램 실행 중 발생하는 에러는 어쩔 수 없지만, 예외는 미리 프로그래머가 대처해줘야 한다.
발생한 예외를 처리하지 못하면, 프로그램은 비정상 종료되며,
처리되지 못한 예외는 JVM의 '예외처리기'가 받아서 예외의 원인을 화면에 출력한다프로그램 오류
프로그램 실행 중 어떤 원인에 의해 오작동하거나 비정상적으로 종료되는 경우의 원인
컴파일 에러 : 컴파일할 때 발생하는 에러
런타임 에러 : 실행 도중에 발생하는 에러
논리적 에러 : 컴파일, 실행에 문제가 없지만 의도와 다르게 동작
소스코드를 컴파일하면 컴파일러가 소스코드(*.java)에 대해 오타나 잘못된 구문, 자료형 체크 등 기본적인 검사를 수행해 오류가 있는지 알려준다.
에러들을 없애고 컴파일을 성공적으로 마치면, 클래스파일(*.class)이 생성된다.
자바에서는 실행 시(런타임) 발생하는 프로그램 오류를 에러(error)와 예외(exception) 두 가지로 구분한다.
에러
메모리부족이나 스택오버플로우 등 일단 발생하면 복구할 수 없는 심각한 오류, 프로그램의 비정상적인 종료를 막을 길이 없음
예외
발생하더라도 프로그래머가 미리 작성한 적절한 코드로 비정상적인 종료를 막을 수 있음
예외 클래스 계층 구조
자바에서는 런타임 오류(!= 런타임 에러)를 클래스로 정의하였다. 얘네 역시 Object 클래스의 자손이다.
Object
Throwable
Exception
RuntimeException
~~~~
~~~~
~~
IOException
Error
~~~
OutOfMemoryError
최고 조상은 Exception클래스.
RuntimeException클래스들은 주로 프로그래머의 실수에 의해 발생될 수 있는 예외들로,자바의 프로그래밍 요소들과 관계가 깊다.
배열의 범위를 벗어남, 값이 null인 참조변수의 멤버를 호출, 클래스 간 형변환을 잘못하거나, 정수를 0으로 나누는 등예외처리 - try-catch
try{ //예외 가능성 있는 문장들 ~~ } catch(Exception1 e1){ //Exception1 발생 시 이를 처리하기 위한 문장 } catch(Exception2 e2){ //Exception2 발생 시 이를 처리하기 위한 문장 }
캐치문 수에 제한 없음
이 중 단 하나의 캐치문만 실행됨
예외와 일치하는 캐치블럭이 없으면 예외처리가 안된다
if문과 달리 try블럭이나 catch블럭 내에 포함된 문장이 하나 뿐이어도 중괄호({})를 생략할 수 없다.
캐치블럭 괄호(())내에 선언된 참조변수의 종류와 생성된 예외클래스의 인스턴스에 instanceof 연산자를 이용해 검사. true인 블럭을 만날 때까지 계속 검사마지막에 Exception클래스 타입의 참조변수를 선언한 catch블럭을 사용하면, 어떤 종류의 예외든 처리가 가능하다
예시class Exception1{ public static void main(String args[]){ try{ try{} catch(Exception e){} } catch(Exception e){ try{} catch (Exception e){} // 에러. 변수 e 중복 선언 } } }
catch블럭 내에 선언된 변수는 지역변수라 모든 catch블럭에 참조변수 e 하나만 써도 됨
근데 이중 try-catch문일 경우 같은 이름은 쓰면 안됨
흐름
1. 예외가 발생한 경우
발생한 예외와 일치하는 catch블럭이 있는지 확인.
일치하는 catch블럭을 찾으면, 그 문장을 수행하고 전체 try-catch문을 빠져나간다
2. 예외가 발생하지 않은 경우
catch블럭을 거치지 않고 전체 try-catch문을 빠져나간다
printStackTrace(), getMessage()
예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨있다.
두 함수를 통해 정보들을 얻을 수 있다.
catch블럭의 괄호에 선언된 참조변수를 통해 이 인스턴스에 접근할 수 있다.
printStackTrace() : 예외 발생 시 호출스택에 있었던 메서드의 정보와 예외 메시지를 화면에 출력
getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.
멀티 catch블럭
jdk1.8부터 여러 catch블럭을 '|'기호를 이용해 하나로 합칠 수 있다. |는 논리 연산자가 아니라 기호이다.
예시try{ ~~~ } catch (Exception1 e1){ e1.printStackTrace(); } catch (Exception2 e2){ e2.printStackTrace(); } >> try{ ~~~ } catch (Exception1 | Exception2 e){ e.printStackTrace(); }
만약 | 기호로 연결된 예외 클래스들이 조상, 자손의 관계라면 컴파일 에러가 발생한다.
그럴거면 그냥 조상 하나에 대한 예외처리만 하면 된다.
근데 멀티 블럭에서는 어떤 예외가 발생한 것인지 자세히 알 수 없다.
instanceof로 참조변수의 타입을 확인하면 되지만 그렇게까지 하면 멀티블럭을 쓰는 의미가 없다
예외 발생시키기
키워드 throw를 통해 프로그래머가 고의로 예외를 발생시킬 수 있으며, 방법은 아래 순서를 따른다
1. 연산자 new를 통해 발생시키려는 예외 클래스 객체를 만듬
Exception e = new Exception("고의 발생");
2. 키워드 throw를 통해 예외 발생
throw e;
>> throw new Exception("고의 발생"); << 이렇게 한줄로도 가능
메서드에 예외 선언
메서드 선언부에 throws를 사용해서 예외를 적어준다. 예외가 여러개면 쉼표로 구분한다
void method() throw Exception1, Exception2, ... ExceptionN{ // 메서드 내용 }
참고 예외 발생 키워드 throw와 메서드의 예외 선언 키워드 throws를 잘 구분하자
모든 예외의 최고조상인 Exception클래스를 메서드에 선언 하면 이 메서드는 모든 종류의 예외가 발생할 가능성이 있다는 뜻void method() throws Exception{ // ~~~~ }
이렇게 선언부에 알려주면 상속받은 메서드를 이용(주로 오버라이딩)하는 입장에서 어떤 예외들을 처리해야 하는 지 알 수 있다.
근데 보통 RuntimeException 클래스 자식들은 선언하지 않는다.
선언한다고 문제가 되는 건 아닌데, 보통 반드시 처리해야만 하는 예외들만 명시한다.
근데 이것은 예외를 직접 처리하지 않고, 처리해달라고 떠넘기는 것이라 마지막에 실행되면 main 메서드에서까지 처리되지 않으면 프로그램이 비정상 종료된다class Exception{ public static void main(String args[]){ method1(); // 같은 클래스 내의 static 메서드라 객체 없이 사용 가능 } static void method1(){ try{ throw new Exception(); } catch (Exception e){ sysout("method1 메서드에서 예외처리"); } } }
이렇게 처리하면 main 메서드에서는 method1에서 예외가 발생했다는 사실을 전혀 모르게 됨
상황에 맞게 main에서 처리하든 method1에서 처리하든 잘 써보자
메서드에 호출 시 넘겨받아야 할 값에 문제가 있어서,
값을 다시 받아야 하는 경우에는 예외를 메서드에 선언해서 호출한 메서드에서 처리해야 한다.
finally 블럭
예외 발생 여부에 관계 없이 반드시 실행되는 블럭
예시try{ } catch(Exception e){ } finally{ }
자동 자원 반환 - try - with - resources문
입출력에 사용되는 클래스 중에선 사용 후에 닫아줘야 하는 것들이 있다.
그래야 사용했던 자원(resources)이 반환되기 때문이다.
try{ fis = new FileInputStream("score.dat"); dis = new DataInputStream(fis); } catch(IOException ie){ ie.printStackTrace(); } finally{ dis.close(); // 예외가 발생하더라도 dis가 닫히도록 finally블럭에 넣음 }
이 코드는 별 문제 없어보이지만 close()가 예외를 발생 시킬 수 있다. 그래서 아래처럼 바꿔야 한다try{ fis = new FileInputStream("score.dat"); dis = new DataInputStream(fis); } catch(IOException ie){ ie.printStackTrace(); } finally{ try{ if(dis != null) dis.close(); } catch(IOException ie){ ie.printStackTrace(); } }
이렇게 하면 보기도 안좋고, try와 finally에서 동시에 예외가 발생하면 try의 예외는 무시된다.
try - with - resources문 괄호 안에 객체를 생성하는 문장을 넣으면 이 객체는 따로 close() 호출 안해도 try 블럭을 벗어날 때, 자동으로 close()를 호출한다try(FileInputStream fis = new FileInputStream("score.dat"));{ ~~~ } catch( ~~){ ~~ }
이렇게 하려면 AutoCloseable라는 인터페이스를 구현한 클래스여야 한다
사용자 정의 예외 만들기
기존의 정의된 예외 클래스 외에 필요에 따라 프로그래머가 새로운 예외 클래스를 정의하여 사용할 수 있다.
class MyException extends Exception{ MyException(String msg){ super(msg); } }
필요하다면, 멤버 변수나 메서드도 추가할 수 있다
예외 되던지기(exception re-throwing)
한 메서드에 발생할 수 있는 예외가 여러개면, 몇 개는 try-catch문을 통해 자체적으로 처리하고,
나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써, 양쪽에서 나눠 처리할 수 있다.
심지어 단 하나의 예외도 양쪽에서 처리할 수 있다.
예외를 처리한 후, 인위적으로 다시 발생시키는 방법을 통하는데 이를 '예외 되던지기'라고 한다
예외 발성 가능성이 있는 메서드에서 try-catch문을 통해 예외를 처리하고, catch문에서 필요한 작업을 행한 후,
throw문을 이용해 예외를 다시 발생시킨다.
다시 발생된 예외는 이 메서드를 호출한 메서드에게 전달되고,
호출한 메서드의 try-catch문에서 예외를 또다시 처리한다.
이 방법은 하나의 예외에 대해서 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해줘야 할 작업이 있을 때 사용된다.
주의
예외가 발생할 메서드에서는 try-catch문을 사용해 예외처리를 함과 동시에 메서드의 선언부에 발생할 예외를 throws에 지정해줘야 한다.
반환값이 있는 return문의 경우, catch블럭에도 return문이 있어야 한다. 예외가 발생했을 경우에도 값을 반환해야하기 때문
연결된 예외(chained exception)
한 예외가 다른 예외를 발생시킬 수도 있다. 예외 A가 예외 B를 발생시켰다면, A를 B의 '원인 예외'라고 한다
예시try{ startInstall(); copyFiles(); } catch (SpaceException e){ InstallException ie = new InstallException("설치 중 예외발생"); //예외 생성 ie.initCause(e); // InstallException의 원인 예외를 SpaceException로 지정 throw ie; // InstallException 발생시키기 } catch(MemoryException me){ ~~~~ }
728x90반응형'Java' 카테고리의 다른 글
Java java.lang - 2, String 클래스 (1) 2024.02.25 Java java.lang - 1, Object 클래스 (1) 2024.02.25 Java 인터페이스 (1) 2024.02.25 Java 클래스의 다양한 형태(추상 클래스, 내부 클래스, 익명 클래스) (1) 2024.02.25 Java 제어자 (0) 2024.02.25