JAVA/java 이론

[자바JAVA] 예외처리 - try catch finally/ RuntimeException의 종류/ 다중 catch/ throws/throw/사용자 정의 예외

자바칩 프라푸치노 2020. 10. 30. 18:27

이 포스팅에는 아래의 내용이 포함되어있습니다.

 

1. 오류의 종류

2. 예외처리의 목적

3. 예외의 종류

4. 예외클래스

5. try- catch- finally

5. RuntimeException의 종류

* NullPointerException

*ArrayIndexOutOfBoundsException 

* NumberFormatException

* ClassCastException

6. 다중catch 

7. thorws

8. throw

9. 사용자 정의 예외

 

1. 오류의 종류

*에러Error

- 의도치 않게 프로그램이 종료되는 것

- 하드웨어의 잘못된 동작 또는 고장으로 인한 오류(정전, 베드섹터 등)

- 정상 실행상태로 돌아갈 수 없음

 

*예외Exception

- 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인한 오류

- 예외처리하면 정상 실행 상태로 돌아갈 수 있음

 

2. 예외처리의 목적

프로그램을 정상 실행상태로 돌리는 것

 

 

3. 예외의 종류

* 일반/ 컴파일/예외(checked exception)

예외 처리 코드없으면 컴파일에서 오류가 나는 것

빨간 줄 쳐지는 것

 

* 실행/ 런타임 예외 (unchecked exception)

예외 처리 코드가 없어도 컴파일이 되는 예외

빨간 줄이 안쳐지고 실행시에 예외가 터지는 것

 

 

4. 예외클래스

Exception은 예외 클래스의 최고 조상

예외에서 RuntimeException을 상속받으면 런타임예외임

 

5. try- catch- finally

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class TryCatchFinallyEx1 {
 
    public static void main(String[] args) {
 
        try {
            System.out.println(1);
            System.out.println(2);
            System.out.println(3);
            System.out.println(1/0); //예외발생. 모든 수는 0으로 나눌 수 없다. ArithmeticException발생
            System.out.println(-10);
        } catch (Exception e) {
            System.out.println(5);
            e.printStackTrace();
        }
        //항상 실행
        finally {
            System.out.println(6);
        }
 
    }
 
}
 
cs

예외처리방법입니다.

try구문에 예외가 발생할 것 같은 코드를 넣어주고

예외가 발생하면 그 즉시 try구문 그 아래에 있는 코드는 실행하지않고

catch문으로 넘어갑니다.

catch문에 매개변수로 Exception을 받아주고 있으니 어떠한 예외가 터져도 다 catch문에서 받겠다는 뜻입니다.

그리고 finally는 무조건 한번 실행하는 코드입니다.

저 위에서는 1 2 3 5 6 이 출력됩니다.

 

<출력결과>

저기서 예외가 어떤 이유로 터졌는지 출력해주는 코드가 바로

e.printStackTrace() 입니다.

 

5. RuntimeException의 종류

* NullPointerException

객체(인스턴스)참조가 없는 상태

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class NullPointerExceptionEx {
 
    @SuppressWarnings("null")
    public static void main(String[] args) {
    
        try {
            String data = null;
            //NullPointerException발생(런타임 예외) 함 그 이유는? 객체없음
            System.out.println(data.toString());
        } catch (Exception e) {
            System.out.println("예외발생");
            e.toString();
            e.printStackTrace();//개발자 전용. 배포할때는 주석처리
        }
 
    }
 
}
 
cs
c

String타입 data에 아무것도 없는데 toString을 출력하니 예외가 발생합니다.

 

*ArrayIndexOutOfBoundsException 

배열에서 인덱스 범위를 초과하여 사용할때 발생

 

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
31
32
33
public class ArrayIndexOutOfBoundsExceptionEx {
//Run - run configurations - argument 설정
    public static void main(String[] args) {
 
    
//        }/예외처리 목적: 프로그램의 정상적인 상태로 종료로 유도하는 것이 최고 목적이다. 
        try {
            String data0 = args[0];
            String data1 = args[1];
            String data2 = args[2];
            String data3 = args[3];
            System.out.println("args[0]"+data0);
            System.out.println("args[1]"+data1);
            System.out.println("args[2]"+data2);
            System.out.println("args[3]"+data3);
            
        } catch (Exception e) {
            //실행 매개값이 없으면 ArrayIndexOutOfBoundsEx발생
            System.out.println(e.toString());
            System.out.println("실행방법");
            System.out.println("Java ArrayIndexOutOfBoundsEx");
            System.out.println("값1 값2");
            
        }
        
        finally {
            //finally 구문은 예외가 발생하든 하지않든 무조건 실행됨
            System.out.println("finally");
        }
    }
 
}
 
cs

main메서드에 매개변수인 args에 값을 대입하는 방법은

Run - runconfiguratoins - arguments를 설정해주시면 됩니다.

 

이렇게 arguments를 두개 설정해서 args의 길이가 2이므로 당연히 예외가 발생하겠죠

 

요렇게 출력이 되었습니다.

여기서 data2 와 data3를 없애면

