본문 바로가기
Java/Java

예외) 예외 전가, 사용자 정의 예외 클래스

by 박채니 2022. 3. 29.

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


예외 전가

- 자신을 호출한 지점으로 예외 전가

- 예외를 전가하면 예외 처리의 의무를 호출한 메소드가 갖게 됨

 

예외 전가 문법 (throws)

리턴타입 메소드명(입력매개변수) throws 예외클래스명 {
	//예외 발생 코드
}

 

test1() 메소드가 스스로 예외를 처리했을 때 (checked Exception)

public void test1() {
	try {
		FileReader fr = new FileReader("test.txt");
	} catch(FileNotFoundException e) {
		e.printStackTrace();
	}
}

 

test1() 메소드가 자신을 호출한 a() 메소드에 예외를 전가했을 때

public void a() {
	try {
		test1();
	} catch(FileNotFoundException e) {
		e.printStackTrace();
	}
}
	
public void test1() throws FileNotFoundException {
	FileReader fr = new FileReader("test.txt");
}

예외 발생 메소드를 호출한 메소드에서 반드시 예외를 처리해줘야 합니다.

 

만일 상위 메소드들도 예외 처리를 하지 않고 계속 메소드를 전가한다면?

public class ThrowExceptionStudy  {
	public static void main(String[] args) throws FileNotFoundException {
		ThrowExceptionStudy study = new ThrowExceptionStudy();
		study.b();
		
		System.out.println("정상출력");
	}
	
	public void b() throws FileNotFoundException {
		a();
	}
	
	public void a() throws FileNotFoundException {
		test1();
	}
	
	public void test1() throws FileNotFoundException {
		FileReader fr = new FileReader("test.txt");
	}
}

@콘솔출력값
Exception in thread "main" java.io.FileNotFoundException: test.txt (지정된 파일을 찾을 수 없습니다)
	at java.base/java.io.FileInputStream.open0(Native Method)
	at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
	at java.base/java.io.FileReader.<init>(FileReader.java:60)
	at com.ce.java.ThrowExceptionStudy.test1(ThrowExceptionStudy.java:23)
	at com.ce.java.ThrowExceptionStudy.a(ThrowExceptionStudy.java:19)
	at com.ce.java.ThrowExceptionStudy.b(ThrowExceptionStudy.java:15)
	at com.ce.java.ThrowExceptionStudy.main(ThrowExceptionStudy.java:9)

위 처럼 메소드를 전가 받은 b()메소드 마저 main()메소드로 예외를 전가하였고, main()메소드에서도 예외를 전가하면 main()메소드를 실행한 JVM이 직접 예외처리를 하게 됩니다.

JVM의 예외 처리 방식은 발생한 예외의 정보를 화면에 출력하고 프로그램을 강제 종료 시키기 때문에 "정상출력"이 출력되지 않았고, 프로그램이 비정상종료가 되었다는 것을 알 수 있습니다.

 

다중 예외 전가

- 여러 개의 예외를 한 번에 전가할 수 있음

public void b() {
	try {
		a();
	} catch(FileNotFoundException e) {
		e.printStackTrace();
	} catch(IOException e) {
		e.printStackTrace();
	}
}
	
public void a() throws FileNotFoundException, IOException {
	test1();
}
	
public void test1() throws FileNotFoundException, IOException {
	FileReader fr = new FileReader("test.txt");
	fr.read();
}

쉼표(,)로 예외 클래스를 구분하여 여러 개의 예외를 한 번에 전가할 수 있습니다.

 

혹은 다형성을 이용해서도 예외를 전가할 수 있는데요,

다형성을 이용하여 예외 전가

public void b() {
	try {
		a();
	} catch(IOException e) {
		e.printStackTrace();
	}
}
	
public void a() throws IOException {
	test1();
}
	
public void test1() throws IOException {
	FileReader fr = new FileReader("test.txt");
	fr.read();
}

FileNotFoundException이 IOException의 자식 클래스이므로 부모 클래스인 IOException으로 예외를 전가하여 예외 처리를 할 수 있습니다.


예외 던지기를 이용한 분기 처리

- JVM에게 예외 객체를 만들어 전달

'throw 예외 객체' 형식을 이용하여 예외 객체를 던짐

 

☞ throw

public void test2() {
		try {
			checkAgeOrThrow();
			adultGame();
		} catch(RuntimeException e) {
			System.out.println("미성년자는 이용할 수 없습니다.");
		}
	}
	
	public void checkAgeOrThrow() {
		Scanner sc = new Scanner(System.in);
		
		System.out.print("> 나이를 입력하세요 : ");
		int age = sc.nextInt();
		
		if(age < 20) 
		//예외 객체 던짐        
		throw new RuntimeException();
	}
	
	public void adultGame() {
		System.out.println("게임을 시작합니다.");
	}
    
