예외처리(Exception)


- C나 C++ 등에서는 java보다는 중요하게 생각하지 않지만,

- java에서는 예외처리를 하지 않으면 컴파일이 되지 않는다.


1. 예외처리란?

예외가 발생했을 때 예외에 대한 제어권을 시스템(JVM)이 아니라 개발자가 가질 수 있게 하는 방법


2. 왜 예외처리를 쓸까?

1) 예외가 발생했을 때 메시지에 대한 표현문제

2) 프로그램의 비정상적인 종료



아래 예제의 경우 처리를 시스템이 하기 때문에 문제가 생긴다.

에러의 정보는 별도의 인스턴스로 따로 보관되어 시스템이 처리한다.


에러가 발생하면 에러의 컨트롤 타워는 시스템이 된다.

개발자는 에러가 발생하면 시스템을 기다릴 수 밖에 없다.


예외처리를 하면 예외가 발생 했을 때 주도권을 시스템이 아니라

개발자가 원하는 방식으로 예외를 처리할 수 있도록 한다.


package prjExeption;

public class ExceptionTest1 {

    public static void main(String[] args) {

        // TODO 예외처리_1

        int [] arr = new int[3];

        

        System.out.println("첫번째 예외처리 테스트");

        

        

        //에러가 났을 경우 자바가상머신이 에러를 처리한다.

        //에러메시지가 내가 원하는 메시지가 아니라 프로그램이 원하는 방향이다.

        //에러가 나면 프로그램이 종료된다.

        arr[7] = 10; // 문법을 틀린 것을 아니지만 값이 배열의 범위를 벗어나서 에러

        

        //가상머신이 정보를 가져가서 메시지가 보이지 않는다.

        //이를 비정상적인 종료라고 한다.

        System.out.println("이 메시지가 보이는가?");

        

    }

}


※ 결과

첫번째 예외처리 테스트

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 7

    at prjExeption.ExceptionTest1.main(ExceptionTest1.java:15)


오류를 냈더니 자바에서 예외처리를 한 클래스가

java.lang.ArrayIndexOutOfBoundsException 이라고 알려준다.

이 정보를 활용하여 예외처리를 해보자.


package prjExeption;

public class ExceptionTest1 {

    public static void main(String[] args) {

        // TODO 예외처리_1

        int [] arr = new int[3];

        

        System.out.println("첫번째 예외처리 테스트");

        

//        arr[7] = 10;

        

        try {        

        arr[7] = 10; // 문법을 틀린 것을 아니지만 값이 배열의 범위를 벗어나서 에러

        }

        //예외가 나면 특정 인스턴스 변수에 저장되는데

        //예외를 가져오고 다른 인스턴스 변수에 저장할 수 있는 저장주소를 인자값에 넣어야한다.

        //에러 관련 클래스인 ArrayIndexOutOfBoundsException에 예외처리한 주소를 넣어준다.

        //java는 예외사항에 대한 클래스를 미리 만들어 놓아서 골라쓰면 된다.

        //예외처리할 클래스가 어떤 클래스인지는 에러를 내면 java에서 알려준다.

        catch(ArrayIndexOutOfBoundsException err) {    

            System.out.println("배열의 범위를 벗어났습니다.");

        }

        

        //가상머신이 정보를 가져가서 메시지가 보이지 않는다.

        //이를 비정상적인 종료라고 한다.

        System.out.println("이 메시지가 보이는가?");

        

    }

}


※ 결과값

첫번째 예외처리 테스트

배열의 범위를 벗어났습니다.

이 메시지가 보이는가?



JAVA API 문서를 들어가자

java.base -> java.lang 에서 예외처리 클래스를 찾아보자.



3. 예외처리는 어떻게 쓸까?


예외처리는 다섯 가지 문법만 알면된다.

이중에서 가장 핵심 키워드는 try/catch 문이다.

나머지는 필수 기능은 아니지만 좀 더 편리함을 제공한다.


1) try/catch block


try{            // 예외가 나는지 안나는지 감시, 모니터링을 시도한다.

      ...        // {} 안에서만 모니터링한다.

}               

