chapter 8 예외처리

byeol·2022년 8월 20일
0

1. 예외처리

1.1 프로그램 오류

1.2 예외 클래스의 계층 구조

✔️ 사용자 실수와 같은 외적인 요인에 의해서 발생하는 예외
존재하지 않는 파일 이름을 입력했다던가 (FileNotFoundException)
실수로 클래스 이름을 잘못 적었다던가(ClassNotFoundException)
입력한 데이터 형식이 잘못된 경우(DataFormatException)

✔️ 주로 프로그래머의 실수에 의해서 발생할 수 있는 예외
배열의 범위를 벗어난다던가 (ArrayIndexOutOfBoundsException)
값이 null인 참조변수의 멤버를 호출하려 했다던가 (NullPointerException)
클래스간의 형변환을 잘못했다던가 (ClassCastException)
정수를 0으로 나누려 했다던가(ArithmeticException)

1.3 예외처리하기 - try-catch문

예외처리란?
프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것, 목적은 프로그램의 비정상적인 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 하는 것!

예외를 처리하기 위해서

try {
//예외가 발생할 가능성이 잇는 문장들을 넣는다.
try{} catch(Exception1 e) {}
//try안에 다시 try -catch문이 들어오는 것도 가능하다.
} catch(Exception1 e1) {
// Exception1이 발생했을 경우 이를 처리하기 위한 문장
//catch안에 다시 try-catch문이 들어갈 수 있다.
try {} catch(Exception1 e2) {}
//내용이 없으면 {} 빈 괄호
// catch블러 내 참조변수 e2는 e1와 이름이 겹치지 않도록
} catch (Exception2 e1) {} // catch블럭 내 참조변수는 지역변수라 생각하기

1.4 try-catch문에서의 흐름

▶ try 블럭 내에서 예외가 발생하는 경우
1. 발생한 예외와 일치하는 catch블럭 있는지 확인
2. 있는 경우 그 catch블럭 내에 있는 문장들을 수행하고 try-catch문 빠져나가서 그 다음 문장 수행 , 만일 일치하는 catch문이 없는 경우는 예외는 처리 되지 못한다.
▶ try 블럭 내에서 예외가 발생하지 않는 경우
1. catch블럭을 거치지 않고 try-catch문 빠져나가기

class ExceptionEx5 {
	public static void main(String[] args) {
		System.out.println(1);
		System.out.println(2);
		try {
			System.out.println(3);
			System.out.println(0/0);
			System.out.println(4);//실행되지 않음
		}catch(ArithmeticException ae) {
			System.out.println(5);
		}
		System.out.println(6);

	}

}

1.5 예외의 발생과 catch블럭

모든 예외 클래스는 Exception 클래스의 자손이므로 catch블럭의
괄호안데 Exception e넣으면 모든 종류의 에러가 발생하더라고 이 catch블럭에서 처리된다.

class ExceptionEx5 {
	public static void main(String[] args) {
		System.out.println(1);
		System.out.println(2);
		try {
			System.out.println(3);
			System.out.println(0/0);
			System.out.println(4);//실행되지 않음
		}catch(Exception ae) {
			System.out.println(5);
		}
		System.out.println(6);

	}

}

class ExceptionEx5 {
	public static void main(String[] args) {
		System.out.println(1);
		System.out.println(2);
		try {
			System.out.println(3);
			System.out.println(0/0);
			System.out.println(4);//실행되지 않음
		}catch(ArithmeticException ae) {
			if(ae instanceof ArithmeticException )
				System.out.println("true");
			System.out.println(5);
		}catch (Exception ae) {
			System.out.println("Exception");
		}
		System.out.println(6);

	}

}


👆 위 예시에서 ArithmeticException이 발생하여 첫번째 catch문이 실행되었기 때문에 두번째 catch문은 실행되지 않고 try-catch문을 빠져나오게 된다. 그러나 만약에 ArithmeticException이 아니 다른 Exception이었다면 첫번째 catch문이 아닌 두번째 catch문을 실행한 후 try-catch문을 빠져나왔을 것이다.

printStackTrace()와 getMessage()

예외가 발생했을 때 생성되는 예외클래스의 인스턴스에 담겨져 있는 정보들은
getMessage()와 printStackTrac()를 통해서 접근할 수 있다.
이 메서드는 catch블럭 괄호 안에 있는 선언된 참조변수를 통해서 이 인스턴스에 접근할 수 있다.

▶ printStackTrace()
예외발생 당시의 호출스텍에 있었던 메서드의 정보와 예외메시지를 화면에 출력한다.
▶ getMessage()
발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.

class ExceptionEX8 {
	public static void main(String[] args) {
		System.out.println(1);
		System.out.println(2);
		try {
			System.out.println(3);
			System.out.println(0/0);
			System.out.println(4);
		}catch(ArithmeticException e) {
			e.printStackTrace();
			System.out.println("예외메세지: "+e.getMessage());
		}
		System.out.println(6);
	}
}

멀티 catch블럭

여러 catch블럭을 '|'기호를 이용해서 , 하나의 catch블럭으로 합칠 수 있다.

