-
27. 예외처리개발자 수업/Java 2021. 10. 20. 14:36
1. 오류의 종류
1) 에러(Error)
- 의도치 않게 프로그램이 종료되는 것을 에러라고 함
- 하드웨어의 잘못된 동작 또는 고장으로 인한 오류(ex 정전, 배드섹터 등)
- 에러가 발생하면 프로그램이 비정상 종료가 됨
- 정상 실행 상태로 돌아갈 수가 없음
2) 예외(Exception)
- 사용자의 잘못된 조작 또는 개발자의 잘못된 프로그래밍으로 인한 오류를 말함
- 예외가 발생하면 프로그램이 종료됨
- 단, 예외 처리를 추가하면 정상 실행 상태로 되돌릴 수가 있음
2. 프로그램에서의 예외(오류)
1) 컴파일 오류(compile error, checked exception)
- 프로그램 코드 작성 중 발생하는 문법적 오류
- 예외 처리 코드가 없다면 컴파일 예외 발생
- eclipse, Intellij에서 대부분 detection됨/* * ArthimeticException : 정수를 0으로 나눈 경우 발생 */ public class ArthimeticTest { public static void main(String[] args) { int a = 10; int b = 0; int c = a / b; System.out.println(c); } }
/* * NullPointerException : 초기화되지 않은 Object 사용하는 경우 */ public class NullPointerTest { public static void main(String[] args) { String str = null; try { System.out.println(str.toString()); } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("end"); } }
2) 실행 오류(runtime error, unchecked exception)
- 실행 중인 프로그램이 의도하지 않은 동작(bug)을 하거나 프로그램이 중지되는 오류
- 실행 오류는 비정상 종료가 되는 경우 시스템의 심각한 장애가 발생할 수 있음
- NullPointerException, ArrayIndexOutOfBoundsException, NumberFormatException ...import java.util.Arrays; /* * ArrayIndexOutOfBoundsException : 배열의 크기를 넘어서 위치를 참조하려는 경우 */ public class ArrayIndexOutOfBoundsTest { public static void main(String[] args) { int[] arr = new int[5]; System.out.println(Arrays.toString(arr)); try { for(int i=0; i<6; i++) { arr[i] = 6; System.out.println("arr[" + i + "] = " + arr[i]); } } catch (ArrayIndexOutOfBoundsException e) { System.out.println(e.getMessage()); System.out.println(e.toString()); } System.out.println("end"); } }
public class NumberFormatTest { public static void main(String[] args) { String str1 = "100"; String str2 = "100가"; // 숫자로 바꿀 수 없음 System.out.println(str1 + ", " + str2); int value1 = Integer.parseInt(str1); System.out.println(value1); int value2 = Integer.parseInt(str2); System.out.println(value2); int result = value1 + value2; System.out.println(result); } }
3. 예외 처리의 중요성
1) 프로그램의 비정상 종료를 피하여 시스템이 원활하게 실행되도록 함
2) 실행 오류가 발생한 경우 오류의 과정을 재현하는 것은 현실적으로 힘듦
3) 오류가 발생한 경우 log를 남겨서 추후 log 분석을 통해 그 원인을 파악하여 bug 수정하는 것이 중요
4. 예외 처리하기와 미루기
1) try ~ catch문
- try 블럭에는 예외가 발생할 가능성이 있는 코드를 try 블럭 안에 기술
- 예외가 발생하면 catch 블럭이 수행됨
try {
예외가 발생할 수 있는 코드
} catch (처리할 예외타입 e) {
try 블럭 안에서 예외가 발생했을 때 예외를 처리하는 부분
}import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class FileExceptionHandlingTest2 { public static void main(String[] args) { try(FileInputStream fis = new FileInputStream("a.txt")) { System.out.println("read"); } catch (FileNotFoundException e) { System.out.println(e.getMessage()); // e.printStackTrace(); } catch (IOException e) { System.out.println(e.getMessage()); // e.printStackTrace(); } System.out.println("end"); } }
2) try ~ catch-finally문
- finally 블럭에서 파일을 닫거나 네트워크를 닫는 등의 리소스 해제 구현을 함
- try {} 블럭이 수행되는 경우 finally {} 블럭은 항상 수행됨
- 여러 개의 예외 블럭이 있는 경우 각각 리소스를 해제하지 않고 finally 블럭에서 해제하도록 구현함import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class FileExceptionHandlingTest { public static void main(String[] args) { FileInputStream fis = null; try { fis = new FileInputStream("a.txt"); } catch (FileNotFoundException e) { System.out.println(e.getMessage()); // e.printStackTrace(); 개발할 때 이렇게 씀 } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("finally 구문은 항상 호출됨"); } System.out.println("end"); } }
3) try with resources문
- 리소스를 사용하는 경우 close() 하지 않아도 자동으로 해제되도록 함
- 리소스를 try() 내부에서 선언해야 함
- close()를 명시적으로 호출하지 않아도 try {} 블럭에서 열린 리소스는 정상적인 경우나 예외가 발생한 경우 모두 자동으로 해제됨
- 해당 리소스 클래스가 AutoCloseable 인터페이스를 구현해야 함
- FileInputStream의 경우에는 AutoCloseable을 구현하고 있음public class AutoCloseableObj implements AutoCloseable { @Override public void close() throws Exception { System.out.println("closing..."); } } public class AutoCloseTest { public static void main(String[] args) { AutoCloseable obj = new AutoCloseableObj(); try(obj) { // 여기 구문이 정상적으로 실행되고 나서 close() 호출되면서 리소스에 대해서 close()함 throw new Exception(); // 예외 발생하더라도 마찬가지로 close() 처리함 } catch(Exception e) { System.out.println("exception"); } System.out.println("end"); } }
4) 예외처리 미루기 (throws)
- 이를 사용하는 부분에서 처리하는 방법
- 메서드를 호출한 곳에서 예외를 처리하라고 떠넘김
- 호출한 곳에서 반드시 예외처리 코드가 있어야 함
- throws를 이용하면 예외가 발생할 수 있는 부분을 사용하는 문장에서 예외를 처리할 수 있음public class ThrowsTest { /* * - main()에서 method1()을 호출하고 있으니 여기서 예외처리를 해야 함 * - throws : 호출한 곳으로 예외를 다시 던지며, 호출한 곳에서 예외를 반드시 처리해야 함 */ public static void main(String[] args) throws Exception { try { method2(); } catch (Exception e) { System.out.println("예외발생 : 0으로 나눌 수 없습니다."); // e.printStackTrace(); } } public static void method1() throws Exception { method2(); } public static void method2() throws Exception { System.out.println(10/0); // 실행예외 } }
5) 하나의 try {} 블럭에서 예외가 여러 개 발생하는 경우
- 여러 개의 예외가 발생하는 경우 예외를 묶어서 하나의 방법으로 처리할 수도 있음
- 각각의 예외를 따로 처리할 수도 있음
- Exception 클래스를 활용하여 처리할 때 Exception 블럭은 맨 마지막에 위치해야 함import java.io.FileInputStream; import java.io.FileNotFoundException; public class ThrowsExceptionTest { public Class loadClass(String fileName, String className) throws ClassNotFoundException, FileNotFoundException { FileInputStream fis = new FileInputStream(fileName); Class c = Class.forName(className); return c; } public static void main(String[] args) { ThrowsExceptionTest test = new ThrowsExceptionTest(); try { test.loadClass("a.txt", "java.lang.String"); } catch (ClassNotFoundException | FileNotFoundException e) { System.out.println(e); // e.printStackTrace(); } System.out.println("end"); } }
5. 강제로 예외 발생시키기 - throw
1) 코드에서 강제로 예외를 발생시킴
6. 사용자 정의 예외 클래스 만들기
1) 자바 표준 API에서 제공하지 않는 예외는 직접 프로그래밍해서 만들어야 함
2) 응용 애플리케이션 서비스에서 발생하는 예외
- 슈팅게임에서 비행기가 리소스가 없어도 비행하는 경우
- 회원가입 실패, 계좌잔고 부족, 계좌이체 실패 등
3) 기존 예외 클래스 중 가장 유사한 예외 클래스에서 상속받아 사용자 정의 예외 클래스를 만듦public class Account { private long balance; // 현재 잔액을 가져오기 public long getBalance() { return this.balance; } // 입금하기 public void deposit(int money) { this.balance += money; } // 출금하기 public void withdraw(int money) throws BalanceException { if(this.balance < money) { throw new BalanceException("잔액 부족"); // 예외발생시킴 } this.balance -= money; } } // 사용자 정의 예외 클래스 public class BalanceException extends Exception { public BalanceException() {} public BalanceException(String message) { super(message); } } public class AccountTest { public static void main(String[] args) { Account account = new Account(); account.deposit(50000); System.out.println("현재 잔액 : " + account.getBalance()); try { account.withdraw(10000); System.out.println("현재 잔액2 : " + account.getBalance()); account.withdraw(50000); System.out.println("현재 잔액3 : " + account.getBalance()); } catch (BalanceException e) { System.out.println("예외 원인 : " + e.getMessage()); // e.printStackTrace(); } finally { System.out.println("정상 종료합니다."); } } }
4) 기본적으로 Exception 클래스를 상속해서 만들 수 있음
5) 패스워드에 대한 예외처리하기
- 패스워드를 입력할 때 다음과 같은 경우 오류처리를 합니다.
- 비밀번호는 null일 수 없습니다.
- 비밀번호의 길이는 10 이상입니다.
- 비밀번호는 문자로만 이루어질 수 없습니다. (하나 이상의 숫자나 특수문자 필요)public class PasswordException extends Exception { public PasswordException(String message) { super(message); } } public class PasswordTest { private String password; public void setPassword(String password) throws PasswordException { if(password == null) { throw new PasswordException("비밀번호는 null일 수 없습니다."); } else if (password.length() < 10) { throw new PasswordException("비밀번호의 길이는 10 이상입니다."); } else if (password.matches("[a-zA-Z]+")) { throw new PasswordException("비밀번호는 문자로만 이루어질 수 없습니다."); } this.password = password; } public static void main(String[] args) { PasswordTest test = new PasswordTest(); String password = null; try { test.setPassword(password); System.out.println("오류없음1"); } catch (PasswordException e) { System.out.println(e.getMessage()); // e.printStackTrace(); } System.out.println("end"); password = "abc"; try { test.setPassword(password); System.out.println("오류없음2"); } catch (PasswordException e) { System.out.println(e.getMessage()); // e.printStackTrace(); } System.out.println("end2"); password = "abcdefghijk"; try { test.setPassword(password); System.out.println("오류없음3"); } catch (PasswordException e) { System.out.println(e.getMessage()); // e.printStackTrace(); } System.out.println("end3"); password = "abcdefghi1#"; try { test.setPassword(password); System.out.println("오류없음4"); } catch (PasswordException e) { System.out.println(e.getMessage()); // e.printStackTrace(); } System.out.println("end4"); } }