catch(...){   //예외가 나면 중간에 system이 빼가지 못하도록 잡는다.

      ...

}


1-1 다중 try/catch 

예외가 발생할 만한 곳을 try~ catch  을 넣는다.

여러가지 예외처리를 위해 다중으로 catch를 넣는다.



package prjExeption;

public class ExceptionTest1 {

    public static void main(String[] args) {

        // TODO 예외처리_1

        int [] arr = new int[3];

        

        System.out.println("첫번째 예외처리 테스트");

        

//        arr[7] = 10;

        

        try {        

        //arr[7] = 10; // 문법을 틀린 것을 아니지만 값이 배열의 범위를 벗어나서 에러

        int i = 10/0;

        

        }

        

        //예외가 나면 특정 인스턴스 변수에 저장되는데

        //예외를 가져오고 다른 인스턴스 변수에 저장할 수 있는 저장주소를 인자값에 넣어야한다.

        //에러 관련 클래스인 ArrayIndexOutOfBoundsException에 예외처리한 주소를 넣어준다.

        //java는 예외사항에 대한 클래스를 미리 만들어 놓아서 골라쓰면 된다.

        //예외처리할 클래스가 어떤 클래스인지는 에러를 내면 java에서 알려준다.

        //가급적이면 위에서 구체적으로 예외를 잡을 수 있다.

        

        //참조변수가 가리킨 문자열로 출력을 해주면 된다.

        // 어떤 이유 때문에 에러가 발생했는지 쓴다.

        catch(ArrayIndexOutOfBoundsException err) {    

            //.toString은 생략이 가능하다.

            System.out.println("배열의 범위를 벗어났습니다." + err.toString());

        

        }

        //산술연산을 잘 못 했을 때 처리해주는 클래스

        catch(ArithmeticException err) {

            System.out.println("산술연산을 잘못했다.");

        }

        

        //밑으로 내려갈 수 록 더 넓은 범위를 잡을 수 있도록 부모 클래스를 지정한다.

        //가급적이면 구체적으로 해주면 좋다.

        catch(RuntimeException err) {

            System.out.println("실행시 오류 발생 : " + err);

        }

        //부모와 자식간의 참조관례로 지정

        catch(Exception err) {

            System.out.println("실행 예외 발생");

            

        }

        

        

        //가상머신이 정보를 가져가서 메시지가 보이지 않는다.

        //이를 비정상적인 종료라고 한다.

        System.out.println("이 메시지가 보이는가?");

        

    }

}


※결과값

첫번째 예외처리 테스트

산술연산을 잘못했다.

이 메시지가 보이는가?



2) throw

- 예외를 던진다. 일부로 에러를 발생시킨다.

- 내가 처리하지 않고 다른 메서드가 처리 할 수 있도록 만든다.

- 자신을 호출하기 위한 메소드에게 넘긴다.

- 예외처리를 한꺼번에 처리하기 위한 목적이다.


<특징>

- 블럭내부에서만 던질 수 있다. (try ~ catch가 아니더라도 메서드 내에서 사용가능하다.)

- 한 블럭에 단 한개만 던질 수 있다.


<일부러 에러를 발생시키는 이유>

test를 위해서 

   - 예외처리가 잘 실행되는지 Test를 해본다.

   - 예외처리의 주체, 위치를 바꾸기 위한 방법



package prjExeption;

public class ExceptionTest3 {

    public static void main(String[] args) {

        // TODO Throw의 간단한 예제

        try {

            System.out.println("여기는 try블럭 내부이다...");

            // 일부러 예외문을 발생시켜서 catch문이 동작할 수 있게 한다.

            //필수 키워드는 아니지만  원하는 곳에서 에러를 낼 수 있어 편하게 일 할 수 있다.

            throw new RuntimeException();

        }

        catch(RuntimeException err)    {

            System.out.println("잘 처리됨..." + err);

        }

        

        System.out.println("try/ catch가 끝나고 난 뒤...");

    }

}


※ 결과값


여기는 try블럭 내부이다...

잘 처리됨...java.lang.RuntimeException

