Power Java 제15장 예외 처리 (Exception Handling)
예외는 오류가 발생하더라도 오류를 우아하게 처리하게 합니다. 이번 장에서 학습할 내용 예외 처리란? 예외처리기의 개요 예외의 종류 예외와 메소드 예외 생성하기 예외는 오류가 발생하더라도 오류를 우아하게 처리하게 합니다.
Chapter Goals To learn how to throw exceptions To be able to design your own exception classes To understand the difference between checked and unchecked exceptions To learn how to catch exceptions To know when and where to catch an exception
오류처리(Error Handling) 전형적인 방식: Method returns error code 문제점: Forget to check for error code Failure notification may go undetected 문제점: Calling method may not be able to do anything about failure Program must fail too and let its caller worry about it Many method calls would need to be checked
오류처리(Error Handling) Instead of programming for success you would always be programming for failure: x.doSomething() if (!x.doSomething()) return false;
예외던지기(Throwing Exceptions) Can't be overlooked Sent directly to an exception handler–not just caller of failed method Throw an exception object to signal an exceptional condition Example: IllegalArgumentException: illegal parameter value IllegalArgumentException exception = new IllegalArgumentException("Amount exceeds balance"); throw exception; Continued…
Throwing Exceptions No need to store exception object in a variable: When an exception is thrown, method terminates immediately Execution continues with an exception handler throw new IllegalArgumentException("Amount exceeds balance");
1. 예외란? 예외(exception): 잘못된 코드, 부정확한 데이터, 예외적인 상황에 의하여 발생하는 오류 (예) 0으로 나누는 것과 같은 잘못된 연산이나 배열의 인덱스가 한계를 넘을 수도 있고, 디스크에서는 하드웨어 에러가 발생할 수 있다. ArithmeticException (java.lang 패키지) ArrayIndexOutOfBoundsException, (java.lang 패키지) DiskException (java.io 패키지)
예외의 예 실행되지 않음! 위 예에서는 ArithmeticException 객체가 던져지긴(throw) 하였으나 잡는(Catch) 코드가 별도로 없기 때문에 프로그램이 강제로 종료됨
중간 점검 문제 예외는 어떤 경우에 발생하는가? 0으로 나누는 경우, 배열의 인덱스가 한계를 넘는 경우, 하드웨어 에러등 2. 예외를 처리하는 경우와 처리하는 않은 경우를 비교하여 보라. 장점은 무엇인가?. 프로그램에서 오류를 감지하여 프로그램을 종료하거나 오류를 처리한 후에 계속 실행가능
2. 예외 처리기(Exception handler)의 개요 try/catch 블록 try 블록 예외가 발생되어 던져질 수 있는 블록 catch 블록 던져진 예외를 잡아서 처리하는 블록 기본 형식 try { … // 예외가 발생할 수 있는 코드 } catch (예외클래스 참조변수) { // 예외를 처리하는 코드 }
try/catch 블록에서의 실행 흐름 try/catch 블록에서의 실행 흐름 try/catch 블록의 특징
예제 #1 : ArithmeticException
예제 #2 : InputMismatchException
예제 #2에서 정수가 입력되었는지 검사하는 코드 삽입 예외 발생 차단 try/catch가 아무리 유용할지라도 만약 정상적인 코딩으로 예외가 발생할 수 있는 상황을 차단할 수 있다면 그렇게 하는 것이 더 좋은 프로그래밍 방법이다. 예제 #1에서 분모가 0인지 미리 검사 예제 #2에서 정수가 입력되었는지 검사하는 코드 삽입 If (y != 0) int result = x / y; import java.util.*; public class InputError { static Scanner scan = new Scanner(System.in); public static void main(String[] args) { int i = 0; System.out.print("정수를 입력하시오: "); if (scan.hasNextInt() ) i = scan.nextInt(); else System.out.println("정수가 아닙니다. "); }
finally 블록 오류가 발생하였건 발생하지 않았건 항상 실행되어야 하는 코드는 finally 블록에 넣을 수 있다.
중간 점검 문제 1. 배열에서 인덱스가 범위를 벗어나도 예외가 발생된다. 크기가 10인 배열을 생성하고 11번째 원소에 0을 대입하여 보라. 이 예외를 처리하는 try-catch 블록을 만들어 보라. int[] array = new int[10]; try { array[11] = 0; } catch(ArrayIndexOutOfBoundsException e){ System.out.println("배열의 범위를 벗어났습니다."); } (java.lang 패키지)
중간 점검 문제 2. 1번 문제에 배열 참조 변수에 null을 대입하여 배열을 삭제하는 문장을 finally 블록으로 만들어서 추가하여 보라. int[] array = new int[10]; try{ array[11] = 0; } catch(ArrayIndexOutOfBoundsException e){ System.out.println("배열의 범위를 벗어났습니다."); } finally{ array = null;
3. 예외의 종류 모든 예외는 Throwable 로부터 상속되어 Exception과 Error 클래스로 나뉘어서 분류된다.
Error와 Exception Error : 시스템에서 발생하는 심각한 에러이다. 프로그램 코드에 의해서 수습될 수 없는 심각한 오류 - 치명적인 내부 오류나 자바 가상 머신에서의 자원 고갈 등과 관련 - 프로그램이 바로 종료됨 - 거의 발생되지 않는다. 예외 RunTimeException 프로세스 실행중에 발생하는 에러 Exception IOException 파일 및 네트워크 입출력시에 발생하는 에러 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
RunTimeException
IOException 입출력과 관련된 오류
체크 예외와 비체크 예외 Error Throwable Exception Unchecked Exception 1) RunTimeException 2) IOException Unchecked Exception Checked Exception 컴파일러에 의해 코딩상에서 해당 예외에 대해 처리가 정확하게 이루어졌는지 체크하는 예외
다형성과 예외 다음과 같은 예외 상속 계층도를 가정하자.
다형성과 예외
다형성과 예외 catch 블록을 여러 개 작성할 때에는 구체적인 클래스 이름을 먼저 사용하고 일반적인 클래스 이름은 나중에 써야 한다.
중간 점검 문제 1. Error와 Exception은 어떻게 다른가? 2. 자바 코드에서 반드시 처리하여야 하는 예외는 어떤 것들인가? 어떤 클래스에서 상속되는가? RuntimeException을 제외한 모든 다른 예외로 IOException, OutOfMemoyError, AWTError, ThreadDeath등이 있다. 3. RuntimeException을 처리했는지를 왜 컴파일러에서는 검사하지 않는가?
예외가 발생하는 메소드 예외를 발생하는 메소드 처리 방법 (방법 1) 예외를 try/catch로 처리하는 방법 (방법 2) 예외를 상위 메소드로 전달하는 방법
try/catch 블록 이용
상위 메소드로 전달 예외를 상위에서 처리.
5. 예외 생성하기 예외는 throw 문장을 이용하여 생성한다. (throws vs. throw) new IOException();
중간 점검 문제 1. 만약 호출한 메소드가 예외를 발생할 가능성이 있다면 어떻게 하여야 하는가? try/catch블록을 이용하여서 처리한다. 2. 예외를 발생할 가능성이 있는 메소드는 어떻게 정의되는가? throws를 사용하여 상위 메소드로 넘긴다 3. throws가 의미하는 것은 무엇인가? 예외를 전달시키는 것
예외 생성의 예 예외 객체 생성
사용자 정의 예외 사용자 정의 예외: Exception 클래스의 서브 클래스를 생성시켜서 만든다. 프로그래머가 직접 예외 클래스를 새롭게 정의하여 이 클래스로부터 예외 객체를 만들어 사용할 수 있다. Exception 클래스 또는 그 서브클래스 중 하나를 상속받아야 한다.
예제: 예외발생 ExceptionTest.java
여러 개의 예외를 주고 받는 방법 네트워크에서 데이터를 받을 때 발생할 수 있는 예외 상황 네트워크 접속이 끊기거나 2) 하드 디스크가 꽉 찰 수 있음
예제
중간 점검 문제 1. DiskFailureException을 정의하여 보라. 이 클래스는 매개 변수가 없는 생성자를 가져야 한다. 만약 예외가 매개 변수가 없는 생성자를 이용하여 발생되면 getMessage()는 “Disk Failure!"를 반환하여야 한다. class DiskFailureException extends Exception{ public String getMessage(){ return "Disk Failure!"; }
중간 점검 문제 2. 사용자로부터 성적을 입력받아서 평균을 계산하는 프로그램을 작성하여 보자. 만약 사용자가 음수를 입력하면 NegativeNumberException을 발생한다. 이 예외를 catch 블록으로 잡아서 처리하는 코드도 추가하라. try{ for(int i = 0; i < 3; i++){ score = scan.nextInt(); if(score < 0) throw new NegativeNumberException(); tot += score; } System.out.println("평균은 " + tot/3.0 + "입니다."); } catch(NegativeNumberException e){ System.out.println(e.toString()); class NegativeNumberException extends Exception{ public NegativeNumberException(){ super("음수는 입력할 수 없음"); }
import java.util.ArrayList; LAB. : BankAccount 확장 import java.util.ArrayList; ArrayList<BankAccount> accounts = new ArrayList<BankAccount>(); Bank BankAccount ~kky/javaT/Lect/Bank Bank.java BankAccount.java BankTester.java
import java.util.ArrayList; /** This bank contains a collection of bank accounts. */ public class Bank { Constructs a bank with no bank accounts. public Bank() accounts = new ArrayList<BankAccount>(); } Adds an account to this bank. @param a the account to add public void addAccount(BankAccount a) accounts.add(a); Bank.java
Gets the sum of the balances of all accounts in this bank. Bank.java /** Gets the sum of the balances of all accounts in this bank. @return the total balance */ public double getTotalBalance() { double total = 0; for (BankAccount a : accounts) total = total + a.getBalance(); } return total;
Counts the number of bank account whose balance is at Bank.java /** Counts the number of bank account whose balance is at least a given value. @param atLeast the balance required to count an account @return the number of accounts having least the given balance */ public int count(double atLeast) { int matches = 0; for (BankAccount a : accounts) if (a.getBalance() >= atLeast) matches++; // found a match } return matches;
Finds a bank account with a given number. Bank.java /** Finds a bank account with a given number. @param accountNumber the number to find @return the account with the given number, or null if there is no such account */ public BankAccount find(int accountNumber) { for (BankAccount a : accounts) if (a.getAccountNumber() == accountNumber) // found a match return a; } return null; // no match in the entire array list
Gets the bank account with the largest balance. /** Gets the bank account with the largest balance. @return the account with the largest balance, or null if the bank has no accounts */ public BankAccount getMaximum() { if (accounts.size() == 0) return null; BankAccount largestYet = accounts.get(0); for (int i = 1; i < accounts.size(); i++) BankAccount a = accounts.get(i); if (a.getBalance() > largestYet.getBalance()) largestYet = a; } return largestYet; Bank.java
/** Add an account to the bank. @param accountNumber the account number of this account @param initialBalance the initial balance of this account */ public void addAccount(int accountNumber, double initialBalance) { . . .// account에 add 메소드 이용하여 새 bankAccount 객체추가 } Deposit money into an account. @param accountNumber the account number @param amount the amount to be deposited public void deposit(int accountNumber, double amount) . . . // find 메소드 이용하여 BankAccount 객체 찾은 다음 deposit 호출 Bank.java
Bank.java /** Withdraw money from an account. @param accountNumber the account number @param amount the amount to be withrawn */ public void withdraw(int accountNumber, double amount) { . . . // find 메소드 이용하여 BankAccount 객체 찾은 다음 withdraw 호출 } Get an account balance @return the account balance public double getBalance(int accountNumber) . . . // find 메소드 이용하여 BankAccount 객체 찾은 다음 getBalance 호출 private ArrayList<BankAccount> accounts;
/** A bank account has a balance that can be changed by deposits and withdrawals. */ public class BankAccount { Constructs a bank account with a zero balance. @param anAccountNumber the account number for this account public BankAccount(int anAccountNumber) accountNumber = anAccountNumber; balance = 0; } Constructs a bank account with a given balance. @param initialBalance the initial balance public BankAccount(int anAccountNumber, double initialBalance) balance = initialBalance; Gets the account number of this bank account. @return the account number public int getAccountNumber() return accountNumber; BankAccount.java
BankAccount.java /** Deposits money into the bank account. @param amount the amount to deposit */ public void deposit(double amount) { double newBalance = balance + amount; balance = newBalance; } Withdraws money from the bank account. @param amount the amount to withdraw public void withdraw(double amount) double newBalance = balance - amount; Gets the current balance of the bank account. @return the current balance public double getBalance() return balance; private int accountNumber; private double balance; BankAccount.java
BankTester.java /** This program tests the Bank class. */ public class BankTester { public static void main(String[] args) Bank bank = new Bank(); int dannysAccount = 0; int sallysAccount = 1; int harrysAccount = 2; int jerrysAccount = 3; bank.addAccount(dannysAccount, 1000); bank.addAccount(sallysAccount, 2000); bank.addAccount(harrysAccount, 3000); bank.addAccount(jerrysAccount, 10000);
bank.deposit(dannysAccount, 200); bank.withdraw(sallysAccount, 500); bank.deposit(harrysAccount, 1000); bank.withdraw(jerrysAccount, 7000); System.out.println( "Danny's Account Balance: " + bank.getBalance(dannysAccount)); System.out.println("Expected: 1200"); "Sally's Account Balance: " + bank.getBalance(sallysAccount)); System.out.println("Expected: 1500"); "Harry's Account Balance: " + bank.getBalance(harrysAccount)); System.out.println("Expected: 4000"); "Jerry's Account Balance: " + bank.getBalance(jerrysAccount)); System.out.println("Expected: 3000"); }
LAB. Programming 3. (P. 449) bank.withdraw(sallysAccount, 5000); 잔고가 인출금액보다 작은 경우 Bankaccount의 withdraw 메소드는 NegativeBalanceException 을 던지고, Bank의 withdraw 메소드는 이를 처리하여야 함.(메시지 출력하고 trace 스택 출력)
Q & A