@콘솔출력값
> 나이를 입력하세요 : 19
미성년자는 이용할 수 없습니다.

> 나이를 입력하세요 : 34
게임을 시작합니다.

만일 20세 미만이라면 RuntimeException() 예외 객체를 던져 예외가 발생되게 하여 분기 처리를 하였습니다.

RuntimeException()이 발생되면 예외 객체를 처리할 catch(){} 구문을 찾아 실행하게 되기 때문에 반드시 예외를 처리하거나 예외를 전가해야 합니다.

 

☞ 사용자 정의 예외 클래스

 

① 사용자 정의 예외 클래스 작성 (두가지 방법)

 1) Exception을 상속하여 일반 예외 클래스 생성 2) RuntimeException을 상속하여 실행 예외 클래스 생성

 

저는 RuntimeException을 상속하여 예외 클래스를 생성해보았습니다.

//사용자 정의 예외 클래스
public class UnderAgeException extends RuntimeException {

	public UnderAgeException() {
		super();
	}

	public UnderAgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}

	public UnderAgeException(String message, Throwable cause) {
		super(message, cause);
	}

	public UnderAgeException(String message) {
		super(message);
	}

	public UnderAgeException(Throwable cause) {
		super(cause);
	}
}

 

② 사용자 정의 예외 객체 생성 및 예외 던지기

- 예외 객체 생성과 예외 객체 던지기는 분리하여도 사용할 수 있습니다.

	public void test2() {
		try {
			checkAgeOrThrow();
			adultGame();
		} catch(RuntimeException e) {
			System.out.println("당신의 나이는 " + e.getMessage() + "세, 미성년자입니다.");
			System.out.println("미성년자는 이용할 수 없습니다.");
		}
	}
	
	public void checkAgeOrThrow() {
		Scanner sc = new Scanner(System.in);
		
		System.out.print("> 나이를 입력하세요 : ");
		int age = sc.nextInt();
		
		if(age < 20) 
			//예외 객체 생성 및 예외 객체 던지기
			throw new UnderAgeException(String.valueOf(age));
	}
	
	public void adultGame() {
		System.out.println("게임을 시작합니다.");
	}
    
@콘솔출력값
> 나이를 입력하세요 : 14
당신의 나이는 14세, 미성년자입니다.
미성년자는 이용할 수 없습니다.

 

예외 클래스의 메소드

① getMessage() 메소드

- 예외가 발생했을 때 생성자로 넘긴 메시지를 문자열 형태로 리턴

 

객체를 생성했을 때 전달된 메시지를 리턴하는 메소드이므로 기본 생성자로 생성 후 출력하면 null값이 출력됩니다.

try {
		throw new Exception();
	} catch(Exception e) {
		System.out.println(e.getMessage());
	}
    
@콘솔출력값
null

문자열을 입력매개변수로 갖는 생성자를 이용해 객체 생성 시, 넘겨준 문자열을 리턴합니다.

try {
		throw new Exception("예외 메시지");
	} catch(Exception e) {
		System.out.println(e.getMessage());
	}
    
@콘솔출력값
예외 메시지

 

② printStackTrace() 메소드

- 예외 발생이 전달되는 경로, 예외가 전가된 과정을 한눈에 확인할 수 있는 메소드

public class ThrowExceptionStudy  {

	public static void main(String[] args) throws FileNotFoundException {
		ThrowExceptionStudy study = new ThrowExceptionStudy();		
		try {
			study.abc();
		} catch(NumberFormatException e) {
			e.printStackTrace();
		}
	}
	
	public void abc() throws NumberFormatException {
		bcd();
	}
	
	public void bcd() throws NumberFormatException {
		cde();
	}
	
	public void cde() throws NumberFormatException {
		int num = Integer.parseInt("10A");
	}
    
@콘솔출력값
java.lang.NumberFormatException: For input string: "10A"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.base/java.lang.Integer.parseInt(Integer.java:652)
	at java.base/java.lang.Integer.parseInt(Integer.java:770)
	at com.ce.java.ThrowExceptionStudy.cde(ThrowExceptionStudy.java:34)
	at com.ce.java.ThrowExceptionStudy.bcd(ThrowExceptionStudy.java:30)
	at com.ce.java.ThrowExceptionStudy.abc(ThrowExceptionStudy.java:26)
	at com.ce.java.ThrowExceptionStudy.main(ThrowExceptionStudy.java:17)

☞ 오버라이딩 적용된 예외

public class OverridingThrowsStudy {
	public static void main(String[] args) {
		Child child = new Child();
	
		child.foo();	//예외처리 없이 편리하게 호출
	}
}

class Parent {
	public void foo() throws SQLException, IOException {
		
	}
}

class Child extends Parent {
	@Override
	public void foo() {
		
	}
}

부모메소드가 던지는 예외를 제거하거나 개수를 줄일 수 있습니다.