try/ catch가 끝나고 난 뒤...



package prjExeption;

public class ExceptionTest3 {

    public static void main(String[] args) {

        // TODO Throw의 간단한 예제

        try {

            System.out.println("여기는 try블럭 내부이다...");

            // 일부러 예외문을 발생시켜서 catch문이 동작할 수 있게 한다.

            //필수 키워드는 아니지만  원하는 곳에서 에러를 낼 수 있어 편하게 일 할 수 있다.

            throw new RuntimeException();

        }

        catch(RuntimeException err)    {

            System.out.println("잘 처리됨..." + err);

            //main 을 호출하는 호출자에게 에러를 던진다.

            //내가 처리하지 않고 다른 매서드가 처리하게끔 에러를 던진다.

            //예외처리를 한꺼번에 처리하기 위한 목적이다.

            //throw err;

        }

        

        //System.out.println("try/ catch가 끝나고 난 뒤...");

    }

}


※ 결과값


여기는 try블럭 내부이다...

잘 처리됨...java.lang.RuntimeException


package prjExeption;

public class ExceptionTest3 {

    public static void main(String[] args) {

        // TODO Throw의 간단한 예제

        try {

            System.out.println("여기는 try블럭 내부이다...");

            // 일부러 예외문을 발생시켜서 catch문이 동작할 수 있게 한다.

            //필수 키워드는 아니지만  원하는 곳에서 에러를 낼 수 있어 편하게 일 할 수 있다.

            throw new RuntimeException("...상황에서 예외를 던짐");

        }

        catch(RuntimeException err)    {

            //.getMessege는 throw가 예외를 던질 때 인스턴스에 저장된 메시지를 가져온다.

            System.out.println("잘 처리됨..." + err.getMessage());

            //main 을 호출하는 호출자에게 에러를 던진다.

            //내가 처리하지 않고 다른 매서드가 처리하게끔 에러를 던진다.

            //예외처리를 한꺼번에 처리하기 위한 목적이다.

            //throw err;

        }

        

        System.out.println("try/ catch가 끝나고 난 뒤...");

    }

}


※ 결과값


여기는 try블럭 내부이다...

잘 처리됨......상황에서 예외를 던짐

try/ catch가 끝나고 난 뒤...




3) trows


- 예외를 던진다.

- 대신 예외처리를 부탁할 때 사용한다.

- 블럭외부 (메서드 외부)에서만 던질 수 있다.

- 여러개를 던질 수 있다.


- 강제성이 없다.

- throw와 예외를 던지는 이유가 다르다.

- 예외를 발생하는게 아니라 예외처리를 대신해달라고 자신을 호출한 메서드에게 부탁한다.

- 예외처리를 넘긴 후 부탁한 메서드가 처리를 못하면 그 다음으로 넘어간다.

- 아무도책임을 지지 않으면 가상머신에게 넘어간다. 


package prjExeption;

import java.io.IOException;

public class ExceptionTest4 {

    

    //second가 해야할 책임을 IOExceptiond으로 first에게 넘김

    //예외처리를 IOException, ArithmeticException 두개 처리함

    static void second() throws IOException, ArithmeticException{

        System.out.println("second 호출됨");

        

        System.out.print("입력:");

        //내부로 부터 입력을 받기 때문에 예외처리를 안해주면 에러가 생김

        char data = (char)System.in.read();    // 입력에러를 발생시키기 위해 씀. IOException으로 처리.

        

        int i = 10/0;    //산술에러를 발생시키기 위해 씀. ArithmeticException로 처리

    }

    

    //second가 해야할 책임을 IOExceptiond으로 main에게 넘김

    static void first() throws IOException, ArithmeticException {

        System.out.println("first 호출됨");

        //first에서 예외처리를 해야하기 때문에 try ~ catch로 처리

        second();

    }

    public static void main(String[] args) {

        // TODO Throws 예제

        //main이 예외처리를 해준다.

        try {

            first();

        }

        //클래스이름 두개를 넣기 싫으면 부모클래스를 넣어준다.

        //부모클래스로 넣어주면 자세한 내용이 나오지는 않는다.

        catch(Exception err) {

            

            System.out.println("main에서 다 처리함");

        }

        

    }

}



