Java 6일차

진창호·2023년 1월 25일

Java

목록 보기
6/9

Java엔 계층적인 예외 클래스가 있다.

아래 사진을 살펴보자.
예외 클래스

Error는 아래와 같다.

  1. 메모리 부족, 스택 오버플로우와 같이 발생하면 복구할 수 없는 상황이다.
  2. 프로그램의 비 정상적 종류를 막을 수 없다.
  3. 미래에는 예외 처리 가능할 수도 있지만, 현재는 JVM이 다운되는 것이 최선의 방안이다.

Exception은 아래와 같다.

  1. 읽으려는 파일이 없거나 네트워크 연결이 안 되는 등 수습될 수 있는 상황이다.
  2. 프로그램 코드에 의해 수습될 수 있다.
  3. 대처 코드가 있으면 컴파일이 진행되는 Checked 계열(대처할 수 없는 상황을 대비함)
    대처 코드가 없어도 컴파일이 진행되는 Unchecked 계열(코드를 고쳐야 함)이 있다.

※ Unchecked 계열의 예외는 예외 처리의 대상이 아니라 디버깅의 대상이 된다.
따라서 조건문으로 Unchecked 계열의 예외는 피해야 한다.


Java는 예외 처리를 지원한다.

Java는 try ~ catch를 이용해 예외를 처리한다. 아래 코드를 살펴보자.

public static void main(String[] args) {
        int[] intArray = { 10 };
        
        try {
        	System.out.println(intArray[2]);
        } catch (ArrayIndexOutOfBoundsException e) {
        	System.out.println("예외 처리 완료 : " + e.getMessage());
        	e.printStackTrace();
        }
        System.out.println("프로그램 종료합니다.");
    }

실행 결과는 아래와 같다. 예외가 적절히 처리된 걸 알 수 있다.

예외 처리 완료 : 2
프로그램 종료합니다.
java.lang.ArrayIndexOutOfBoundsException: 2
at g_collection.exception.SimpleException.main(SimpleException.java:7)

try ~ catch 문의 흐름을 자세히 알아보자.
try ~ catch

  1. try에서 예외가 발생하면 해당 Exception 클래스의 객체 생성 후 이를 던진다.
    (throw new XXException)
  2. 던져진 exception을 처리할 수 있는 catch에서 받은 후 예외 처리한다.
    (exception을 catch가 처리하지 못하면 예외 처리는 실패한다.)
  3. 예외 처리가 끝나면 try ~ catch 문을 벗어난다.

Throwable 클래스의 주요 메서드는 아래와 같다.

  1. public String getMessage() : 발생된 예외에 대한 구체적인 메시지를 반환한다.
  2. public Throwable getCause() : 예외의 원인인 Throwable 객체 혹은 null을 반환한다.
  3. public void printStackTrace() : 메서드 호출 스택을 출력한다.

Java는 다중 예외 처리를 지원한다.

다중 예외 처리 시 주의해야 할 점은 아래와 같다.

  1. 상위 타입의 예외가 뒤에 있는 예외보다 위에 선언되면 뒤에 있는 catch가 동작하지 못하므로 예외가 발생한다.
  2. try에서 예외 발생 시 가장 상위 catch부터 예외인 지 살펴본다.
    그리고 적절한 catch를 만나면 예외 처리 후 try ~ catch 문을 나간다.
  3. 예외는 상황별로 처리하는 걸 권장한다. 따라서 Exception e 같은 예외 처리는 지양한다.
  4. 모든 예외를 세분화하는 것도 권장하지 않는다. |를 이용해 여러 개의 Exception을 처리할 수 있다.
    (ClassNotFoundException | FileNotFoundException e)

아래 코드는 다중 예외 처리의 예시이다.

public static void main(String[] args) {
        try {
        	Class.forName("abc.def");
        	new FileInputStream("abc.txt");
        	DriverManager.getConnection("some");	
        } catch (ClassNotFoundException e) {
        	// Exception e로 하면 아래 catch들 예외 발생
        	System.out.println("클래스를 찾을 수 없습니다.");
        } catch (FileNotFoundException e) {
        	System.out.println("파일을 찾을 수 없습니다.");
        } catch (SQLException e) {
        	System.out.println("DB 접속 실패");
        }
        
        System.out.println("프로그램 정상 종료");
    }

