Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

forDevLife

[생활코딩-자바입문] 핵심 요약 3 본문

Java

[생활코딩-자바입문] 핵심 요약 3

JH_Lucid 2020. 11. 8. 21:56

생활코딩 38. 예외 던지기

 

1) 예외의 강제

 

좀 헷갈리지만, 예외처리 방식에 대해서 알아보자.

아래는 java에서 추천해주는 try - catch 방식으로 예외처리를 진행하기 위한 방식이다.

bReader 부분에서는 FileNotFoundException 에러가 발생, input 부분에서는 IOException 에러가 발생했고, 이 부분을 앞에서 배운 try-catch로 처리할 것이다. 

 

에러 발생 시, 해당 class를 더블클릭 > f2를 누르면 예외처리 방법이 나오고, 방식을 누른 후에 다시 f2를 누르면 예외에 대한 설명 나온다.

아래코드를 실행하면 예외 처리로 인하여 e.printStackTrace 부분이 출력된다.

import java.io.*;

public class CheckExceptionDemo {

	public static void main(String[] args) {
		BufferedReader bReader = null;
		try {
			bReader = new BufferedReader(new FileReader("out.txt"));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String input = null;
		try {
			input = bReader.readLine();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(input);
	}
}

뭐가 잘 안됐다는 식으로 나오는 듯,,

 

2) 예외 throws

 

throws를 통해서, 예외 처리 대상을 변경할 수 있다. 정확히 말하면 내가 하기 싫은 걸 다른 class에게 전달하는 것이다.

아니 싫은건 아니고, 해당 클래스에서 처리가 불가한 부분이어서 그렇다고 치자.

 

아래는, B를 C가 상속받고, 최종 사용자는 ThrowExceptionDemo 클래스이다.(최초 생산자는 B 클래스)

B에서 throws 키워드를 통해 자기가 처리하기 싫은 예외 두 개를 C로 보낸다.

C도 마찬가지로 throws를 통해 예외 두 개를 ThrowExceptionDemo로 보냈다.

그럼 최종적으로 ThrowExceptionDemo에서는 무조건 이 예외를 처리해야 한다. 

 

println으로 최종 예외를 처리했는데, 이렇게 해야 실 사용자가 와닿게 뭐가 오류인지 알 수 있다.

 

+ 아래에서 최종적으로 IOException만 처리하는데, IOException이 FileNotFoundException을 포함하기 때문이다.

+ 또한 bReader 부분(class B의 run)에서 노란 줄이 뜨는데, close를 안써서 그렇다는데 이건 일단 넘어갑시다.

package org.opentutorials.exception;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

class B {
	void run() throws FileNotFoundException, IOException {
		BufferedReader bReader = null;
		String input = null;
		bReader = new BufferedReader(new FileReader("out.txt")); // 이 부분 메모리 누수?
		input = bReader.readLine();
		System.out.println(input);
	}
}

class C {
	void run() throws FileNotFoundException, IOException {
		B b = new B();
		b.run();
	}
}

public class ThrowExceptionDemo {

	public static void main(String[] args) {
		C c = new C();
		try {
			c.run();
		} catch (IOException e) {
			System.out.println("파일이 없으니 확인 바람");
		}
	}
}

 

3) 예외 만들어 던지기~

 

예외 만들어 던지는 방법에 대해 알아보자.

아래서, new를 통해 ArithmeticException을 생성하였다. 이로 인해 ArithmeticException이 발생하고 실행이 중지된다.

 

만약 main에서 try - catch로 실행하게 되면, 아래 catch의 인자로 전달되어, e.getMessage()로 실행이 된다

차이는 아래의 결과를 보자.

class Calculator {
	int left, right;
	
	public void SetOperands(int left, int right) {
		this.left = left;
		this.right = right;
	}
	