※ 결과값


first 호출됨

second 호출됨

입력:a

main에서 다 처리함


예외처리는 적절하게 쓰는 것이 중요하다.



package prjExeption;

import java.io.IOException;

public class ExceptionTest4 {

    

    //second가 해야할 책임을 IOExceptiond으로 first에게 넘김

    //예외처리를 IOException, ArithmeticException 두개 처리함

    static void second() throws IOException, ArithmeticException{

        System.out.println("second 호출됨");

        

        System.out.print("입력:");

        //내부로 부터 입력을 받기 때문에 예외처리를 안해주면 에러가 생김

        char data = (char)System.in.read();    // 입력에러를 발생시키기 위해 씀. IOException으로 처리.

        

        int i = 10/0;    //산술에러를 발생시키기 위해 씀. ArithmeticException로 처리

    }

    

    //second가 해야할 책임을 IOExceptiond으로 main에게 넘김

    static void first() throws IOException, ArithmeticException {

        System.out.println("first 호출됨");

        //first에서 예외처리를 해야하기 때문에 try ~ catch로 처리

        second();

    }

    public static void main(String[] args) {

        // TODO Throws 예제

        //main이 예외처리를 해준다.

        try {

            first();

        }

        //클래스이름 두개를 넣기 싫으면 부모클래스를 넣어준다.

        //부모클래스로 넣어주면 자세한 내용이 나오지는 않는다.

        catch(Exception err) {

            

            System.out.println("main에서 다 처리함");

            

            //프로그램의 순서를 파악하기 쉽게 정보를 뿌려준다.

            //어디서 에러가 났는지 추적하기에 좋다.

            err.printStackTrace();

            

        }

        

    }

}


※ 결과값


first 호출됨

second 호출됨

입력:a

main에서 다 처리함

java.lang.ArithmeticException: / by zero

    at prjExeption.ExceptionTest4.second(ExceptionTest4.java:16)

    at prjExeption.ExceptionTest4.first(ExceptionTest4.java:23)

    at prjExeption.ExceptionTest4.main(ExceptionTest4.java:31)






4) finally 블럭


- try/catch 와 함께 사용


try {

    ...

}

finally{

    ... //반드시 실행해야 하는 코드를 쓴다. 

}


* 반드시 실행해야만 하는 코드를 묶어줄 때 사용

* 네트워크를 연결했는데 닫아주거나, 데이터연결했는데 닫아주는 등 


package prjExeption;

import java.io.IOException;

public class ExceptionTest3 {

    

    //throws 는 예외처리를 대신 대할라고 자신을 호출한 메서드에게 부탁한다.

    public static void main(String[] args) throws IOException{

        // TODO Throw의 간단한 예제

        try {

            System.out.println("여기는 try블럭 내부이다...");

            // 일부러 예외문을 발생시켜서 catch문이 동작할 수 있게 한다.

            //필수 키워드는 아니지만  원하는 곳에서 에러를 낼 수 있어 편하게 일 할 수 있다.

            throw new RuntimeException("...상황에서 예외를 던짐");

        }

        catch(RuntimeException err)    {


        }

        finally {

            System.out.println("여기는 반드시 실행됨...");

        }

        

        

        System.out.println("try/ catch가 끝나고 난 뒤...");

    }

}


※ 결과값


여기는 try블럭 내부이다...

잘 처리됨......상황에서 예외를 던짐

여기는 반드시 실행됨...

try/ catch가 끝나고 난 뒤...


에러가 나거나 안나거나 반드시 실행된다.



package prjExeption;

import java.io.IOException;

public class ExceptionTest3 {

    

    //throws 는 예외처리를 대신 대할라고 자신을 호출한 메서드에게 부탁한다.

