JAVA 예외처리

금송·2024년 9월 11일
0

이론

목록 보기
17/17
post-thumbnail

예외처리

에러 상황을 처리하는 것

에러 - 컴퓨터 하드웨어의 오작동 또는 고장으로 인해 응용프로그램 실행 오류가 발생했다는 것을 에러라고 함.

개발자 입장에서 에러

예상치 못한 에러, 대응 불가능한 에러 (ex. 데이터센터 화재, 천재지변, 서버손상으로 인한 에러, 메모리 이슈로 인한 에러)

대응이 가능한 에러 (ex. 개발자가 대응할 수 있는 에러)

→ 예외가 발생했을 때 상황에서 처리할 수 있는 코드 작성

없는 파일에 데이터를 쓰고 싶을 경우 → 시스템 장애가 남

→ 이럴 경우 디폴트 파일로 향하도록 코드 작성

Database 데이터 저장 → 시스템 장애남

→ 실패에 대한 예외 처리

예외처리 → ~~Exception

→ 시스템 장애를 막고, 예상치 못한 서비스의 shutdown을 막을 수 있음

자바(JDK)에서 정의하는 예외 (Exception) 예외처리 대상

예외가 발생하면 프로그램을 중단하고 오류 메세지를 보여준다.

  • CheckedException : 명시적으로 예외 처리를 강제화 해야하는 예외 (컴파일러가 빨간 밑줄로 해당 에러 상황을 알려준다.) - 생성자가 일으킬 수 있는 에러를 알려줌 → 컴파일 오류

  • UncheckedException : 명시적으로 예외 처리를 강제화 하지 않아도 되는 예외

    • 프로그램이 실행되는 단계(런타임)에 오류가 남. → 런타임 오류

      ArithmeticException 오류 - 런타임 오류 예시, 산술 오류

최상위 부모 클래스 - Object

  • Throwable : 최상위 예외
  • Error : 메모리 부족이나 심각한 시스템 오류같은 복구가 불가능한 예외. 개발자가 잡을 수 없는 예외
  • Exception : 컴파일러가 체크하는 checked exception. 단 RuntimeException은 예외
  • RuntimeException : 컴파일러가 체크하지 않는 unchecked exception. 런타임 예외

FileNotFoundException - 원하는 파일을 찾을 수 없다는 오류

ArrayIndexOutOfBoundsException - 인덱스 범위를 초과하여 사용했을 때 발생하는 오류

try-catch문

예외를 처리하는 문

try {
    <수행할 문장 1>;
    <수행할 문장 2>;
    ...
} catch (예외1) {
    <수행할 문장 A>;
    ...
} catch (예외2) {
    <수행할 문장 a>;
    ...
}

혹은

try {
    <수행할 문장 1>;
    <수행할 문장 2>;
    ...
} catch (예외1 | 예외2) {  // 둘 중 하나가 예외일 경우 A 수행 (멀티 catch)
    <수행할 문장 A>;
    ...
}
  • 실습 코드 01
    package chap09;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileIOSample {
        public static void main(String[] args){
            // 파일 내용 읽어오는 코드
            try{
                String file = System.getProperty("user.dir");
                String filename = file + "/file01.txt";
                System.out.println(filename);
    
                String path = FileIOSample.class.getResource("").getPath();
                System.out.println("path : " + path);
    
                // BufferedReader reader = new BufferedReader(new FileReader("file01.txt"));
                // BufferedReader reader = new BufferedReader(new FileReader("file.txt")); // 파일을 찾을 수 없습니다.
                BufferedReader reader = new BufferedReader(new FileReader(filename));
                System.out.println(reader.readLine());
                reader.close(); // 닫아주는 습관을 들여야 데이터 누수가 안일어남.
            } catch (FileNotFoundException e){
                System.out.println("파일을 찾을 수 없습니다.");
            } catch (IOException e){
                System.out.println("IOException 발생");
            }
        }
    }
    

