에러는 JVM실행에 문제가 생겼다는 것이므로 JVM위에서 실행되는 프로그램을 아무리 견고하게 만들어도 결국 실행 불능이 된다. 자바에서는 에러 이외에 예외라고 부르는 오류가 있다. 예외란 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류를 말한다. 예외가 발생하면 프로그램은 곧바로 종료된다는 점에서 동일하다. 그러나 예외는 예외 처리를 통해 프로그래밍을 종료하지 않고 정상 실행 상태가 유지되도록 할 수 있다.
예외에는 두 가지 종류가 있습니다. 바로 일반예외이고 다른 하나는 실행 예외 입니다. 일반 예외는 컴파일러 체크 예외라고도 하는데, 자바 소스를 컴파일 하는 과정에서 예외 처리 코드가 필요한지 검사하기 때문입니다. 실행 예외는 컴파일하는 과정에서 예외 처리 코드를 검사하지 않는 예외를 말합니다. 자바에서는 예외를 클래스로 관리합니다. JVM은 프로그램을 실행하는 도중에 예외가 발생하면 해당 예외 클래스로 객체를 생성한다. 그리고 나서 예외 처리 코드에서 예외 객체를 이용할 수 있도록 해줍니다. 모든 예외 클래스들은 java.lang.Exception클래스를 상속받습니다.
실행 예외는 자바 컴파일러가 체크를 하지 않지만 오로지 개발자의 경험에 의해서만 예외처리 코드를 삽입해야 합니다.
NullPointerException
객체 참조가 없는 상태. 즉 null값을 갖는 참조 변수로 객체 접근 연산자인 도트(.)를 사용했을 때 발생한다.
package Exception1;
public class NullPointerExceptionExample {
public static void main(String[] args) {
String data = null;
System.out.println(data.toString());
}
}
//Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toString()" because "data" is null
//at Exception/Exception1.NullPointerExceptionExample.main(NullPointerExceptionExample.java:6)
예외문을 보면 NullPointerExceptionExample.java의 6번째 줄에서 java.lang.NullPointerException이 발생하고 있다고 친절하게 알려주고 있습니다.
ArrayIndexOutOfBoundsException
또한 배열에서 인덱스 범위를 초과하여 사용할 경우 ArrayIndexOutOfBoundsException이 발생한다.
package com.company.Exception;
public class ArrayIndexOutOfBoundsExceptionExample {
public static void main(String[] args) {
String data1 = args[0];
String data2 = args[1];
System.out.println("args[0]: " + data1);
System.out.println("args[1]: " + data2);
}
}
//Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
// at com.company.Exception.ArrayIndexOutOfBoundsExceptionExample.main(ArrayIndexOutOfBoundsExceptionExample.java:5)
NumberFormatException
Integer와 Double을 포장(Wrapper)클래스라고 하는데, 이 클래스의 정적 메소드인 parseXXX()메소드를 이용하면 문자열을 숫자로 변환할 수 있다. 하지만 숫자로 변환할 수 없는 문자가 포함되어 있다면 NumberFormatException이 발생한다.
package com.company.Exception;
public class NumberFormatException {
public static void main(String[] args){
String data1 = "100";
String data2 = "a100";
int value1 = Integer.parseInt(data1);
int value2 = Integer.parseInt(data2);
int result = value1 + value2;
System.out.println(data1 + "+" + data2 + "-" + result);
}
}
//Exception in thread "main" java.lang.NumberFormatException: For input string: "a100"
// at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
// at java.lang.Integer.parseInt(Integer.java:580)
// at java.lang.Integer.parseInt(Integer.java:615)
// at com.company.Exception.NumberFormatException.main(NumberFormatException.java:9)
ClassCastException
타입 변환은 상위 클래스와 하위 클래스 간에 발생하고 구현클래스와 인터페이스 간에도 발생한다. 이러한 관계가 아니라면 클래스는 다른 클래스로 타입 변환할 수 없다.
package com.company.Exception;
public class ClassCastException {
public static void main(String[] args) {
Dog dog = new Dog();
changeDog(dog);
Cat cat = new Cat();
changeDog(cat);
}
public static void changeDog(Animal animal) {
Dog dog = (Dog) animal;
}
static class Animal {}
static class Dog extends Animal {}
static class Cat extends Animal {}
}
//Exception in thread "main" java.lang.ClassCastException: com.company.Exception.ClassCastException$Cat cannot be cast to com.company.Exception.ClassCastException$Dog
// at com.company.Exception.ClassCastException.changeDog(ClassCastException.java:15)
// at com.company.Exception.ClassCastException.main(ClassCastException.java:10)
하지만 이를 피할 수 있는 방법은 타입 변환 전에 타입 변환이 가능한지 instanceof연산자로 확인하는 것이 좋다. instanceof연산자의 결과가 true이면 좌항 객체를 우항 타입으로 변환이 가능하다는 것이다.
package com.company.Exception;
public class ClassCastException {
public static void main(String[] args) {
Dog dog = new Dog();
changeDog(dog);
Cat cat = new Cat();
changeDog(cat);
}
public static void changeDog(Animal animal) {
if(animal instanceof Dog) {
Dog dog = (Dog) animal;
}
}
static class Animal {}
static class Dog extends Animal {}
static class Cat extends Animal {}
}
예외 처리 코드
자바 컴파일러는 소스 파일을 컴파일할 떄 일반 예외가 발생할 가능성이 있는 코드를 발견하면 컴파일 오류를 발생시켜 개발자로 하여금 강제적으로 예외 처리 코드를 작성하도록 요구한다. 그러나 실행 예외는 컴파일러가 체크해주지 않기 때문에 예외 처리 코드를 개발자의 경험을 바탕으로 작성해야 한다. 예외 처리 코드는 try-catch-finally블록을 이용한다.
package com.company.Exception;
public class TryCatchFinallyExample {
public static void main(String[] args) {
try {
Class clazz = Class.forName("java.lang.String2");
} catch(ClassNotFoundException e) {
System.out.println("클래스가 존재하지 않습니다.");
}
}
}
//클래스가 존재하지 않습니다.
Class.forName()메소드는 매개값으로 주어진 클래스가 존재하면 Class객체를 리턴하지만, 존재하지 않으면 ClassNotFoundException예외를 발생시킨다.
다중 catch
try블록에는 다양한 예외가 발생할 수 있다. 발생되는 예외별로 예외 처리 코드를 다르게 하려면 다중 catch문을 사용하면 된다. catch블록이 여러 개라 할지라도 단 하나의 catch블록만 실행된다. 그 이유는 try블록에서 동시 다발적으로 예외가 발생하지 않고, 하나의 예외가 발생하면 즉시 실행을 멈추고 해당 catch블록으로 이동하기 때문이다.
package com.company.Exception;
public class CatchByExceptionKindExample {
public static void main(String[] args) {
try {
String data1 = args[0];
String data2 = args[1];
int value1 = Integer.parseInt(data1);
int value2 = Integer.parseInt(data2);
int result = value1 + value2;
System.out.println(data1 + "+" + data2 + "=" + result);
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("실행 매개값의 수가 부족합니다.");
System.out.println("[실행 방법]");
System.out.println("java CatchByExceptionKindExample num1 num2");
} catch(java.lang.NumberFormatException e) {
System.out.println("숫자로 변환할 수 없습니다.");
} finally {
System.out.println("다시 실행하세요");
}
}
}
//실행 매개값의 수가 부족합니다.
//[실행 방법]
//java CatchByExceptionKindExample num1 num2
//다시 실행하세요
다중 catch블록을 작성할 때 주의할 점은 상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야 한다. 만약 상위 예외 클래스의 catch블록이 위에 있다면 하위 예외 클래스의 catch블록은 실행되지 않는다. 왜냐하면 하위 예외는 상위 예외를 상속했기 때문에 상위 예외 타입도 되기 때문이다.
package com.company.Exception;
public class CatchByExceptionKindExample {
public static void main(String[] args) {
try {
String data1 = args[0];
String data2 = args[1];
int value1 = Integer.parseInt(data1);
int value2 = Integer.parseInt(data2);
int result = value1 + value2;
System.out.println(data1 + "+" + data2 + "=" + result);
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("실행 매개값의 수가 부족합니다.");
System.out.println("[실행 방법]");
System.out.println("java CatchByExceptionKindExample num1 num2");
} catch(java.lang.NumberFormatException e) {
System.out.println("숫자로 변환할 수 없습니다.");
} catch(Exception e) {
System.out.println("실행에 문제가 있습니다.");
} finally {
System.out.println("다시 실행하세요");
}
}
}
//실행 매개값의 수가 부족합니다.
//[실행 방법]
//java CatchByExceptionKindExample num1 num2
//다시 실행하세요
이와 같이 처리해주어야 한다.
멀티 catch
또한 하나의 catch블록에서 여러 개의 예외를 처리할 수도 있다.
package com.company.Exception;
public class MultiCatchExample {
public static void main(String[] args) {
try {
String data1 = args[0];
String data2 = args[1];
int value1 = Integer.parseInt(data1);
int value2 = Integer.parseInt(data2);
int result = value1 + value2;
System.out.println(data1 + "+" + data2 + "=" + result);
} catch (ArrayIndexOutOfBoundsException | java.lang.NumberFormatException e) {
System.out.println("실행 매개값의 수가 부족하거나 숫자로 변환할 수 없습니다.");
} catch (Exception e) {
System.out.println("실행에 문제가 있습니다.");
} finally {
System.out.println("다시 실행하세요");
}
}
}
//실행 매개값의 수가 부족하거나 숫자로 변환할 수 없습니다.
//다시 실행하세요
예외 떠넘기기
메소드 내부에서 예외가 발생할 수 있는 코드를 작성할 때 try-catch블록으로 예외를 처리하는 것이 기본이지만, 경우에 따라서는 메소드를 호출한 곳으로 예외를 떠넘길 수도 있다. 이떄 사용하는 키워드가 throws이다. throws키워드는 메소드 선언부 끝에 작성되어 메소드에서 처리하지 않은 예외를 호출한 곳으로 떠넘기는 역할을 한다.
package com.company.Exception;
public class ThrowsExample {
public static void main(String[] args) {
try {
findClass();
} catch(ClassNotFoundException e) {
System.out.println("클래스가 존재하지 않습니다.");
}
}
public static void findClass() throws ClassNotFoundException{
Class clazz = Class.forName("java.lang.String2");
}
}
//클래스가 존재하지 않습니다.
예외 발생시키기
우선 사용자 정의 예외를 정의하고 예외를 발생시켜 보겠습니다.
package com.company.Exception;
public class BalanceInsufficientException extends Exception{
public BalanceInsufficientException() { }
public BalanceInsufficientException(String message) {
super(message);
}
}
package com.company.Exception;
public class Account {
private long balance;
public Account() { }
public long getBalance() {
return balance;
}
public void deposit(int money) {
balance += money;
}
public void withdraw(int money) throws BalanceInsufficientException{
if(balance < money) {
throw new BalanceInsufficientException("잔고부족" + (money - balance) + " 모자람;");
}
balance -= money;
}
}
package com.company.Exception;
public class AccountExample {
public static void main(String[] args) {
Account account = new Account();
// 예금하기
account.deposit(10000);
System.out.println("예금액: " + account.getBalance());
// 출금하기
try {
account.withdraw(30000);
} catch(BalanceInsufficientException e) {
String message = e.getMessage();
System.out.println(message);
System.out.println();
e.printStackTrace();
}
}
}
//예금액: 10000
//잔고부족 20000 모자람;
//
//com.company.Exception.BalanceInsufficientException: 잔고부족 20000 모자람;
// at com.company.Exception.Account.withdraw(Account.java:18)
// at com.company.Exception.AccountExample.main(AccountExample.java:13)
'School > Java Programming' 카테고리의 다른 글
Java Programming - Collection Framework (0) | 2022.04.07 |
---|---|
Java Programming - Basic API Class (0) | 2022.03.24 |
Java Programming - 중첩 클래스 & 중첩 인터페이스 (0) | 2022.03.17 |
Java Programming - Interface (0) | 2022.03.10 |
Java Programming - Inheritance (0) | 2022.03.09 |