	public void divide() {
		if (this.right == 0) {
			throw new ArithmeticException("두 번째 인자의 값은 0이 될 수 없다.");
		}
		try {
			System.out.print("계산 결과는 ");
			System.out.print(this.left / this.right);
			System.out.print(" 입니다.");
		} catch (Exception e) {
			System.out.println("\n\ne.getMessage()\n"+e.getMessage());
			System.out.println("\n\ne.toString()\n"+e.toString());
			System.out.println("\n\ne.printStackTrace()");
			e.printStackTrace();
			
		}
	}
}

public class CalculatorDemo {

	public static void main(String[] args) {
		Calculator c1 = new Calculator();
		c1.SetOperands(10, 0);
		try {
			c1.divide();
		} catch (ArithmeticException e) {
			System.out.println(e.getMessage());
		}
		//c1.divide();
	}
}

try ~ catch로 인한 출력

 

c1.divide()만 했을 경우 출력

 

우리가 직접 예외를 발생시켜야 하는 경우에는 IllegalArgumentException을 사용한다.

 

 

4) Throwable class

 

Exception이 Throwable class를 상속하는 클래스라, 아래의 method를 사용이 가능한 것이였다.

따라서 예외로 던질 수 있는 클래스들은 반드시 throwable을 상속 받아야 한다. (모든 예외클래스의 부모)

 

 

throwable의 상속관계

 

- Throwable : 모든 예외 클래스의 부모

- Error : 건들 필요 x, 기반 시스템의 문제이기 때문에 자바 애플리케이션 개발자의 입장에선, virtual machine의 오류이기 때문에 할 수 있는게 없다. try / catch 할 필요 없음(메모리 문제 등)

- Exception 

   -. IOException : 부모가 Exception임 → checked exception : 반드시 try ~ catch를 사용하여 예외를 처리해야 함

                                                  → 또는 상위로 throws 하여 예외를 처리하던지 해야 함                

   -. RuntimeException

      -.ArithmeticException : (직)부모가 RuntimeException임 → unchecked exception : 처리할 필요 없음

 

 

아래에서 보면, IOException을 위해서, divide( ) 정의 부분에 throws로 IOException을 main에서 처리하도록 강제하였다.

저 부분을 안써준다면 오류가 발생한다.

import java.io.IOException;

class Calculator {
	int left, right;
	
	public void SetOperands(int left, int right) {
		this.left = left;
		this.right = right;
	}
	
	public void divide() throws IOException{
		if (this.right == 0) {
			throw new IOException("IOException은 메인에서 처리된다.");
		}
		try {
			System.out.print("계산 결과는 ");
			System.out.print(this.left / this.right);
			System.out.print(" 입니다.");
		} catch (Exception e) {
			System.out.println("\n\ne.getMessage()\n"+e.getMessage());
			System.out.println("\n\ne.toString()\n"+e.toString());
			System.out.println("\n\ne.printStackTrace()");
			e.printStackTrace();
			
		}
	}
}

public class CalculatorDemo {

	public static void main(String[] args) {
		Calculator c1 = new Calculator();
		c1.SetOperands(10, 0);
		try {
			c1.divide();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		//c1.divide();
	}
}

 

 

아래처럼 IOException에 대한 부분을, divide 내에 try - catch로 처리했다. 이 경우에는, 예외가 처리되고나서 아래 계산결과는 ~ 부분의 예외도 처리가 된다. main에서 IOException을 처리하도록 하면(위 처럼 throws를 썼을 경우), 예외 처리 후 종료가 된다.

	public void divide() {
		if (this.right == 0) {
			try {
				throw new IOException("처리해도 안 끝나네");
			} catch (IOException e) {
				System.out.println(e.getMessage());
			}
			try {
				System.out.print("계산 결과는 ");
				System.out.print(this.left / this.right);
				System.out.print(" 입니다.");
			} catch (Exception e) {
				System.out.println("\n\ne.getMessage()\n"+e.getMessage());
				System.out.println("\n\ne.toString()\n"+e.toString());
				System.out.println("\n\ne.printStackTrace()");
				e.printStackTrace();
			}
		}
	}

 

5) 사용자 정의 예외

 