부모 예외 클래스보다 아래에 자식 예외 클래스를 선언하면 컴파일 오류가 난다

  • 실습 코드 02 다중 catch문
  • 두가지 예외 처리 합쳐보기(FileNotFoundException | ArithmeticException e)
    다중 catch문 주의사항
    - 상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야 한다.
    ```java
    package chap09;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileIOSample {
        public static void main(String[] args){
            // 파일 내용 읽어오는 코드
            int b = Integer.parseInt(args[0]);
            try{
                int result = 5 / b; // ArithmeticException 발생 가능
                String file = System.getProperty("user.dir");
                String filename = file + "/fi.txt";
                System.out.println(filename);
    
                // String path = FileIOSample.class.getResource("").getPath();
                // System.out.println("path : " + path);
    
                // BufferedReader reader = new BufferedReader(new FileReader("file01.txt"));
                // BufferedReader reader = new BufferedReader(new FileReader("file.txt")); // 파일을 찾을 수 없습니다.
                BufferedReader reader = new BufferedReader(new FileReader(filename));
                System.out.println(reader.readLine());
                reader.close(); // 닫아주는 습관을 들여야 데이터 누수가 안일어남.
            } catch (FileNotFoundException | ArithmeticException e){ // | 둘중에 하나의 오류가 발생 했을 때 실행.
                System.out.println("Exception 발생");
            } catch (IOException e){
                //FileNotFoundException 처리문이 없어도 해당 부모클래스가 실행시켜 에러를 잡을 수 있지만 해당 처리문에서만 이벤트 적용 시켜야 한다면 따로 빼서 적어주는게 맞다.
                System.out.println("IOException 발생");
    //        } catch (Exception e){
    //            System.out.println("예외 발생");
    //            // 예외 출력문만 남길 때 (= 로깅)
            }
        }
    }
    
    // 결과
    Exception 발생
    
    ```

finally

어떤 예외가 발생하더라도 반드시 실행되는 부분이 바로 finally구문