    public static void main(String[] args) throws IOException{

        // TODO Throw의 간단한 예제

        try {

            System.out.println("여기는 try블럭 내부이다...");

            // 일부러 예외문을 발생시켜서 catch문이 동작할 수 있게 한다.

            //필수 키워드는 아니지만  원하는 곳에서 에러를 낼 수 있어 편하게 일 할 수 있다.

            throw new RuntimeException("...상황에서 예외를 던짐");

        }

        catch(RuntimeException err)    {

            //.getMessege는 throw가 예외를 던질 때 인스턴스에 저장된 메시지를 가져온다.

            System.out.println("잘 처리됨..." + err.getMessage());

            

            //1. 리턴값 반환 2. 중간에 메서드 강제종료. 지금은 2번의 경우이다.

            return;

                        

        }

        //return을 해도 finally가 실행된다.

        finally {

            System.out.println("여기는 반드시 실행됨...");

        }

        

        

        //System.out.println("try/catch가 끝나고 난 뒤...");

    }

}


※ 결과값


여기는 try블럭 내부이다...

잘 처리됨......상황에서 예외를 던짐

여기는 반드시 실행됨...


return을 써도 그 무엇을 써도 finally는 반드시 실행이 된다.

하.지.만. finally보다 더 쎈 놈이 있다.


package prjExeption;

import java.io.IOException;

public class ExceptionTest3 {

    

    //throws 는 예외처리를 대신 대할라고 자신을 호출한 메서드에게 부탁한다.

    public static void main(String[] args) throws IOException{

        // TODO Throw의 간단한 예제

        try {

            System.out.println("여기는 try블럭 내부이다...");

            // 일부러 예외문을 발생시켜서 catch문이 동작할 수 있게 한다.

            //필수 키워드는 아니지만  원하는 곳에서 에러를 낼 수 있어 편하게 일 할 수 있다.

            throw new RuntimeException("...상황에서 예외를 던짐");

        }

        catch(RuntimeException err)    {

            //.getMessege는 throw가 예외를 던질 때 인스턴스에 저장된 메시지를 가져온다.

            System.out.println("잘 처리됨..." + err.getMessage());

            //인자값은 어떻게 컴퓨터가 종료되었는지를 개발자가 미리 정한 숫자로 보관하기 위해 쓰지만

            //따로 지정이 안되어 있으면 아무거나 써도 된다.

            //이를 사용하면 강제적으로 강하게 종료가 된다.

            //될 수 있으면 쓰면 안된다.

            System.exit(0);

                        

        }

        //System.exit(0);를 쓰면 finally가 실행되지 않는다.

        finally {

            System.out.println("여기는 반드시 실행됨...");

        }

        

        

        //System.out.println("try/catch가 끝나고 난 뒤...");

    }

}


※ 결과값

여기는 try블럭 내부이다...

잘 처리됨......상황에서 예외를 던짐


System.exit() 는 될 수 있으면 쓰지 말자.



4. 관련클래스


Throwable


Class Direct Known Subclasses:

Error, Exception


Error 

- 하드웨어에서 발생한 물리적인 예외를 말한다.

- 메모리, 컴퓨터 꺼짐 등등

- 하드웨어상 문제는 개발자가 신경 쓸 필요가 없다.

- Errors는 에러의 자식을 모아놓은 것이다. 개발자가 볼 필요가 별로 없다.


Exception

- 로직이나 코딩에서 발생한 예외를 말한다.

- 소프트웨어 상 발생한 에러

- 개발자가 신경써야 할 부분이다.

- Exceptions은 개발자가 신경써서 봐야할 예외이다.




자바 API를 살펴보면,

Throwable 클래스가 예외처리 클래스들의 부모이다.




<주요 메서드>


get messege();


printStackTrace()


toString() //Object라는 부모로 부터 오버라이딩함



클래스를 잘 살펴보면 어마 어마하게 상속받는다.


RuntimeException


- 프로그램을 실행해봐야 발생 할 수 있는 예외들이다.

- 예외처리를 해도되고 안해도 된다.

- 컴파일 하는데까지 지장은 받지 않는다,

- 예외처리를 강제로 하지 않아도 된다,

- 출력할 때는 상관이 없으나 입력을 받을 때는 매우 조심해야한다.



throws 


값을 입력 받을 때느 반드시 예외처리를 해야한다.




+ Recent posts