요렇게 나오구요 finally구문은 무조건 실행되는 것을 확인할 수 있습니다.

 

* NumberFormatException

문자열을 숫자로 변환할 때, 문자열에 숫자로 변환할 수 없는 문자가 포함되어있을 때 나타나는 예외이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class NumberFormatExceptionEx {
 
    public static void main(String[] args) {
        
    
        try {
            String value1 = "100";
            String value2 = "100a";
            
            int num1 = Integer.parseInt(value1);
            int num2 = Integer.parseInt(value2);//예외발생
            
            System.out.println("num1 : " + num1);
            System.out.println("num2 : " + num2);
                
        } catch (Exception e) {
            System.out.println("예외발생");
            e.printStackTrace();
//            e.toString();
        }
    }
 
}
 
cs

String타입 value2에 100a를 숫자로 바꿀 수 없어서 예외가 발생합니다.

 

* ClassCastException

타입이 변환되지 않을 경우 발생합니다.

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
31
32
33
34
35
36
package sec01_exam;
//예외처리하는 이유- 예외가 발생해도 프로그램을 정상적으로 종료하기 위해
class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}
public class ClassCastExceptionEx {
    @SuppressWarnings("unused")
    public static void changeDog(Animal animal) {
        if (animal instanceof Dog) {
            
            Dog dog = (Dog)animal;
            System.out.println("Dog로 변환 성공");
        }
        else {
            System.out.println("Dog로 변환 실패");
        }
        
    }
    @SuppressWarnings("unused")
    public static void main(String[] args) {
        try {
            Animal animal = new Dog();
            Dog dog = (Dog)animal;
            Cat cat = (Cat)animal; //런타임, unchecked exception, 실행 예외발생
            changeDog(dog);
            changeDog(new Cat());
        } catch (Exception e) {
            System.out.println("예외발생");
            e.printStackTrace();
        }
        
 
    }
 
}
 
cs

 

상속 계층도가 요렇게 되어있습니다.

main에서 Animal을 Dog객체로 생성을 했는데 

Cat 부분에서 Cat으로 타입 변환을 하고 있으니 런타임 예외가 발생합니다.

그런데 컴파일은 문제없습니다. 왜냐하면 자손이 맞기 때문에 컴파일은 된다 이말입니다.

 

6. 다중catch 

예외별로 예외처리를 다르게 구현 하는 것

 

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
31
32
33
package sec03_exam;
 
public class CatchOrderEx {
 
    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);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("ArrayIndexOutOfBoundsException 발생");
            System.out.println("실행매개값이 부족합니다.");
            System.out.println("실행방법 java CatchByExceptionKindExample num1 num2");
            System.out.println("프로그램을 정상종료합니다.");
        }
        catch (NumberFormatException e2) {
            System.out.println("NumberFormatException발생");
            System.out.println("숫자로 변환할 수 없습니다. 매개변수의 값을 확인하세요!");
            System.out.println("프로그램을 정상종료합니다.");
            
        }
        //항상 catch문은 1개만 실행하므로 예외의 최고 조상 클래스 Exception은 항상 마지막에 위치
        catch (Exception e3) {
            System.out.println("예외가 발생하였습니다.");
            System.out.println("프로그램을 정상종료합니다.");
        }
    }
 
}
 
cs

위에서 발생하는 예외가 무엇이냐에 따라 catch문이 달라집니다.

저기에서는 

이 결과가 나왔습니다.

실행 매개값을 주지않았기에 ArrayIndexOutOfBoundsException이 발생했습니다.

여기서 알아야 할 것은

catch의 매개변수로 Exception을 받는 코드는 맨 마지막에 위치하여야합니다.

그 이유는 앞에서 Exception을 받아버리면 예외란 예외는 모두 그 catch문에서 처리하겠다는 뜻이므로 밑의 코드들이 의미가 없게 됩니다.

 

 

7. throws

메서드에서 처리하지 않은 예외를 호출한 곳으로 떠넘기는 역할

8. throw 

예외를 강제로 발생시키는 역할

 

두개 같이 예제를 보겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package sec05_exam;
 
public class ThrowsEx1 {
 
    public static void main(String[] args) throws Exception {
    //이렇게 main()다른 메서드인 method1()을 호출했을때
        //예외를 떠넘기기 하고 있어서 main()에 예외처리코드가 들어가야한다
        //main -> throws Exception붙인다.
        //main()자체에서 try/ catch 구문으로 직접 예외처리를 한다.
        ThrowsEx1.method1();
    }
 
    public static void method1() throws Exception{
        ThrowsEx1.method2();
        
    }
 
    public static void method2() throws Exception{
        throw new Exception("예외 발생함");
        
    }
 
}
 
cs

 

method2에서 throw로 예외를 강제로 발생시키고 있습니다.

그러면 method2를 호출한 곳에서 던져주어야하니 throws로 던지고 있습니다.

그리고 method1에서 method2를 호출했으니 예외처리를 해주어야하는데

또 method1을 호출한 곳에 throws를 통해 예외를 던집니다.

그러면 main에서 try- catch구문을 사용해서 예외처리를 하거나 