try{
 } catch (ExceptionA e) {
   e.printStackTrace();
 } catch (ExceptionB e2) {
   e2.printStackTrace();
 }

를 멀티 catch블럭으로 바꾸기

try{
 } catch (ExceptionA | ExceptionB e) {
   e.printStackTrace();
 } 

근데 만약에 ExceptionA(Parent)와 ExceptionB(Child)사이 관계가 상속관계라면
컴파일 에러가 발생한다.
당연한 말이다. 그냥 부모 클래스를 적는거랑 같은 말이기 때문이다.

멀티 catch는 하나의 catch블럭으로 여러가지 에외를 처리하기 때문에 catch블럭은 어떤 예외가 발생했는지 알 수 없다. 따라서 catch블럭 ()에 선언된 참조변수로 사용할 수 있는 멤버는 블럭 내 선언된 에외들의 조상클래스에 있는 멤버만 사용할 수 있다. 그렇지만 어떤 예외가 발생했는지 확인하는 intanceof로 호출하여 사용하였다면 그 예외에 있는 멤버를 사용할 수 있다.

또한 멀티 catch블럭에 선언된 참조변수 e는 상수이기 때문에 값을 변경할 수 있다.

1.6 예외 발생시키기

키워드 throw를 사용하여 프로그래머가 고의로 예외를 발생시킬 수 있다.

  1. 먼저, 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 다음
    Exception e = new Exception("고의로 발생시켰음");
  2. 키워드 throw를 이용해서 예뢰를 발생시킨다
    throw e;
class ExceptionEx9 {
	public static void main(String[] args) {
		try {
			Exception e = new Exception("고의로 발생시켰음");
			throw e;
		} catch(Exception e) {
			System.out.println("에러 메시지: "+e.getMessage());
			e.printStackTrace();
		}
		System.out.println("프로그램이 정상 종료되었음.");
	}
	

}

class ExceptionEx9 {
	public static void main(String[] args) {
		throw new Exception();
	}
}


👆 일부러 발생시킨 에러를 해결하지 않으면 이렇게 컴파일조차 되지 않는다.

class ExceptionEx9 {
	public static void main(String[] args) {
		throw new RuntimeException();
	}
}


👆 일부러 RuntimeException()을 발생시키고 예외처리를 하지 않는 상황이다.
하지만 컴파일 에러는 발생하지 않고 실행하면 위의 실행결과처럼 RuntimeException이 발생하여 비정상정으로 종료된다.

중요한 것은 컴파일은 성공적으로 되었다는 것!
앞서 배웠지만 RuntimeException와 그 자손은 프로그래머가 실수로 발생시키는 것들이기 때문에 예외처리를 강제하지 않는다.
만약에 예외처리를 강제했다면
RuntimeException이 속하는 예외가 발생할 가능성이 있는 코드 모든 곳 예를 들어 배열이 있는 것이다 참조변수가 사용되는 곳 모든 곳에 예외처리를 해줘야했을 것이다.

컴파일러가 예외처리를 확인하지 않는 RuntimeException클래스들은 unchecked예외라고 부르고, 예외처리를 확인하는 Exception클래스들은 checked예외라고 부른다.

1.7 메서드에 예외 선언하기

예외를 처리하는 방법
① try-catch문을 사용하는 것
② 예외를 메서드에 선언하는 것
두번째 예외를 메서드에 선언하려면
메서드의 선언부에 throws를 사용해서 메서드 내에서 발생할 수 있는 예외를 적어주기만 하면 된다. 예외가 여러개인 경우네는 쉼표로 구분한다.

void method() throws Exception1, Exception2, ... ExceptionN {
//메서드의 내용
}

일부러 예외를 발생시키는 throw와 메서드의 선언부에 예외를 선언하는 throws를 잘 구분하자.

만일 선언부애 모든 예외의 최고 조상인 Exception클래스를 메서드에 선언하면
이 메서드의 모든 종류의 예외가 발생할 가능성이 있다는 뜻
이 말은 이 클래스의 자손타입의 예외까지도 발생할 수 있다는 것을 의미한다.

메서드의 선언부에 예외를 선언한다는 것
= 이 메서드를 사용하기 위해서는 이 예외가 발생할 가능성이 있으니 어디선가는 처리를 해줘야 한다를 의미한다.


이는 Java API문서에 있는 Object클래스의 wait 메서드인데
선언부에 throws InterruptedException이 발생하니 이 메서드를 호출하고자 하는 메서드에서는 이 예외를 처리해줄 것을 말하고 있다.


wait메서드의 Throws를 보면 이 메서드에 발생하는 3종류의 예외를 볼 수 있는데
메서드 선언부에는 InterruptedException만 선언되어져 있다.

그 이유는 InterruptedException은 Exception의 자손이므로 무조건 예외처리를 해줘야(cheched예외) 하지만 나머지 2개는 RuntimeException클래스의 자손이므로 예외처리를 해주지 않아도 된다.(unchecked예외)

profile
꾸준하게 Ready, Set, Go!

0개의 댓글

관련 채용 정보