아래는 비검사항목인 RuntimeException을 상속한 사용자 정의 예외이다. DivideException이라는 놈이 얘를 상속한다.

아래 사용자정의 메세지를 출력하기 위해 main에서 try - catch를 사용했지만 안해도 무방하다. 비검사항목이기 때문.

 

클래스 선언에서, string message를 인자로 받는 생성자를 하나 더 정의했는데, 이는 main에서 getMessage( )메소드를 호출했을 때 반환하는 값을 부여하기 위함이다. 

작성하지 않으면, DivideException이 string을 못받는다고 오류가 발생한다.

 

+ 궁금한것은, RuntimeException에도 string을 받을 수 있는 생성자가 있을텐데 상속받는 입장에서 이 부분은 상속을 못받는건가?

 

클래스에서 기본 생성자(인자 없는) 외 전달 인자가 있는 생성자를 정의할 경우, 기본 생성자는 생성되지 않으므로 추가로 직접 정의가 필요하다가 핵심인 듯 하다.

1) RuntimeException에는 아마 기본 / string 생성자가 둘 다 존재할 것이다. super로 call 되는 것을 확인하자.

2) DivdeException에 아무것도 정의하지 않을 경우(생성자 아무것도 정의하지 않고 상속만 할 경우), 자바는 자동으로 기본 생성자를 생성할 것이다. 이는 인자가 없는 것을 생성하는 기본 생성자가 자동으로 생성되는 것으로 보인다. 따라서 throw new DivideException( )처럼 아무 인자가 없을 경우에는 에러가 없다.

3) throw new DivideException에 string이 전달될 경우, 해당 생성자가 없으므로 에러가 발생한다.

따라서 DivideException(String message) { super ~) 를 작성해준다.

4) 이 경우, 인자가 있는 생성자가 DivideException의 기본 생성자가 된다. 사용자가 정의했으므로 자바는 인자 없는 생성자를 만들어 주지 않는다.

5) 따라서 throw new DivideException( ) 처럼 인자 없이 호출되는 생성자는 에러가 난다. 그러므로 이것도 정의해줘야 한다.

6) 결론적으로, 인자 있는 생성자와 없는 생성자를 둘 다 받기 위해서 각각 정의가 필요하다.

class DivideException extends RuntimeException {
	DivideException() {
		super();
	}
	DivideException(String message) {
		super(message);
	}
}

class Calculator1 {
	int left, right;
	
	public void setOperands(int left, int right) {
		this.left = left;
		this.right = right;
	}
	
	public void divide() {
		if (this.right == 0) {
			throw new DivideException("0으로 나누는건 바람직하지 않다.");
		}
		System.out.println(this.left/this.right);
	}
}
public class CalculatorDemo2 {

	public static void main(String[] args) {
		Calculator1 c1 = new Calculator1();
		c1.setOperands(10, 0);
		try {
			c1.divide();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
}
}

 

 

마지막으로, 당연하게 Exception을 상속한다면 try ~ catch로 예외처리는 필수이다.

아래에서는 main으로 throws 하여 처리를 하였고, 또는 divide 메소드 내의 if ~ sysout을 try로 묶고, catch하여 처리도 가능하다.

class DivideException extends Exception {
	DivideException() {
		super();
	}
	DivideException(String message) {
		super(message);
	}
}

class Calculator1 {
	int left, right;
	
	public void setOperands(int left, int right) {
		this.left = left;
		this.right = right;
	}
	
	public void divide() throws DivideException {
		if (this.right == 0) {
			throw new DivideException("0으로 나누는건 허용되지 않는다.");
		}
		System.out.println(this.left/this.right);
	}
}
public class CalculatorDemo2 {

	public static void main(String[] args) {
		Calculator1 c1 = new Calculator1();
		c1.setOperands(10, 0);
		try {
			c1.divide();
		} catch(DivideException e) {
			System.out.println(e.getMessage());
		}
		
}
}
Comments