ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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");
    	}
    }

    '개발자 수업 > Java' 카테고리의 다른 글

    29. 멀티스레드  (0) 2021.10.22
    28. IO입출력  (0) 2021.10.20
    26. 스트림  (0) 2021.10.19
    25. 람다식  (0) 2021.10.19
    24. 내부클래스  (0) 2021.10.19

    댓글