예외가 발생하던 안하던 실행시키고 싶은 구문을 작성하는 공간.

  • 실습코드
    package chap09;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileIOSample {
        public static void main(String[] args){
            // 파일 내용 읽어오는 코드
            int b = Integer.parseInt(args[0]);
            try{
                int result = 5 / b; // ArithmeticException 발생 가능
                String file = System.getProperty("user.dir");
                String filename = file + "/fi.txt";
                System.out.println(filename);
    
                // String path = FileIOSample.class.getResource("").getPath();
                // System.out.println("path : " + path);
    
                // BufferedReader reader = new BufferedReader(new FileReader("file01.txt"));
                // BufferedReader reader = new BufferedReader(new FileReader("file.txt")); // 파일을 찾을 수 없습니다.
                BufferedReader reader = new BufferedReader(new FileReader(filename));
                System.out.println(reader.readLine());
                reader.close(); // 닫아주는 습관을 들여야 데이터 누수가 안일어남.
                System.out.println("byyyye");           // 예외 발생해도 실행하고 싶은 코드
    
            } catch (FileNotFoundException | ArithmeticException e){ // | 둘중에 하나의 오류가 발생 했을 때 실행.
                System.out.println("Exception 발생");
            } catch (IOException e){
                //FileNotFoundException 처리문이 없어도 해당 부모클래스가 실행시켜 에러를 잡을 수 있지만 해당 처리문에서만 이벤트 적용 시켜야 한다면 따로 빼서 적어주는게 맞다.
                System.out.println("IOException 발생");
    //        } catch (Exception e){
    //            System.out.println("예외 발생");
    //            // 예외 출력문만 남길 때 (= 로깅)
            } finally {
    						System.out.println("byyyye");           // 예외 발생해도 실행해야하는 코드
            }
        }
    }
    // 결과
    Exception 발생
    byyyye
    
  • 숫자 순서 맞추기
    package chap09;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileIOSample {
        public static void main(String[] args){
            // 파일 내용 읽어오는 코드
            // b = 10
            int b = Integer.parseInt(args[0]);
            try{
                int result = 5 / b; // ArithmeticException 발생 가능
                System.out.println(result); // 0.5 -> 정수형 0
                String file = System.getProperty("user.dir");
                String filename = file + "/fi.txt";
                // System.out.println(filename);
    
                // String path = FileIOSample.class.getResource("").getPath();
                // System.out.println("path : " + path);
    
                System.out.println(1);      // 1
                // BufferedReader reader = new BufferedReader(new FileReader("file01.txt"));
                // BufferedReader reader = new BufferedReader(new FileReader("file.txt")); // 파일을 찾을 수 없습니다.
                BufferedReader reader = new BufferedReader(new FileReader(filename));
                System.out.println(reader.readLine());
                reader.close(); // 닫아주는 습관을 들여야 메모리 누수가 안일어남. 해당 문은 finally에 적어주기. 
    
                System.out.println(2);
    
            } catch (FileNotFoundException | ArithmeticException e){ // | 둘중에 하나의 오류가 발생 했을 때 실행.
                //System.out.println("Exception 발생");
                System.out.println(3);      // 3
            } catch (IOException e){
                //FileNotFoundException 처리문이 없어도 해당 부모클래스가 실행시켜 에러를 잡을 수 있지만 해당 처리문에서만 이벤트 적용 시켜야 한다면 따로 빼서 적어주는게 맞다.
                //System.out.println("IOException 발생");
                System.out.println(4);
    //        } catch (Exception e){
    //            System.out.println("예외 발생");
    //            // 예외 출력문만 남길 때 (= 로깅)
            } finally {
                //System.out.println("byyyye");           // 예외 발생해도 실행해야하는 코드
                System.out.println(5);      // 5
            }
        }
    }
    // 결과
    0
    1
    3
    5
  • finally문
    package chap09;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileIOSample {
        public static void main(String[] args){
            // 파일 내용 읽어오는 코드
            // b = 10
            int b = Integer.parseInt(args[0]);
            BufferedReader reader = null;
            try{
                int result = 5 / b; // ArithmeticException 발생 가능
                System.out.println(result); // 0.5 -> 정수형 0
                String file = System.getProperty("user.dir");
                String filename = file + "/fi.txt";
    
                reader = new BufferedReader(new FileReader(filename));
                System.out.println(reader.readLine());
                
    
            } catch (FileNotFoundException | ArithmeticException e){ // | 둘중에 하나의 오류가 발생 했을 때 실행.
                System.out.println("Exception 발생");
            } catch (IOException e){
                //FileNotFoundException 처리문이 없어도 해당 부모클래스가 실행시켜 에러를 잡을 수 있지만 해당 처리문에서만 이벤트 적용 시켜야 한다면 따로 빼서 적어주는게 맞다.
                System.out.println("IOException 발생");
    //        } catch (Exception e){
    //            System.out.println("예외 발생");
    //            // 예외 출력문만 남길 때 (= 로깅)
            } finally {
                //System.out.println("byyyye");           // 예외 발생해도 실행해야하는 코드
                try{
                reader.close(); // 닫아주는 습관을 들여야 메모리 누수가 안일어남. 해당 문은 finally에 적어주기.
                } catch (Exception e){
                    // reader자원 닫으면서 예외 발생했을 때
                }
            }
        }
    }
    
  • try-with-resource (예외 직접 처리)
    package chap09;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileIOSample {
        public static void main(String[] args){
            // 파일 내용 읽어오는 코드
            // b = 10
            int b = Integer.parseInt(args[0]);
            String file = System.getProperty("user.dir");
            String filename = file + "/fi.txt";
            try (BufferedReader reader = new BufferedReader(new FileReader(filename))){
                int result = 5 / b; // ArithmeticException 발생 가능
                System.out.println(result); // 0.5 -> 정수형 0
    
                // reader = new BufferedReader(new FileReader(filename)); // 해당문 try위로 올리기
                System.out.println(reader.readLine());
    
            } catch (FileNotFoundException | ArithmeticException e){ // | 둘중에 하나의 오류가 발생 했을 때 실행.
                System.out.println("Exception 발생");
            } catch (IOException e){
                //FileNotFoundException 처리문이 없어도 해당 부모클래스가 실행시켜 에러를 잡을 수 있지만 해당 처리문에서만 이벤트 적용 시켜야 한다면 따로 빼서 적어주는게 맞다.
                System.out.println("IOException 발생");
    //        } catch (Exception e){
    //            System.out.println("예외 발생");
    //            // 예외 출력문만 남길 때 (= 로깅)
            }
        }
    }
    
  • throws
    package chap09;
    
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileIOSample2 {
        public static void main(String[] args){
            FileIOSample2 fileIOSample2 = new FileIOSample2();
            // 방법 1
             try{ // throws에서 넘긴 오류 try catch문으로
                 fileIOSample2.readFile();
             } catch(IOException e){
                 System.out.println("IOException 예외 발생 시 예외 처리");
             }
        }
    
        public void readFile() throws IOException { // 이렇게 예외 처리 하면 최종 호출측에 처리를 넘기는 행동.
             BufferedReader reader = new BufferedReader(new FileReader(""));
             System.out.println(reader.readLine());
             reader.close();
            //방법 2
    //        try{
    //            BufferedReader reader = new BufferedReader(new FileReader(""));
    //            System.out.println(reader.readLine());
    //            reader.close();
    //        } catch (Exception e){
    //            System.out.println("예외 처리");
    //        }
            //여기서 바로 처리 해주는 것이 좋음.
        }
    
    }
    

