프로그램 이용자나 소스 코드 이용자는 소스 코드 작성자가 작성한 의도대로 행동하지 않을 수 있습니다. 이것을 예외 사항이라고 합니다. 이러한 예외 사항을 어떻게 대응할 수 있을까요? 이번 강의에서는 예외 사항을 대응하는 법에 대해 배웁니다.
예외 처리를 잘 하는 방법은 프로그램 사용자의 입장에서 생각하는 것입니다. 우리가 만든 프로그램은 스스로 사용하기 위해 만들 수도 있지만 대부분은 사용자를 위해서 만들게 됩니다. 실습과 연구를 많이 할수록 더 많은 문제에 미리 대응할 수 있습니다. 또한 지금 당장은 이해가 되지 않는다고 하더라도 코딩하다 보면 깨닫게 되는 부분도 있으므로 마음을 조급하게 먹지 않아도 됩니다.
ㅤ
ㅤ
모든 에러와 예외에 대한 최상위 클래스입니다. Object 클래스를 상속받고 있으며 Error와 Exception 하위 클래스들을 가지고 있습니다. 일반적으로는 해당 클래스를 직접적으로 이용할 일은 없습니다. 그러므로 있다는 것만 알고 넘어갑시다.
ㅤ
프로그램 실행 도중 해결할 수 있는 문제가 아니라 이후에 발견되어 처리해야 하는 문제들이 Error
클래스와 연관되어 있습니다. 코드상으로 별도의 예외 처리를 할 수 없습니다.
ㅤ
자바 가상 머신이 메모리가 부족하여 인스턴스를 할당할 수 없고 가비지 컬렉터가 메모리를 사용할 수 없을 때 발생합니다.
ㅤ
스레드의 Stack 메모리가 꽉 찼을 경우 발생합니다. (이후의 강의에서 Stack 메모리가 무엇인지 알게 됩니다)
ㅤ
자바 가상 머신에 문제가 생겼거나 실행되는데 리소스가 부족할 경우 발생됩니다.
ㅤ
프로그램 실행 도중 종료될 수 있는 문제와 연관되어 예외 처리를 선택적으로 하거나, 꼭 해야 하는 클래스들의 슈퍼 클래스입니다. Exception
에는 Checked Exception과 Unchecked Exception이 있으며 Checked Exception은 반드시 예외 처리를 해야지 컴파일이 가능하나 Unchecked Exception은 예외 처리가 선택입니다.
*Checked Exception과 Unchecked Exception을 구분하는 방법은 직접적으로 Exception 클래스를 상속받는 경우 Checked Exception 으로 보면 되며 RuntimeException을 상속받는 경우 Unchecked Exception으로 보면 쉽습니다.
ㅤ
반드시 try - catch
구문을 사용해야지 컴파일되는 예외이며 직접적으로 Exception
클래스를 상속받는 예외입니다.
ㅤ
InputStream.java
public abstract class InputStream implements Closeable {
...
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
...
}
입력과 출력과 관련된 예외이고 주로 throws를 통해 메서드에 선언되어 있으며 반드시 예외 처리를 해야 컴파일이 됩니다. 주로 InputStream
, OutputStream
관련 메서드를 사용할 때 IOException
과 관련한 예외처리를 해야 합니다.
ㅤ
Thread.java
public class Thread implements Runnable {
...
public static void sleep(long millis, int nanos) throws InterruptedException {
...
}
public final synchronized void join(long millis) throws InterruptedException {
...
}
...
}
스레드와 관련된 예외이고 주로 throws를 통해 메서드에 선언되어 있으며 반드시 예외 처리를 해야 컴파일이 됩니다. 주로 Thread
관련 메서드를 사용할 때 InterruptedException
과 관련한 예외처리를 해야 합니다.
ㅤ
강제적으로 try - catch
구문을 사용하지 않아도 되는 예외이며 프로그램 실행도중 발생할 수 있는 예외들로 구성되어 있습니다.
ㅤ
Unchecked Exception의 최상위 클래스로 Exception
클래스를 직접적으로 상속받은 클래스이나 특수하게 RuntimeException
을 상속 받는 예외들은 try - catch
구문에 대한 강제성이 없습니다.
이러한 예외들은 예외가 발생할 가능성이 높을 때 방어코드를 사용하거나 또는 try - catch
구문으로 회피하는 방식을 택할 수 있습니다.
ㅤ
String text = null;
System.out.println(text.isEmpty());
// NullPointerException 발생
RuntimeException
을 상속 받는 예외입니다. 참조 자료형 변수에 인스턴스가 저장되어 있지 않고 null 값이 저장되어 있을 때, 인스턴스 메서드를 호출하거나, 변수의 접근 시 발생할 수 있습니다.
ㅤ
class Warrior {
}
class SuperWarrior extends Warrior {
}
Warrior warrior = new Warrior();
SuperWarrior superWarrior = (SuperWarrior) warrior;
// 생성된 인스턴스는 Warrior 이므로 SuperWarrior로 형 변환시
// ClassCastException 발생
RuntimeException
을 상속 받는 예외입니다. 객체 형 변환시 올바르지 않은 객체로 형 변환 할 경우 발생할 수 있습니다.
ㅤ
예외 발생 가능성이 높다고 했을 때 어떻게 적절한 Exception
을 찾아낼까요?
방법은 간단합니다.
case 1
int num = -1;
int[] array = new int[3];
System.out.println(array[num]);
일부로 예외가 발생하도록 코드를 작성해봅니다.
그리고 코드를 실행하면 아래와 같은 예외가 발생합니다.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException : -1
at Main.main(Main.java:12)
Exception
내용을 보면 ArrayIndexOutOfBoundsException
라고 작성되어있습니다.
이것이 잘못된 인덱스로 배열로 접근할 때 발생하는 예외입니다.
이 내용을 근거로 코드를 재작성해봅시다.
int num = -1;
int[] array = new int[3];
try {
System.out.println(array[num]);
} catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("잘못된 인덱스입니다.");
}
이렇게 하면 어떤 예외가 발생하는 지 확인할 수 있습니다.
case 2
Scanner scanner = new Scanner(system.in);
int value = scanner.nextInt();
String text = scanner.nextLine();
…
Scanner
클래스는 이용자가 특정한 입력을 했을 때 입력을 기준으로 문자 또는 숫자 또는 바이트 등 다양한 자료형으로 입력값을 반환 받을 수 있는 클래스입니다. 그러므로 우리는 사용자의 키보드를 제어할 수 없을 뿐더러, 시스템에 숫자가 입력될지 문자가 입력될지 알 수 없습니다.
우리가 의도한 프로그램이 실행될 때 사용자에 의한 예외로 인한 프로그램 종료를 방지하기 위해서는 적절한 예외를 찾아주는 것이 좋습니다.
Scanner scanner = new Scanner(system.in);
// 문자 입력해보기
int value = scanner.nextInt();
발생하는 에러는 다음과 같습니다.
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at Main.main(Main.java:10)
Exception
내용을 보면 InputMismatchException
라고 작성되어있습니다.
이것이 문자를 입력 받았을 때, 정수형으로 변환시 발생하는 예외입니다.
Scanner scanner = new Scanner(system.in);
try {
int value = scanner.nextInt();
} catch (InputMismatchException ex) {
System.out.println("잘못된 입력입니다");
}
이렇게 예외처리 해주면 됩니다.
ㅤ
모든 예외를 체크할 수는 없으나 어떠한 예외가 발생할 가능성이 높다고 생각했을 때 RuntimeException
클래스를 통해 예외에 대응할 수 있습니다.
int num = -1;
int[] array = new int[3];
try {
System.out.println(array[num]);
} catch (RuntimeException ex) {
System.out.println("잘못된 인덱스입니다.");
}
RuntimeException
클래스는 프로그램 실행도중 발생하는 예외와 관련된 슈퍼 클래스이기 때문에 RuntimeException
을 이용하여 예외에 대응할 수 있습니다.
다만 예외의 종류가 좁혀진다면 예외 종류에 따라 대응이 달라져야 하므로 적절한 RuntimeException
클래스를 찾아서 대응하는 것이 가장 좋습니다.
더 좋은건 예외가 발생하지 않도록 프로그래밍 하는 것 입니다.
ㅤ
이미 만들어진 예외가 아니라 직접 예외를 적용하기 위해 클래스를 만들 수 있습니다.
Unchecked Exception을 만들기 위한 CustomException
public class CustomException extends RuntimeException {
public CustomException() {
super("커스텀 예외다!");
}
}
public class ExceptionTest {
String name;
public void setName(String name) {
if (null == name) {
throw new CustomException();
}
}
}
해당 코드는 setName 메서드를 호출 시 매개변수 값이 null
로 전달 받았을 때 CustomException
을 호출하는 방법입니다. CustomException
은 RuntimeException
을 상속 받았기 때문에 Unchecked Exception 입니다. 그러므로 강제적으로 try - catch
구문을 사용하지 않아도 됩니다.
(throw
키워드를 이용하여 고의적으로 CustomException
을 던질 수 있습니다.)
그러나 우리가 만든 코드를 누군가가 이용한다고 했을 때 코드 이용자가 꼭 예외 사항을 대응하길 바란다고 생각한다면 이렇게 처리해도 됩니다.
Checked Exception을 만들기 위한 CustomException
public class CustomException extends Exception {
// Exception 클래스를 상속받는다.
public CustomException() {
super("커스텀 예외다!");
}
}
public class ExceptionTest {
String name;
public void setName(String name) throws CustomException {
if (null == name) {
throw new CustomException();
}
}
}
CustomException
은 Exception
클래스를 상속 받았습니다.
그리고 메서드에 throws
키워드를 이용하고 CustomException
클래스를 명시했습니다.
이 뜻은 해당 메서드는 Checked exception을 대응해야 한다는 의미로 이 메서드를 사용 시 try - catch
구문을 명시적으로 작성하도록 강제한다는 의미입니다.
결론적으로 Checked exception을 만들 때는 Exception
클래스를 상속 받고, Unchecked exception을 만들 때는 RuntimeException
클래스를 상속 받는 것이 좋습니다.
어떻게 보면 큰차이는 없지만 의미상 차이가 있으므로, 가급적 언어 스펙에 맞게 코드를 작성하시는 것이 좋습니다.
ㅤ
ㅤ
코드 작성
public class Main {
public static void main(String[] args) {
int index = -1;
int[] array = {1, 2, 3};
System.out.println(array[index]);
System.out.println("프로그램 종료");
}
}
출력
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
비정상적인 index로 배열에 접근했으므로 예외가 발생합니다. 그러므로 ArrayIndexOutOfBoundsException
와 관련된 예외 처리를 해봅시다.
코드 변경
public class Main {
public static void main(String[] args) {
int index = -1;
int[] array = {1, 2, 3};
try {
System.out.println(array[index]);
} catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("예외 발생");
}
System.out.println("프로그램 종료");
}
}
출력
예외 발생
프로그램 종료
예외가 발생 시 catch 구문으로 이동하여 실행 후 프로그램이 종료되지 않고 그다음 명령어를 실행 후 프로그램이 종료됩니다.
ㅤ
코드 작성
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
while (true) {
Scanner scanner = new Scanner(System.in);
int dividend;
int divisor;
System.out.println("피제수를 입력해주세요.");
dividend = scanner.nextInt();
System.out.println("제수를 입력해주세요.");
divisor = scanner.nextInt();
int quotient = dividend / divisor;
System.out.printf("%d / %d = %d\n", dividend, divisor, quotient);
}
}
}
두 수를 입력하여 몫을 구하는 나눗셈 프로그램입니다. (Scanner 객체를 사용하므로 import를 해주어야 합니다)
입력
0
0
출력
Exception in thread "main" java.lang.ArithmeticException: / by zero
피제수와 제수를 0으로 하면 나눌 수 없으므로 ArithmeticException
관련 예외가 발생합니다.
1차 코드 변경
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
while (true) {
Scanner scanner = new Scanner(System.in);
int dividend;
int divisor;
try {
System.out.println("피제수를 입력해주세요.");
dividend = scanner.nextInt();
System.out.println("제수를 입력해주세요.");
divisor = scanner.nextInt();
int quotient = dividend / divisor;
System.out.printf("%d / %d = %d\n", dividend, divisor, quotient);
} catch(ArithmeticException ex) {
System.out.println("0으로 나눌 수 없습니다.");
}
}
}
}
try - catch
구문으로 예외 처리를 해줍시다.
입력
0
0
출력
0으로 나눌 수 없습니다.
예외가 발생했으나 예외 처리가 되어있으므로 프로그램은 종료되지 않습니다.
이번엔 문자를 입력해봅시다.
입력
ㄱ
출력
Exception in thread "main" java.util.InputMismatchException
정수로 변환하는 과정에서 문자를 정수로 바꿀 수 없으므로 InputMismatchException
예외가 발생합니다.
2차 코드 변경
import java.util.Scanner;
import java.util.InputMismatchException;
public class Main {
public static void main(String[] args) {
while (true) {
Scanner scanner = new Scanner(System.in);
int dividend;
int divisor;
try {
System.out.println("피제수를 입력해주세요.");
dividend = scanner.nextInt();
System.out.println("제수를 입력해주세요.");
divisor = scanner.nextInt();
int quotient = dividend / divisor;
System.out.printf("%d / %d = %d\n", dividend, divisor, quotient);
} catch(ArithmeticException ex) {
System.out.println("0으로 나눌 수 없습니다.");
} catch(InputMismatchException ex) {
System.out.println("문자를 입력할 수 없습니다.");
}
}
}
}
InputMismatchException
관련 예외 처리를 해줍시다. (다만 해당 예외 처리는 import java.util.InputMismatchException
를 작성해 주어야 합니다.)
입력
ㄱ
출력
문자를 입력할 수 없습니다.
예외가 발생했으나 예외 처리가 되어있으므로 프로그램은 종료되지 않습니다.
ㅤ
Checked Exception은 예외 처리가 되어있지 않으면 컴파일이 되지 않습니다.
ㅤ
코드 작성
public class Main {
public static void main(String[] args) {
byte[] numArray = {'1', '2', '3', '4'};
System.out.write(numArray);
}
}
System.out.write()
메서드는 바이트 배열을 전달 받아 문자를 출력하는 메서드입니다.
출력
Main.java:10: error: unreported exception IOException; must be caught or declared to be thrown
System.out.write(numArray); 1 error
System.out.write()
메서드는 Checked Exception인 IOException이 선언된 메서드입니다. 그러므로 반드시 try - catch 구문을 작성해주어야 합니다.
코드 변경
import java.io.IOException;
public class Main {
public static void main(String[] args) {
byte[] numArray = {'1', '2', '3', '4'};
try {
System.out.write(numArray);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
IOException
관련 예외 처리를 해줍시다. (다만 해당 예외 처리는 import java.io.IOException
을 작성해 주어야 합니다.)
출력
1234
try - catch
구문을 처리했으므로 정상적으로 컴파일이 되고 프로그램이 실행됩니다.
프로그램 이용자나 소스 코드 이용자는 소스 코드 작성자가 작성한 의도대로 행동하지 않을 수 있습니다. 이것을 예외 사항이라고 합니다. 이러한 예외 사항을 어떻게 대응할 수 있을까요? 이번 강의에서는 예외 사항을 대응하는 법에 대해 배웁니다.
예외 처리를 잘 하는 방법은 프로그램 사용자의 입장에서 생각하는 것입니다. 우리가 만든 프로그램은 스스로 사용하기 위해 만들 수도 있지만 대부분은 사용자를 위해서 만들게 됩니다. 실습과 연구를 많이 할수록 더 많은 문제에 미리 대응할 수 있습니다. 또한 지금 당장은 이해가 되지 않는다고 하더라도 코딩하다 보면 깨닫게 되는 부분도 있으므로 마음을 조급하게 먹지 않아도 됩니다.
ㅤ
ㅤ
모든 에러와 예외에 대한 최상위 클래스입니다. Object 클래스를 상속받고 있으며 Error와 Exception 하위 클래스들을 가지고 있습니다. 일반적으로는 해당 클래스를 직접적으로 이용할 일은 없습니다. 그러므로 있다는 것만 알고 넘어갑시다.
ㅤ
프로그램 실행 도중 해결할 수 있는 문제가 아니라 이후에 발견되어 처리해야 하는 문제들이 Error
클래스와 연관되어 있습니다. 코드상으로 별도의 예외 처리를 할 수 없습니다.
ㅤ
자바 가상 머신이 메모리가 부족하여 인스턴스를 할당할 수 없고 가비지 컬렉터가 메모리를 사용할 수 없을 때 발생합니다.
ㅤ
스레드의 Stack 메모리가 꽉 찼을 경우 발생합니다. (이후의 강의에서 Stack 메모리가 무엇인지 알게 됩니다)
ㅤ
자바 가상 머신에 문제가 생겼거나 실행되는데 리소스가 부족할 경우 발생됩니다.
ㅤ
프로그램 실행 도중 종료될 수 있는 문제와 연관되어 예외 처리를 선택적으로 하거나, 꼭 해야 하는 클래스들의 슈퍼 클래스입니다. Exception
에는 Checked Exception과 Unchecked Exception이 있으며 Checked Exception은 반드시 예외 처리를 해야지 컴파일이 가능하나 Unchecked Exception은 예외 처리가 선택입니다.
*Checked Exception과 Unchecked Exception을 구분하는 방법은 직접적으로 Exception 클래스를 상속받는 경우 Checked Exception 으로 보면 되며 RuntimeException을 상속받는 경우 Unchecked Exception으로 보면 쉽습니다.
ㅤ
반드시 try - catch
구문을 사용해야지 컴파일되는 예외이며 직접적으로 Exception
클래스를 상속받는 예외입니다.
ㅤ
InputStream.java
public abstract class InputStream implements Closeable {
...
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
...
}
입력과 출력과 관련된 예외이고 주로 throws를 통해 메서드에 선언되어 있으며 반드시 예외 처리를 해야 컴파일이 됩니다. 주로 InputStream
, OutputStream
관련 메서드를 사용할 때 IOException
과 관련한 예외처리를 해야 합니다.
ㅤ
Thread.java
public class Thread implements Runnable {
...
public static void sleep(long millis, int nanos) throws InterruptedException {
...
}
public final synchronized void join(long millis) throws InterruptedException {
...
}
...
}
스레드와 관련된 예외이고 주로 throws를 통해 메서드에 선언되어 있으며 반드시 예외 처리를 해야 컴파일이 됩니다. 주로 Thread
관련 메서드를 사용할 때 InterruptedException
과 관련한 예외처리를 해야 합니다.
ㅤ
강제적으로 try - catch
구문을 사용하지 않아도 되는 예외이며 프로그램 실행도중 발생할 수 있는 예외들로 구성되어 있습니다.
ㅤ
Unchecked Exception의 최상위 클래스로 Exception
클래스를 직접적으로 상속받은 클래스이나 특수하게 RuntimeException
을 상속 받는 예외들은 try - catch
구문에 대한 강제성이 없습니다.
이러한 예외들은 예외가 발생할 가능성이 높을 때 방어코드를 사용하거나 또는 try - catch
구문으로 회피하는 방식을 택할 수 있습니다.
ㅤ
String text = null;
System.out.println(text.isEmpty());
// NullPointerException 발생
RuntimeException
을 상속 받는 예외입니다. 참조 자료형 변수에 인스턴스가 저장되어 있지 않고 null 값이 저장되어 있을 때, 인스턴스 메서드를 호출하거나, 변수의 접근 시 발생할 수 있습니다.
ㅤ
class Warrior {
}
class SuperWarrior extends Warrior {
}
Warrior warrior = new Warrior();
SuperWarrior superWarrior = (SuperWarrior) warrior;
// 생성된 인스턴스는 Warrior 이므로 SuperWarrior로 형 변환시
// ClassCastException 발생
RuntimeException
을 상속 받는 예외입니다. 객체 형 변환시 올바르지 않은 객체로 형 변환 할 경우 발생할 수 있습니다.
ㅤ
예외 발생 가능성이 높다고 했을 때 어떻게 적절한 Exception
을 찾아낼까요?
방법은 간단합니다.
case 1
int num = -1;
int[] array = new int[3];
System.out.println(array[num]);
일부로 예외가 발생하도록 코드를 작성해봅니다.
그리고 코드를 실행하면 아래와 같은 예외가 발생합니다.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException : -1
at Main.main(Main.java:12)
Exception
내용을 보면 ArrayIndexOutOfBoundsException
라고 작성되어있습니다.
이것이 잘못된 인덱스로 배열로 접근할 때 발생하는 예외입니다.
이 내용을 근거로 코드를 재작성해봅시다.
int num = -1;
int[] array = new int[3];
try {
System.out.println(array[num]);
} catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("잘못된 인덱스입니다.");
}
이렇게 하면 어떤 예외가 발생하는 지 확인할 수 있습니다.
case 2
Scanner scanner = new Scanner(system.in);
int value = scanner.nextInt();
String text = scanner.nextLine();
…
Scanner
클래스는 이용자가 특정한 입력을 했을 때 입력을 기준으로 문자 또는 숫자 또는 바이트 등 다양한 자료형으로 입력값을 반환 받을 수 있는 클래스입니다. 그러므로 우리는 사용자의 키보드를 제어할 수 없을 뿐더러, 시스템에 숫자가 입력될지 문자가 입력될지 알 수 없습니다.
우리가 의도한 프로그램이 실행될 때 사용자에 의한 예외로 인한 프로그램 종료를 방지하기 위해서는 적절한 예외를 찾아주는 것이 좋습니다.
Scanner scanner = new Scanner(system.in);
// 문자 입력해보기
int value = scanner.nextInt();
발생하는 에러는 다음과 같습니다.
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at Main.main(Main.java:10)
Exception
내용을 보면 InputMismatchException
라고 작성되어있습니다.
이것이 문자를 입력 받았을 때, 정수형으로 변환시 발생하는 예외입니다.
Scanner scanner = new Scanner(system.in);
try {
int value = scanner.nextInt();
} catch (InputMismatchException ex) {
System.out.println("잘못된 입력입니다");
}
이렇게 예외처리 해주면 됩니다.
ㅤ
모든 예외를 체크할 수는 없으나 어떠한 예외가 발생할 가능성이 높다고 생각했을 때 RuntimeException
클래스를 통해 예외에 대응할 수 있습니다.
int num = -1;
int[] array = new int[3];
try {
System.out.println(array[num]);
} catch (RuntimeException ex) {
System.out.println("잘못된 인덱스입니다.");
}
RuntimeException
클래스는 프로그램 실행도중 발생하는 예외와 관련된 슈퍼 클래스이기 때문에 RuntimeException
을 이용하여 예외에 대응할 수 있습니다.
다만 예외의 종류가 좁혀진다면 예외 종류에 따라 대응이 달라져야 하므로 적절한 RuntimeException
클래스를 찾아서 대응하는 것이 가장 좋습니다.
더 좋은건 예외가 발생하지 않도록 프로그래밍 하는 것 입니다.
ㅤ
이미 만들어진 예외가 아니라 직접 예외를 적용하기 위해 클래스를 만들 수 있습니다.
Unchecked Exception을 만들기 위한 CustomException
public class CustomException extends RuntimeException {
public CustomException() {
super("커스텀 예외다!");
}
}
public class ExceptionTest {
String name;
public void setName(String name) {
if (null == name) {
throw new CustomException();
}
}
}
해당 코드는 setName 메서드를 호출 시 매개변수 값이 null
로 전달 받았을 때 CustomException
을 호출하는 방법입니다. CustomException
은 RuntimeException
을 상속 받았기 때문에 Unchecked Exception 입니다. 그러므로 강제적으로 try - catch
구문을 사용하지 않아도 됩니다.
(throw
키워드를 이용하여 고의적으로 CustomException
을 던질 수 있습니다.)
그러나 우리가 만든 코드를 누군가가 이용한다고 했을 때 코드 이용자가 꼭 예외 사항을 대응하길 바란다고 생각한다면 이렇게 처리해도 됩니다.
Checked Exception을 만들기 위한 CustomException
public class CustomException extends Exception {
// Exception 클래스를 상속받는다.
public CustomException() {
super("커스텀 예외다!");
}
}
public class ExceptionTest {
String name;
public void setName(String name) throws CustomException {
if (null == name) {
throw new CustomException();
}
}
}
CustomException
은 Exception
클래스를 상속 받았습니다.
그리고 메서드에 throws
키워드를 이용하고 CustomException
클래스를 명시했습니다.
이 뜻은 해당 메서드는 Checked exception을 대응해야 한다는 의미로 이 메서드를 사용 시 try - catch
구문을 명시적으로 작성하도록 강제한다는 의미입니다.
결론적으로 Checked exception을 만들 때는 Exception
클래스를 상속 받고, Unchecked exception을 만들 때는 RuntimeException
클래스를 상속 받는 것이 좋습니다.
어떻게 보면 큰차이는 없지만 의미상 차이가 있으므로, 가급적 언어 스펙에 맞게 코드를 작성하시는 것이 좋습니다.
ㅤ
ㅤ
코드 작성
public class Main {
public static void main(String[] args) {
int index = -1;
int[] array = {1, 2, 3};
System.out.println(array[index]);
System.out.println("프로그램 종료");
}
}
출력
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
비정상적인 index로 배열에 접근했으므로 예외가 발생합니다. 그러므로 ArrayIndexOutOfBoundsException
와 관련된 예외 처리를 해봅시다.
코드 변경
public class Main {
public static void main(String[] args) {
int index = -1;
int[] array = {1, 2, 3};
try {
System.out.println(array[index]);
} catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("예외 발생");
}
System.out.println("프로그램 종료");
}
}
출력
예외 발생
프로그램 종료
예외가 발생 시 catch 구문으로 이동하여 실행 후 프로그램이 종료되지 않고 그다음 명령어를 실행 후 프로그램이 종료됩니다.
ㅤ
코드 작성
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
while (true) {
Scanner scanner = new Scanner(System.in);
int dividend;
int divisor;
System.out.println("피제수를 입력해주세요.");
dividend = scanner.nextInt();
System.out.println("제수를 입력해주세요.");
divisor = scanner.nextInt();
int quotient = dividend / divisor;
System.out.printf("%d / %d = %d\n", dividend, divisor, quotient);
}
}
}
두 수를 입력하여 몫을 구하는 나눗셈 프로그램입니다. (Scanner 객체를 사용하므로 import를 해주어야 합니다)
입력
0
0
출력
Exception in thread "main" java.lang.ArithmeticException: / by zero
피제수와 제수를 0으로 하면 나눌 수 없으므로 ArithmeticException
관련 예외가 발생합니다.
1차 코드 변경
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
while (true) {
Scanner scanner = new Scanner(System.in);
int dividend;
int divisor;
try {
System.out.println("피제수를 입력해주세요.");
dividend = scanner.nextInt();
System.out.println("제수를 입력해주세요.");
divisor = scanner.nextInt();
int quotient = dividend / divisor;
System.out.printf("%d / %d = %d\n", dividend, divisor, quotient);
} catch(ArithmeticException ex) {
System.out.println("0으로 나눌 수 없습니다.");
}
}
}
}
try - catch
구문으로 예외 처리를 해줍시다.
입력
0
0
출력
0으로 나눌 수 없습니다.
예외가 발생했으나 예외 처리가 되어있으므로 프로그램은 종료되지 않습니다.
이번엔 문자를 입력해봅시다.
입력
ㄱ
출력
Exception in thread "main" java.util.InputMismatchException
정수로 변환하는 과정에서 문자를 정수로 바꿀 수 없으므로 InputMismatchException
예외가 발생합니다.
2차 코드 변경
import java.util.Scanner;
import java.util.InputMismatchException;
public class Main {
public static void main(String[] args) {
while (true) {
Scanner scanner = new Scanner(System.in);
int dividend;
int divisor;
try {
System.out.println("피제수를 입력해주세요.");
dividend = scanner.nextInt();
System.out.println("제수를 입력해주세요.");
divisor = scanner.nextInt();
int quotient = dividend / divisor;
System.out.printf("%d / %d = %d\n", dividend, divisor, quotient);
} catch(ArithmeticException ex) {
System.out.println("0으로 나눌 수 없습니다.");
} catch(InputMismatchException ex) {
System.out.println("문자를 입력할 수 없습니다.");
}
}
}
}
InputMismatchException
관련 예외 처리를 해줍시다. (다만 해당 예외 처리는 import java.util.InputMismatchException
를 작성해 주어야 합니다.)
입력
ㄱ
출력
문자를 입력할 수 없습니다.
예외가 발생했으나 예외 처리가 되어있으므로 프로그램은 종료되지 않습니다.
ㅤ
Checked Exception은 예외 처리가 되어있지 않으면 컴파일이 되지 않습니다.
ㅤ
코드 작성
public class Main {
public static void main(String[] args) {
byte[] numArray = {'1', '2', '3', '4'};
System.out.write(numArray);
}
}
System.out.write()
메서드는 바이트 배열을 전달 받아 문자를 출력하는 메서드입니다.
출력
Main.java:10: error: unreported exception IOException; must be caught or declared to be thrown
System.out.write(numArray); 1 error
System.out.write()
메서드는 Checked Exception인 IOException이 선언된 메서드입니다. 그러므로 반드시 try - catch 구문을 작성해주어야 합니다.
코드 변경
import java.io.IOException;
public class Main {
public static void main(String[] args) {
byte[] numArray = {'1', '2', '3', '4'};
try {
System.out.write(numArray);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
IOException
관련 예외 처리를 해줍시다. (다만 해당 예외 처리는 import java.io.IOException
을 작성해 주어야 합니다.)
출력
1234
try - catch
구문을 처리했으므로 정상적으로 컴파일이 되고 프로그램이 실행됩니다.