🌑 예외처리 (exception handling)
✔️ 프로그램 오류
🔹JVM의 예외처리
- 메서드에서 예외(exception)이 발생하면, 메서드가 예외 객체(인스턴스)를 생성해서 JVM에게 전달!
- 해당 예외를 발생시킨 메서드에서 예외 처리 코드(try-catch)(= Exception Handler)를 찾고, 예외 처리 코드가 없는 경우 예외 객체를 호출 스택의 하위 메서드로 예외 전파
- 호출 스택의 모든 메서드가 예외를 처리할 수 없는 경우(catch 블록이 없는 경우)
JVM의 Uncaught Exception Handler(기본 예외 처리기)를 사용하여 예외를 처리 - Uncaught Exception Handler는 예외 객체에 대한 정보를 출력하고 프로그램을 비정상적으로 종료

- Compile error
: 컴파일 시에 발생하는 에러 - Runtime error
: 실행 시에 발생하는 에러 - Logical error
: 실행은 되지만, 의도와 다르게 작동하는 것.

- Exception class의 계층구조
- 모든 class의 조상은 Object class이므로 Exception과 Error class 역시 Object class의 Sub class이다.
- Exception class ( ➞ Checked Exception )
➠ 사용자의 실수와 같은 외적인 요인에 의해 발생하는 Exception
➠ 예외 처리를 강제 - RuntimeException class ( ➞ Unchecked Exception )
➠ 프로그래머의 실수로 발생하는 Exception
➠ 예외 처리를 강제 X
➠ 예외 처리를 강제했다면 아래와 같이 참조 변수와 배열이 사용되는 모든 곳에 예외처리가 필요
- Exception class ( ➞ Checked Exception )
- 모든 class의 조상은 Object class이므로 Exception과 Error class 역시 Object class의 Sub class이다.
try {
int[] arr = new int[10];
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException ae) {
...
} catch (NullPointerException ne) {
...
}
✔️ 예외 처리 (Exception handling)
🔺 실행 중 (Runtime) Error는 어쩔 수 없지만 Exception는 이에 대한 처리를 미리 해주어야 한다 !
- 프로그램 실행 시 발생할 수 있는 예외에 대비한 코드를 작성
➠ 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지
🔹try - catch (- finally)
- if 문과 달리, 괄호 { } 생략 불가능 !
try {
// Exception가 발생할 가능성이 있는 코드
try { ... } catch (Exception e) { }
} catch (Exception e) {
// Exception이 발생했을 때 실행되는 코드
try { ... } catch (Exception e) { } // Compile error !
} finally {
// Exception 발생 여부와 상관 없이 무조건 실행되는 코드
}
- try 문
- 예외 발생하지 않은 경우, try-catch 문에서 빠져나온다.
➠ 발생한 경우, Exception이 발생한 코드의 뒷부분은 실행되지 않고
발생한 Exception과 일치하는 catch 블럭을 찾는다 !
- 예외 발생하지 않은 경우, try-catch 문에서 빠져나온다.
- catch 문
- catch 블럭의 괄호 내에 선언된 변수는 해당 catch 블럭 내에서만 유효
➠ 각각의 catch 블럭 내에선 같은 이름의 참조변수 (' e ') 사용 가능
➠ 그러나, catch 블럭 내에 또다른 try-catch 문이 포함된 경우엔
같은 이름의 참조변수 (' e ')는 사용 X - 멀티 catch 문 (JDK1.7 ~ )
➠ ' | ' 기호 이용
➠ 여러 개의 예외들을 한 번에 처리하는 것이기 때문에,
각 예외마다 세세하게 제어하고 싶다면 if 문과 instanceOf 연산자 이용 !
➠ 다형성의 장점을 누릴 수 있다.
- catch 블럭의 괄호 내에 선언된 변수는 해당 catch 블럭 내에서만 유효
try {
...
} catch (NullPointException | ArrayIndexOutOfBoundsException e) { // 멀티 catch 문
if(e instanceOf NullPointException) {
...
} else if(e instanceOf ArrayIndexOutOfBoundsException) {
...
}
}
▪️ try - catch 블록 안의 변수는 local 변수처럼 취급 !
- finally 문
- Error 발생 여부와 관계 없이 무조건 실행되는 코드
➠ 그 전에 return 문이 실행되더라도 finally 블럭의 코드가 실행된 후,
현재 실행중인 메서드를 종료 !
- Error 발생 여부와 관계 없이 무조건 실행되는 코드
🔹try - with - resourses ( 자동 자원 반환 - JDK1.7 ~ )
🔺 입출력에 사용되는 class 중에서는 사용한 후에 꼭 닫아 줘야 하는 것들이 있다.
➠ 사용했던 자원(resources)이 반환되기 위해 !
try {
fis = enw FileInputStream("score.dat");
dis = new DataInputStream(fis); //파일로부터 데이터를 읽는 코드
...
} catch (IOException ie) {
ie.printStackTrace();
} finally {
// dis.close(); // (1) 작업중 예외가 발생하더라도, dis가 닫히도록 finally 블럭에 넣은 것
try { // (2) close() 예외 처리
if (dis != null) dis.close();
} catch (IOException ie) {
ie.printStackTrace();
}
}
- ' close() '가 예외를 발생시킬 수 있다는 문제
- 가독성도 떨어지고, try 블럭과 finally 블럭에서 모두 예외가 발생하면, try 블럭의 예외는 무시되는 문제 !
➠ 이를 해결하기 위해 try - with - resources 문이 추가된 것이다.
// 괄호 () 안에 두 문장 이상 넣을 경우 ' ; ' 로 구분
try (FileInputStream fis = new FileInputStream("score.dat");
DataInputStream dis = new DataInputStream(fis)) {
while(true) {
score = dis.readInt();
System.out.println(score);
sum += score;
}
} catch (EOFException e) {
System.out.println("점수의 총합은 " + sum + "입니다.");
} catch (IOException e) {
ie.printStackTrace();
}
➠ 괄호 ( ) 안에서 객체를 생성하면, 이 객체는 따로 ' close() '를 호출하지 않아도
try 블럭을 벗어나는 순간 ' close() '가 자동 호출 !
그 다음에 catch블럭 또는 finally 블럭이 수행된다.
➠ 괄호 ( ) 안에는 변수 선언도 가능하며, try 블럭 내에서만 사용 가능하다.
🔺두 개 이상의 예외는 동시에 발생할 수는 없기 때문에,
하나를 실제 발생한 예외로, 나머지를 ' 억제된 예외 '로 다룬다.
void addSuppressed (Throwable exception) // 억제된 예외를 추가
Throwable[] getSuppressed() // 억제된 예외 (배열)를 반환
✔️ 예외의 발생
🔹 Exception class의 참조변수
try {
System.out.println(3);
System.out.println(0.0); // ArithmeticException 발생
System.out.println(4); // 실행 X
} catch (Exception e) { // ArithmeticException 대신 Exception 사용
System.out.println(5);
}
System.out.println(6);
3
5
6
🔻 ArithmeticException class는 Exception class의 Sub class이므로
ArithmeticException class의 instance와 Exception class와의 instanceOf 연산결과가 true가 되어
Exception class 타입의 참조변수를 선언해도 발생한 예외가 정상적으로 처리되는 것 !
try {
System.out.println(3);
System.out.println(0.0); // ArithmeticException 발생
System.out.println(4); // 실행 X
} catch (ArithmeticException ae) {
if (ae instanceof ArithmeticException)
System.out.println("true");
System.out.println("ArithmeticException");
} catch (Exception e) { // ArithmeticException을 제외한 모든 예외가 처리
System.out.println("Exception");
}
System.out.println(6);
3
true
ArithmeticException
6
🔹 예외 메시지 출력
} catch (Exception e) {
...
}
🔻 Exception ➙ 변수의 class 타입 / e ➙ 변수 ( ≒ 메소드의 매개변수)
- e.printStackTrace()
- Exception 발생 당시의 호출 스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 출력한다.
- e.getMessage()
- 발생한 Exception class의 instance에 저장된 메시지를 얻는다.
✔️ 예외 던지기
🔹 throw
Exception e = new Exception("고의로 발생시켰다.");
throw e; // 예외를 발생시켰다
// throw new Exception("고의로 발생시켰다.");
- 강제로 예외를 발생시키는 것
- 연산자 new를 이용해서 발생시키려는 Exception class의 객체를 생성
- 키워드 throw를 이용해서 예외를 발생시킨다.
class ExceptionThrow {
public static void main(String args[]) {
try {
Scanner s = new Scanner(System.in);
System.out.print("음수를 제외한 숫자만 입력하세요 : ");
int num = s.nextInt();
if (num < 0) {
// 음수를 입력한 경우, 강제로 에러 발생시킨다!
throw new ArithmeticException("음수가 입력됐습니다.");
} catch (ArithmeticException ae) {
System.out.println("Error massage : " + e.getMessage());
e.printStackTrace();
} finally {
System.out.println("프로그램을 종료합니다.");
}
}
}
에러 메시지 : 음수가 입력됐습니다.
java.lang.Exception: 음수가 입력됐습니다.
at ExceptionThrow.main(ExceptionThrow.java:4)
프로그램을 종료합니다.
➠ 예외 class의 instance를 생성할 때, 생성자에 String(문자열)을 넣어주면,
생성된 instance에 해당 String이 메시지로 저장된다.
class ExceptionThrow {
public static void main(String args[]) {
throw new Exception(); // Compile Error!
throw new RuntimeException(); // OK
}
}
➠ RuntimeException은 ' Unchecked Exception ' 이기 때문에,
강제로 발생시킨 RuntimeException에 대한 예외 처리를 해주지 않아도 정상적으로 실행된다.
🔹 throws (예외 떠넘기기)
void method() throws Exception {
...
}
➠ ' throws Exception1, Exception2, ... ExceptionN ' 처럼 ' , ' 를 이용하면 여러 개의 예외를 선언 가능
➠ 모든 예외의 최상위 Super class인 Exception class를 선언해주면,
이 메서드는 모든 종류의 예외가 발생할 가능성이 있다는 의미이다.
➠ Overriding을 할 경우 예외의 개수뿐만 아니라 그 Sub class 타입의 예외까지 고려 !
➠ Java API 문서를 통해 사용하고자 하는 메서드의 선언부와 'Throws:'를 보고,
이 메서드에서는 어떤 예외가 발생할 수 있으며 반드시 처리해줘야 하는 예외는 어떤 것인지 확인.
public static void main(String[] args) {
method1();
method2();
}
public static void method1() {
try {
throw new ClassNotFoundException("Error");
} catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
}
}
public static void method1() {
try {
throw new NullPointerException("Error");
} catch (NullPointerException e) {
System.out.println(e.getMessage());
}
}
- 메서드 내에서 처리해야 할 예외을 메서드에 선언하여, 해당 메서드를 호출한 메서드에서 처리!
➠ 즉, throws 키워드는 메소드를 호출한 곳에서 예외 처리를 하도록 예외를 떠넘기는 역할이다.
➠ 하지만, 예외가 처리되는 것이 아니고 예외를 단순히 전달만 하는 것이기 때문에,
어느 한 곳에서는 반드시 try - catch 문으로 예외 처리를 해주어야 한다.
public static void main(String[] args) {
try {
method1();
method2();
} catch (ClassNotFoundException | NullPointerException e) {
System.out.println(e.getMessage());
}
}
public static void method1() throws ClassNotFoundException {
throw new ClassNotFoundException("Error");
}
public static void method1() throws NullPointerException {
throw new NullPointerException("Error");
}
➠ 코드 간소화
➠ 메서드를 사용하기 전에 어떠한 예외들이 처리되어야 하는지 쉽게 알 수 있어 견고한 코드 작성 가능 !
🔹 예외 되던지기 (exception re-throwing)
- 예외를 처리한 후에 인위적으로 다시 발생시키는 것
➠ 하나의 예외를 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두 처리해야 할 작업이 있을 때 사용
- 예외가 발생할 가능성이 있는 메서드에서 try - catch 문으로 예외를 처리한다.
- 예외를 처리하고, catch 문에서 필요한 작업을 행한 후에 throw 문으로 예외를 다시 발생시킨다.
- 다시 발생한 예외가 이 메서드를 호출한 메서드에게 전달되고
호출한 메서드의 try - catch 문에서 예외를 또다시 처리한다.
public static void main(String[] args) {
try {
method();
} catch (Exception e) {
System.out.println("main 메서드에서 예외가 처리되었습니다.");
}
}
static void method() throws Exception {
try {
throw new Exception();
} catch (Exception e) {
System.out.println("method 메서드에서 예외가 처리되었습니다.");
throw e; // 다시 예외를 발생시킨다.
}
}
method 메서드에서 예외가 처리되었습니다.
main 메서드에서 예외가 처리되었습니다.
🔻 반환값이 있는 return 문의 경우, 예외가 발생해도 값을 반환해야 하기 때문에,
catch 블럭에도 return 문이 있어야 한다 !
➠ catch 블럭에서 예외 되던지기를 해서 호출한 메서드로 예외를 전달하면, return 문 없이도 가능
static int method() throws Exception {
try {
System.out.println("method()이 호출되었습니다.");
return 0; // 현재 진행중인 메서드를 종료
} catch (Exception e) {
e.printStackTrace();
// return 1;
throw new Exception(); // return문 대신 예외를 호출한 메서드로 다시 발생시킨 예외 전달
} finally {
System.out.println("method()의 finally 블럭이 실행되었습니다.");
}
🔹 연결된 예외 (chained exception)
- 예외 A가 다른 예외 B를 발생시켰을 때,
A ⭠ B의 '원인 예외(cause exception)'
➠ 여러가지 예외를 하나의 큰 부류의 예외로 묶어서 다루기 위한 방법
try {
startInstall(); // A(SpaceException) 발생
} catch (SpaceException e) {
InstallException ie = new InstallException("설치중 예외발생"); // 예외 생성
ie.initCause(e); // B(InstallException)의 원인 예외를 A(SpaceException)으로 지정
throw ie; // B(InstallException)시킨다.
}
Throwable initCause(Throwable cause) // 지정한 예외를 원인 예외로 등록
Throwable getCause() // 원인 예외를 반환
🔻 ' initCause() '는 Exception class의 Super class인
Throwable class에 정의되어 있기 때문에 모든 예외에서 사용 가능 !
🔺 ' Checked Exception '이 발생해도 예외를 처리할 수 없는 상황이 발생
➠ 의미없는 try - catch 문 추가 X
➠ Checked Exception을 Unchecked Exception로 바꾸면 예외 처리가 선택적 !
try {
startInstall();
} catch (SpaceException e) {
InstallException ie = new InstallException("설치중 예외발생");
ie.initCause(e);
throw ie;
} catch (MemoryException me) {
InstallException ie = new InstallException("설치중 예외발생");
ie.RuntimeException(me); // initCause() 대신 RuntimeException의 생성자 사용
throw ie;
}
static void startInstall() throws MemoryException {
if(!enoughSpace())
throw new SpaceException("설치할 공간이 부족합니다.");
if(!enoughMemory())
throw new RuntimeException(new MemoryException("메모리가 부족합니다."));
}
🔻MemoryExcption은 Exception class의 Sub class이기 때문에 반드시 예외 처리를 해줘야 하지만,
이 예외를 RuntimeException으로 감싸버렸기 때문에 Unchecked Exception이 되었다.
➠ 더 이상 ' startInstall() ' 선언부에 MemoryException을 선언하지 않아도 되는 것 !
✔️ 사용자정의 예외 (custom exception)
🔹 custom exception class는 Exception class를 상속하여 만들 수 있다.
class MyErrException extends Exception {
private String msg;
// custom exception class 생성자
public MyErrException(String msg) {
super(msg); // 부모 Exception 클래스 생성자도 호출
this.msg = msg;
}
// custom exception class 메세지 출력
public void printMyMessage() {
System.out.println(this.msg);
}
}
public class Main {
public static void main(String[] args) {
try {
throw new MyErrException("나의 custom exception class 메시지"); // 커스텀 예외 클래스 발생
} catch (MyErrException e) {
e.printMyMessage(); // custom exception class의 메서드 실행
e.printStackTrace(); // 상속한 Super class의 메서드 실행
}
}
}
🔸 Exception class (' Checked Exception ')처럼 예외 처리의 여부를 강제하는 것이 아니라, RuntimeException class (' Unchecked Exception ')처럼 예외 처리를 선택적으로 할 수 있도록 Exception class를 상속받는 것이 아닌 RuntimeException을 상속받아서 작성하는 경우
▼ Study📋
☑️ JVM의 예외처리
☑️ 멀티 catch 문을 쓰는 이유
☑️ 예외되던지기 (exception re-throwing)를 쓰는 경우
🌑 예외처리 (exception handling)
✔️ 프로그램 오류
🔹JVM의 예외처리
- 메서드에서 예외(exception)이 발생하면, 메서드가 예외 객체(인스턴스)를 생성해서 JVM에게 전달!
- 해당 예외를 발생시킨 메서드에서 예외 처리 코드(try-catch)(= Exception Handler)를 찾고, 예외 처리 코드가 없는 경우 예외 객체를 호출 스택의 하위 메서드로 예외 전파
- 호출 스택의 모든 메서드가 예외를 처리할 수 없는 경우(catch 블록이 없는 경우)
JVM의 Uncaught Exception Handler(기본 예외 처리기)를 사용하여 예외를 처리 - Uncaught Exception Handler는 예외 객체에 대한 정보를 출력하고 프로그램을 비정상적으로 종료

- Compile error
: 컴파일 시에 발생하는 에러 - Runtime error
: 실행 시에 발생하는 에러 - Logical error
: 실행은 되지만, 의도와 다르게 작동하는 것.

- Exception class의 계층구조
- 모든 class의 조상은 Object class이므로 Exception과 Error class 역시 Object class의 Sub class이다.
- Exception class ( ➞ Checked Exception )
➠ 사용자의 실수와 같은 외적인 요인에 의해 발생하는 Exception
➠ 예외 처리를 강제 - RuntimeException class ( ➞ Unchecked Exception )
➠ 프로그래머의 실수로 발생하는 Exception
➠ 예외 처리를 강제 X
➠ 예외 처리를 강제했다면 아래와 같이 참조 변수와 배열이 사용되는 모든 곳에 예외처리가 필요
- Exception class ( ➞ Checked Exception )
- 모든 class의 조상은 Object class이므로 Exception과 Error class 역시 Object class의 Sub class이다.
try {
int[] arr = new int[10];
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException ae) {
...
} catch (NullPointerException ne) {
...
}
✔️ 예외 처리 (Exception handling)
🔺 실행 중 (Runtime) Error는 어쩔 수 없지만 Exception는 이에 대한 처리를 미리 해주어야 한다 !
- 프로그램 실행 시 발생할 수 있는 예외에 대비한 코드를 작성
➠ 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지
🔹try - catch (- finally)
- if 문과 달리, 괄호 { } 생략 불가능 !
try {
// Exception가 발생할 가능성이 있는 코드
try { ... } catch (Exception e) { }
} catch (Exception e) {
// Exception이 발생했을 때 실행되는 코드
try { ... } catch (Exception e) { } // Compile error !
} finally {
// Exception 발생 여부와 상관 없이 무조건 실행되는 코드
}
- try 문
- 예외 발생하지 않은 경우, try-catch 문에서 빠져나온다.
➠ 발생한 경우, Exception이 발생한 코드의 뒷부분은 실행되지 않고
발생한 Exception과 일치하는 catch 블럭을 찾는다 !
- 예외 발생하지 않은 경우, try-catch 문에서 빠져나온다.
- catch 문
- catch 블럭의 괄호 내에 선언된 변수는 해당 catch 블럭 내에서만 유효
➠ 각각의 catch 블럭 내에선 같은 이름의 참조변수 (' e ') 사용 가능
➠ 그러나, catch 블럭 내에 또다른 try-catch 문이 포함된 경우엔
같은 이름의 참조변수 (' e ')는 사용 X - 멀티 catch 문 (JDK1.7 ~ )
➠ ' | ' 기호 이용
➠ 여러 개의 예외들을 한 번에 처리하는 것이기 때문에,
각 예외마다 세세하게 제어하고 싶다면 if 문과 instanceOf 연산자 이용 !
➠ 다형성의 장점을 누릴 수 있다.
- catch 블럭의 괄호 내에 선언된 변수는 해당 catch 블럭 내에서만 유효
try {
...
} catch (NullPointException | ArrayIndexOutOfBoundsException e) { // 멀티 catch 문
if(e instanceOf NullPointException) {
...
} else if(e instanceOf ArrayIndexOutOfBoundsException) {
...
}
}
▪️ try - catch 블록 안의 변수는 local 변수처럼 취급 !
- finally 문
- Error 발생 여부와 관계 없이 무조건 실행되는 코드
➠ 그 전에 return 문이 실행되더라도 finally 블럭의 코드가 실행된 후,
현재 실행중인 메서드를 종료 !
- Error 발생 여부와 관계 없이 무조건 실행되는 코드
🔹try - with - resourses ( 자동 자원 반환 - JDK1.7 ~ )
🔺 입출력에 사용되는 class 중에서는 사용한 후에 꼭 닫아 줘야 하는 것들이 있다.
➠ 사용했던 자원(resources)이 반환되기 위해 !
try {
fis = enw FileInputStream("score.dat");
dis = new DataInputStream(fis); //파일로부터 데이터를 읽는 코드
...
} catch (IOException ie) {
ie.printStackTrace();
} finally {
// dis.close(); // (1) 작업중 예외가 발생하더라도, dis가 닫히도록 finally 블럭에 넣은 것
try { // (2) close() 예외 처리
if (dis != null) dis.close();
} catch (IOException ie) {
ie.printStackTrace();
}
}
- ' close() '가 예외를 발생시킬 수 있다는 문제
- 가독성도 떨어지고, try 블럭과 finally 블럭에서 모두 예외가 발생하면, try 블럭의 예외는 무시되는 문제 !
➠ 이를 해결하기 위해 try - with - resources 문이 추가된 것이다.
// 괄호 () 안에 두 문장 이상 넣을 경우 ' ; ' 로 구분
try (FileInputStream fis = new FileInputStream("score.dat");
DataInputStream dis = new DataInputStream(fis)) {
while(true) {
score = dis.readInt();
System.out.println(score);
sum += score;
}
} catch (EOFException e) {
System.out.println("점수의 총합은 " + sum + "입니다.");
} catch (IOException e) {
ie.printStackTrace();
}
➠ 괄호 ( ) 안에서 객체를 생성하면, 이 객체는 따로 ' close() '를 호출하지 않아도
try 블럭을 벗어나는 순간 ' close() '가 자동 호출 !
그 다음에 catch블럭 또는 finally 블럭이 수행된다.
➠ 괄호 ( ) 안에는 변수 선언도 가능하며, try 블럭 내에서만 사용 가능하다.
🔺두 개 이상의 예외는 동시에 발생할 수는 없기 때문에,
하나를 실제 발생한 예외로, 나머지를 ' 억제된 예외 '로 다룬다.
void addSuppressed (Throwable exception) // 억제된 예외를 추가
Throwable[] getSuppressed() // 억제된 예외 (배열)를 반환
✔️ 예외의 발생
🔹 Exception class의 참조변수
try {
System.out.println(3);
System.out.println(0.0); // ArithmeticException 발생
System.out.println(4); // 실행 X
} catch (Exception e) { // ArithmeticException 대신 Exception 사용
System.out.println(5);
}
System.out.println(6);
3
5
6
🔻 ArithmeticException class는 Exception class의 Sub class이므로
ArithmeticException class의 instance와 Exception class와의 instanceOf 연산결과가 true가 되어
Exception class 타입의 참조변수를 선언해도 발생한 예외가 정상적으로 처리되는 것 !
try {
System.out.println(3);
System.out.println(0.0); // ArithmeticException 발생
System.out.println(4); // 실행 X
} catch (ArithmeticException ae) {
if (ae instanceof ArithmeticException)
System.out.println("true");
System.out.println("ArithmeticException");
} catch (Exception e) { // ArithmeticException을 제외한 모든 예외가 처리
System.out.println("Exception");
}
System.out.println(6);
3
true
ArithmeticException
6
🔹 예외 메시지 출력
} catch (Exception e) {
...
}
🔻 Exception ➙ 변수의 class 타입 / e ➙ 변수 ( ≒ 메소드의 매개변수)
- e.printStackTrace()
- Exception 발생 당시의 호출 스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 출력한다.
- e.getMessage()
- 발생한 Exception class의 instance에 저장된 메시지를 얻는다.
✔️ 예외 던지기
🔹 throw
Exception e = new Exception("고의로 발생시켰다.");
throw e; // 예외를 발생시켰다
// throw new Exception("고의로 발생시켰다.");
- 강제로 예외를 발생시키는 것
- 연산자 new를 이용해서 발생시키려는 Exception class의 객체를 생성
- 키워드 throw를 이용해서 예외를 발생시킨다.
class ExceptionThrow {
public static void main(String args[]) {
try {
Scanner s = new Scanner(System.in);
System.out.print("음수를 제외한 숫자만 입력하세요 : ");
int num = s.nextInt();
if (num < 0) {
// 음수를 입력한 경우, 강제로 에러 발생시킨다!
throw new ArithmeticException("음수가 입력됐습니다.");
} catch (ArithmeticException ae) {
System.out.println("Error massage : " + e.getMessage());
e.printStackTrace();
} finally {
System.out.println("프로그램을 종료합니다.");
}
}
}
에러 메시지 : 음수가 입력됐습니다.
java.lang.Exception: 음수가 입력됐습니다.
at ExceptionThrow.main(ExceptionThrow.java:4)
프로그램을 종료합니다.
➠ 예외 class의 instance를 생성할 때, 생성자에 String(문자열)을 넣어주면,
생성된 instance에 해당 String이 메시지로 저장된다.
class ExceptionThrow {
public static void main(String args[]) {
throw new Exception(); // Compile Error!
throw new RuntimeException(); // OK
}
}
➠ RuntimeException은 ' Unchecked Exception ' 이기 때문에,
강제로 발생시킨 RuntimeException에 대한 예외 처리를 해주지 않아도 정상적으로 실행된다.
🔹 throws (예외 떠넘기기)
void method() throws Exception {
...
}
➠ ' throws Exception1, Exception2, ... ExceptionN ' 처럼 ' , ' 를 이용하면 여러 개의 예외를 선언 가능
➠ 모든 예외의 최상위 Super class인 Exception class를 선언해주면,
이 메서드는 모든 종류의 예외가 발생할 가능성이 있다는 의미이다.
➠ Overriding을 할 경우 예외의 개수뿐만 아니라 그 Sub class 타입의 예외까지 고려 !
➠ Java API 문서를 통해 사용하고자 하는 메서드의 선언부와 'Throws:'를 보고,
이 메서드에서는 어떤 예외가 발생할 수 있으며 반드시 처리해줘야 하는 예외는 어떤 것인지 확인.
public static void main(String[] args) {
method1();
method2();
}
public static void method1() {
try {
throw new ClassNotFoundException("Error");
} catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
}
}
public static void method1() {
try {
throw new NullPointerException("Error");
} catch (NullPointerException e) {
System.out.println(e.getMessage());
}
}
- 메서드 내에서 처리해야 할 예외을 메서드에 선언하여, 해당 메서드를 호출한 메서드에서 처리!
➠ 즉, throws 키워드는 메소드를 호출한 곳에서 예외 처리를 하도록 예외를 떠넘기는 역할이다.
➠ 하지만, 예외가 처리되는 것이 아니고 예외를 단순히 전달만 하는 것이기 때문에,
어느 한 곳에서는 반드시 try - catch 문으로 예외 처리를 해주어야 한다.
public static void main(String[] args) {
try {
method1();
method2();
} catch (ClassNotFoundException | NullPointerException e) {
System.out.println(e.getMessage());
}
}
public static void method1() throws ClassNotFoundException {
throw new ClassNotFoundException("Error");
}
public static void method1() throws NullPointerException {
throw new NullPointerException("Error");
}
➠ 코드 간소화
➠ 메서드를 사용하기 전에 어떠한 예외들이 처리되어야 하는지 쉽게 알 수 있어 견고한 코드 작성 가능 !
🔹 예외 되던지기 (exception re-throwing)
- 예외를 처리한 후에 인위적으로 다시 발생시키는 것
➠ 하나의 예외를 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두 처리해야 할 작업이 있을 때 사용
- 예외가 발생할 가능성이 있는 메서드에서 try - catch 문으로 예외를 처리한다.
- 예외를 처리하고, catch 문에서 필요한 작업을 행한 후에 throw 문으로 예외를 다시 발생시킨다.
- 다시 발생한 예외가 이 메서드를 호출한 메서드에게 전달되고
호출한 메서드의 try - catch 문에서 예외를 또다시 처리한다.
public static void main(String[] args) {
try {
method();
} catch (Exception e) {
System.out.println("main 메서드에서 예외가 처리되었습니다.");
}
}
static void method() throws Exception {
try {
throw new Exception();
} catch (Exception e) {
System.out.println("method 메서드에서 예외가 처리되었습니다.");
throw e; // 다시 예외를 발생시킨다.
}
}
method 메서드에서 예외가 처리되었습니다.
main 메서드에서 예외가 처리되었습니다.
🔻 반환값이 있는 return 문의 경우, 예외가 발생해도 값을 반환해야 하기 때문에,
catch 블럭에도 return 문이 있어야 한다 !
➠ catch 블럭에서 예외 되던지기를 해서 호출한 메서드로 예외를 전달하면, return 문 없이도 가능
static int method() throws Exception {
try {
System.out.println("method()이 호출되었습니다.");
return 0; // 현재 진행중인 메서드를 종료
} catch (Exception e) {
e.printStackTrace();
// return 1;
throw new Exception(); // return문 대신 예외를 호출한 메서드로 다시 발생시킨 예외 전달
} finally {
System.out.println("method()의 finally 블럭이 실행되었습니다.");
}
🔹 연결된 예외 (chained exception)
- 예외 A가 다른 예외 B를 발생시켰을 때,
A ⭠ B의 '원인 예외(cause exception)'
➠ 여러가지 예외를 하나의 큰 부류의 예외로 묶어서 다루기 위한 방법
try {
startInstall(); // A(SpaceException) 발생
} catch (SpaceException e) {
InstallException ie = new InstallException("설치중 예외발생"); // 예외 생성
ie.initCause(e); // B(InstallException)의 원인 예외를 A(SpaceException)으로 지정
throw ie; // B(InstallException)시킨다.
}
Throwable initCause(Throwable cause) // 지정한 예외를 원인 예외로 등록
Throwable getCause() // 원인 예외를 반환
🔻 ' initCause() '는 Exception class의 Super class인
Throwable class에 정의되어 있기 때문에 모든 예외에서 사용 가능 !
🔺 ' Checked Exception '이 발생해도 예외를 처리할 수 없는 상황이 발생
➠ 의미없는 try - catch 문 추가 X
➠ Checked Exception을 Unchecked Exception로 바꾸면 예외 처리가 선택적 !
try {
startInstall();
} catch (SpaceException e) {
InstallException ie = new InstallException("설치중 예외발생");
ie.initCause(e);
throw ie;
} catch (MemoryException me) {
InstallException ie = new InstallException("설치중 예외발생");
ie.RuntimeException(me); // initCause() 대신 RuntimeException의 생성자 사용
throw ie;
}
static void startInstall() throws MemoryException {
if(!enoughSpace())
throw new SpaceException("설치할 공간이 부족합니다.");
if(!enoughMemory())
throw new RuntimeException(new MemoryException("메모리가 부족합니다."));
}
🔻MemoryExcption은 Exception class의 Sub class이기 때문에 반드시 예외 처리를 해줘야 하지만,
이 예외를 RuntimeException으로 감싸버렸기 때문에 Unchecked Exception이 되었다.
➠ 더 이상 ' startInstall() ' 선언부에 MemoryException을 선언하지 않아도 되는 것 !
✔️ 사용자정의 예외 (custom exception)
🔹 custom exception class는 Exception class를 상속하여 만들 수 있다.
class MyErrException extends Exception {
private String msg;
// custom exception class 생성자
public MyErrException(String msg) {
super(msg); // 부모 Exception 클래스 생성자도 호출
this.msg = msg;
}
// custom exception class 메세지 출력
public void printMyMessage() {
System.out.println(this.msg);
}
}
public class Main {
public static void main(String[] args) {
try {
throw new MyErrException("나의 custom exception class 메시지"); // 커스텀 예외 클래스 발생
} catch (MyErrException e) {
e.printMyMessage(); // custom exception class의 메서드 실행
e.printStackTrace(); // 상속한 Super class의 메서드 실행
}
}
}
🔸 Exception class (' Checked Exception ')처럼 예외 처리의 여부를 강제하는 것이 아니라, RuntimeException class (' Unchecked Exception ')처럼 예외 처리를 선택적으로 할 수 있도록 Exception class를 상속받는 것이 아닌 RuntimeException을 상속받아서 작성하는 경우