사용자 정의 예외

public class XXXException extends **Exception** { 
	public XXXException() { }
	public XXXException(String message) {  super(message);  }
}

or 

public class XXXException extends **RuntimeException** {
	public XXXException() { }
	public XXXException(String message) {  super(message);  }
}

사용자 정의 예외 킄래스는 컴파일러가 체크하는 (CheckedException) 일반 예외로 선언할 수 있고, 컴파일러가 체크하지 않는 (UncheckedException) 실행 예외로 선언할 수도 있음.

예외 클래스 네이밍은 Exception으로 끝나는 것이 좋음.

  • 실습 코드
    package chap09.custom;
    
    public class CustomExceptionSample {
        void method() throws BalanceInsufficientException{
            // 예외 수동으로 발생시키기 (만약 런테임 오류라면 컴파일 에러가 발생하지 않음.)
            throw new BalanceInsufficientException("잔고부족 에러 메시지");
        }
    }
    package chap09.custom;
    
    /*
        사용자 정의 예외 (checked Exception)
    * */
    public class BalanceInsufficientException extends Exception {
        public BalanceInsufficientException() {
    
        }
    
        public BalanceInsufficientException(String message){
            super(message);
        }
    }
    package chap09.custom;
    
    /*
        은행계좌 클래스
    * */
    public class Account {
        long balance; // 잔금 필드로 선언
    
        Account(long balance) {
            this.balance = balance;
        }
        // 예금
        void deposit(long money){
            this.balance += money;
        }
    
        // 출금
        void withdraw(long money) throws BalanceInsufficientException {
            if(balance < money) { // 출금 불가
                throw new BalanceInsufficientException("잔액이 부족합니다. 남은 금액 : " + balance);
            }
            balance -= money;
        }
    }
    package chap09.custom;
    
    public class Bank {
        public static void main(String[] args){
            Account account = new Account(30000);
    
            account.deposit(40000);
            try{
                account.withdraw(100000);
            } catch (BalanceInsufficientException e){
                System.out.println(e.getMessage());
            }
        }
    }

throw키워드와 throws키워드의 차이점

throw - 실제로 메소드 내에 예외를 생성. 발생시키는 데 사용. 처리는 따로 하지 않음.

throw new BalanceInsufficientException()

throws - 메서드가 예외를 던질 수 있음을 선언. 메소드 선언부에서 사용, 해당 메소드가 처리하지 않은 예외를 호출자에게 전달한다는 의미를 내포하고 있음.

public void withdraw(int money) throws BalanceInsufficientException
profile
goldsong

0개의 댓글