출력 결과는 아래와 같다.

클래스를 찾을 수 없습니다.
프로그램 정상 종료


Java는 try ~ catch에 finally를 지원한다.

finally는 예외 발생 여부와 상관없이 항상 실행된다. 아래 코드를 살펴보자.

public static void main(String[] args) {
        int num = new Random().nextInt(2); // 0 혹은 1
        try {
            System.out.println("code 1, num: " + num);
            int i = 1 / num;
            System.out.println("code 2 - 예외 없음: " + i);
            return;
        } catch (ArithmeticException e) {
            System.out.println("code 3 - exception handling 완료");
        } finally {
            System.out.println("code 4 - 언제나 실행");
        }
        System.out.println("code 5");
    }

예외가 발생하면 1 -> 3 -> 4 -> 5 순으로,
예외가 발생하지 않으면 1 -> 2 -> 4 순으로 실행된다.
즉, try에 return이 있음에도 finally가 실행된다.

finally를 사용하면 코드가 지저분해질 수 밖에 없다. 아래 코드를 살펴보자.

public void useStream() {
        FileInputStream fileInput = null;
        try {
            fileInput = new FileInputStream("abc.txt");
            fileInput.read();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileInput != null) {
                try {
                    fileInput.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

따라서 JDK 1.7 이상부터 AutoCloseable 인터페이스를 구현한 객체들에 대해
자동으로 close를 호출한다.
아래 코드를 살펴보자.

public void useStream() {
        try (FileInputStream fileInput = new FileInputStream("abc.txt")) {
            fileInput.read();
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }

예외 발생 여부에 상관없이 close가 실행된다.

※ AutoCloseable 인터페이스를 implements하는 클래스는 아래와 같다.
AutoCloseable


Java는 throws를 지원한다.

예외 처리의 책임을 호출한 곳으로 전달하는 방법이 throws이다.
아래 코드를 살펴보자.

public class CTest {
	
	void aa() throws FileNotFoundException {
		FileInputStream fis = new FileInputStream("a.txt");
	}
	
	public CTest() throws FileNotFoundException {
		aa();
	}

	public static void main(String[] args) throws FileNotFoundException {
		new CTest();
	}
}

aa() -> CTest() -> main() 순으로 예외 처리의 책임이 전달된다.

※ 메서드 오버라이딩 시 자식 메서드에선 부모 메서드보다 작거나 같은 범위의
throws만 가능하다.


Java는 사용자가 Exception을 작성할 수 있다.

Checked Exception은 Exception 클래스를 상속받아서,
Unchecked Exception은 RuntimeException 클래스를 상속받아서 Exception을 작성한다.

아래 코드는 Checked Exception을 작성한 예시이다.

class MyException extends Exception {
	String msg;
	
	public MyException(String msg) {
		super(msg);
		this.msg = msg;
	}

	@Override
	public String toString() {
		return "MyException [" + msg + "]";
	}
}

public class DTest {

	public DTest() { // 처리 가능할 때 try ~ catch
		try {
			m1();
		} catch (MyException e) {
			System.out.println("오류 처리 : " + e);
			System.out.println("오류 처리 : " + e.toString());
			System.out.println("오류 처리 getMessage() : " + e.getMessage());
		}
	}
	
	void m1() throws MyException { // 전파 목적이므로 throws
		int type = 1;
		if (type == 0) {
			System.out.println("정상");
		} else {
			throw new MyException("오류");
		}
		
	}
	
	public static void main(String[] args) throws MyException {
		new DTest();
	}
}

출력 결과는 아래와 같다.

오류 처리 : MyException [오류]
오류 처리 : MyException [오류]
오류 처리 getMessage() : 오류

toString()을 오버라이딩하거나 super로 getMessage()에 인자를 전달해 오류의 내용을 출력한다.
주로 후자를 선택한다.

profile
백엔드 개발자

0개의 댓글