throws를 해서 JVM에게 넘기면 되는데 이 코드에서는 JVM에게 예외를 던지고 있습니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package sec04_exam;
 
public class ThrowEx1 {
 
    public static void main(String[] args) {
        
        try {
            //강제로 예외를 발생시키는 코드
            throw new Exception("고의로 예외 발생시켰음");
            //결코 도달없는 코드이므로 예외발생
//            System.out.println("아~~");
        } catch (Exception e) {
            System.out.println("예외 메시지 출력: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println("정상종료");
        
    }
 
}
 
cs

 

여기서는 try구문에서 예외를 강제로 발생시키고 있습니다.

여기서 예외 메세지는 "고의로 예외 발생시켰음"입니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
package sec04_exam;
 
public class ThrowEx2 {
//JVM에게 예외를 던진다. 디버그 창 나옴
    public static void main(String[] args) throws Exception{
        throw new Exception("고의로 예외 발생시켰음");
    
    }
 
}
 
cs

여기서는 try- catch구문을 쓰지않고 throws로 JVM에게 예외를 던집니다.

여기서는 디버그 창이 나옵니다.


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
package sec04_exam;
 
public class ThrowEx3 {
 
    public static void main(String[] args) {
        String file1 = createFile("test.txt");
        String file2 = createFile(""); //예외발생메시지 
        System.out.println(file1 + "파일이 성공적으로 생성되었습니다.");
        System.out.println(file2 + "파일이 성공적으로 생성되었습니다.");
    }
 
    private static String createFile(String fileName) {
        try {
            if (fileName==null || fileName.equals("")) {
                throw new Exception("파일 이름이 유효하지 않습니다.");
            }
        } catch (Exception e) {
            System.out.println("예외발생: " + e.getMessage());
            fileName = "제목없음 .txt";
        }
        return fileName;
    }
 
    
}
 
cs

 

마지막 throw예제입니다.

createFile메서드에서 fileName에 아무것도 들어있지 않으면 예외를 발생시키고 

예외 메시지는 "파일이름이 유효하지 않습니다."이고

그 예외가 발생하면 catch문에서 파일이름을 제목없음이라고 설정합니다.

<출력결과>


9. 사용자 정의 예외

자바 표준 API에서 제공하지 않는 예외는 사용자가 직접 정의해서 사용합니다.

예를 들어서 잔고가 부족합니다예외,회원가입 실패 예외 이런것들말입니다.

 

선언방법

*관례적으로 Exception을 붙여준다.

* 일반예외인지 실행예외인지 선택해서 상속받는다

* 기본 생성자 하나 생성한다.

* 조상의 생성자를 반드시 호출한다.

 

예제를 통해 함께 보겠습니다.

 

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
package sec06_exam;
 
public class Account {
 
    private long balance;
    
    public long getBalance() {
        return this.balance;
    }
    //입금하기
    public void deposit(int money) {
        this.balance +=money;
        System.out.println(money + "원 입금 완료되었습니다.");
    }
    
    //출금하기
    public void withDraw(int money)throws BalanceInsufficientException{
        if (this.balance<money) {
            throw new BalanceInsufficientException("잔고 부족: " + (money-this.getBalance()) +"원 모자람");
        }
        this.balance -=money;
        System.out.println(money + "원 출금완료되었습니다");
    }
}
 
 
cs

Account클래스 하나 만들었습니다.

여기서 withDraw메서드를 보시면 

만약에 출금하려는 돈이 잔고보다 많으면 

BalanceInsufficientException을 발생시키고 예외 메시지는 그 위에 잔고부족 어쩌고 입니다.

그리고 사용자 정의예외인 BalanceInsufficientException 을 호출한 곳으로 던집니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
package sec06_exam;
//사용자 정의 예외 클래스
public class BalanceInsufficientException extends Exception{
//예외에 접미사 붙임
    public BalanceInsufficientException() {
    
    }
    public BalanceInsufficientException(String message) {
        super(message); //조상클래스의 생성자호출
    }
}
 
cs

사용자 정의 예외 만드는 법.

아까 위에서 말씀드린 대로 

Exception을 상속(일반예외 상속)

기본 생성자 하나 호출

조상의 클래스 생성자 호출 하고 있습니다.

이름은 Exception을 관례적으로 붙여주었습니다.

 

 

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
package sec06_exam;
 
public class AccountEx {
 
    public static void main(String[] args) {
        Account account = new Account();
        account.deposit(100000);
        System.out.println("현재 잔액: " + account.getBalance()+"원");
        
        try {
            account.withDraw(5000);
            System.out.println("현재 잔액: " + account.getBalance()+"원");
            account.withDraw(150000);//예외발생
            System.out.println("현재 잔액: " + account.getBalance()+"원");
            
        } catch (Exception e) {
            System.out.println("예외 원인: " + e.getMessage());
//            e.printStackTrace();
            
        }
    }
    
}
 
 
cs

실행 클래스에서 

주석 처리 한 부분을 보시면 잔고보다 출금하려는 금액이 많아서 예외가 발생합니다.

 

<출력결과>

이렇게 하여 예외처리 포스팅을 마치겠습니다.

728x90