본문 바로가기

JAVA

예외 처리 - 객지설 7~9주차

예외

디스크에서 하드웨어 에러가 발생하는 것

ex. 0으로 나눌 때 예외 발생, 배열 크기를 넘어서 계산할 때 예외 발

 

예외를 처리하는 방법

1. try-catch 구조

try {
     // 예외가 발생할 수 있는 코드
} catch(예외 클래스 변수) {
     // 예외를 처리하는 코드
} finally {
     // try 블록이 끝나면 무조건 실행되는 블
}

 

*try 블록은 생략이 불가능함

*예외 1개 당 catch 블록 1개 지정

*catch 블록이 여러 개 일 때, 매개변수는 모두 달라야 함

*catch 코드 부분은 0 ~ n개 작성 가능 

*finally 코드 부분은 0 ~ 1개 작성 가능

*finally는 무조건 실행이 됨

*try와 catch와 finally 블록은 별도의 독립된 블록임. 즉, try블록에서 정의된 변수는 catch, finally 블록에서 사용 불가. catch도 마찬가지임.

 

try-catch 예외 예시 1

// 예외 발생하지 않은 경우
// 흐름 : try -> finally
try {
	int result = 10 / 20;
}
catch(Exception e) {
	System.out.println("오류 발생");
}
finally {
	System.out.println("try/catch 통과");
}

// 예외 발생한 경우
// 흐름 : try -> catch -> finally
try {
	int result = 10 / 0;
}
catch(Exception e) {
	System.out.println("오류 발생");
}
finally {
	System.out.println("try/catch 통과");
}

 

try-catch 예외 예시 2

//finally가 없는 경우
//흐름 : try -> catch -> return(프로그램 종료 혹은 호출자로 이동)
try {
	out = new PrintWriter(...);
}
catch(IOException e) {
	return;
}
out.close;

//finally가 있는 경우
//흐름 : try -> catch -> finally -> return(프로그램 종료 혹은 호출자로 이동)
try {
	out = new PrintWriter(...);
}
catch(IOException e) {
	return;
}
out.close;

 

try-catch 예외 예시 3

// 흐름 : (1) -> (3)

try {
	int result = 10 / 0; //(1)
	System.out.println("결과는 " + result); //(2)
}
catch (Exception e) {
	System.out.println("예외 발생!"); //(3)
}

예외는 발생 즉시 catch 블록으로 이동함.

 

+) 예외의 종류

예외를 나타내는 클래스 이름 존재 (catch 블록에 적어야 하기 때문)

Error : 너무 심각한 에러라 할 수 있는 방법이 없음 -> 프로그램 종료

Runtime Exception : 프로그래밍 버스이므로 스스로 고쳐야 함 -> 예외 처리가 아닌 코드 수정으로 예외를 막음.

그 외 예외들 : 반드시 처리해야하는 예외 -> 예외 처리를 안 하면 컴파일 오류 발생

 

Runtime Exception 예외 종류 (이 외에도 더 많다)

체크 예외 종류 (이 외에도 더 많다)

ex. 사용자가 잘못된 파일 이름을 입력하면 예외가 발생하지만, 예외 처리를 통해 다시 입력하라는 메시지가 나오게끔 할 수 있음.

 

2. 예외 떠넘기기 : 예외는 폭탄과 같은 존재
- try-catch 블록이 아닌 상위 메소드(호출자)로 예외를 전달하여 처리하는 방법.

 

다음 코드는 디스크에 있는 텍스트 파일을 열어서 문자를 하나씩 읽는 코드이다.

입출력 예외가 발생하면 상위 메소드로 예외를 던져서 처리하고 있다.

//교재357 코드
import java.io.*;

public class ExceptionTest {
	
    public static void main(String args[]) throws IOException {
    	Filereader fr = new FileReader("test.txt");
        char[] a = new char[50];
        fr.read(a);
        for(char c : a)
        	System.out.print(c);
    }
}

* throws IOException이란, 입출력 예외가 발생하면 상위 메소드로 예외를 던져서 처리하겠다는 의미임.

* 만약 여러 개의 예외를 처리하고 싶다면 ,를 기준으로 사용하여도 된다. 

ex. throws 예외1, 예외2, 예외3 {...}

 

예외 떠넘기기의 흐름

//main 함수
public static void main(String[] args) {
	sub1();
}

//sub1에서는 sub2에서 오류가 나게 되면 catch 블록으로 예외를 처리함.
//try-catch라는 예외 처리기가 있는 메소드임.
public static void sub1() {
	try {
    	sub2();
    } catch (IOException e) { ... }
}

//sub2에서는 Io예외가 나면 sub1으로 예외를 떠넘김
//별도의 예외 처리기가 없는 메소드임.
public static void sub2() throws IoException {
	writeList();
}

public static void writeList() throws IoException {
	...
}

 

- 예외처리기가 없는 예외처리의 경우, 호출 스택 밑에 있는 함수에 예외를 전달하고, 계속 호출 스택을 내려가면서 예외처리가 있는 메소드를 찾음.

 

예시)

writeList()에서 오류 발생시, sub2()로 예외를 던짐.

sub2에서 오류 발생 시, sub1로 예외를 던짐.
sub1에서 오류 발생 시, 예외처리기가 있으므로 자기 함수에서 오류를 처리.

 

*만약 main에서 예외를 throws 하면, 예외는 JVM으로 던져져서 비정상적으로 프로그램이 마무리 됨.

 

예외 생성하기

억지로 예외를 발생시켜서 프로그램을 종료해야할 때, 예외 클래스 객체를 통해 해결할 수 있음.

*객체가 만들어지면 그 밑의 코드는 전부 무시되고, 자신이 호출된 곳으로 돌아감

public void main() {
	process();
}

public void process() {
    try {
    	risky();
    } catch (IOException e) {
    	//예외처리
    }
}

public void risky() throws IOException {
	if(ioError == true) {
    	     IOEception e = new IOException();
    	     throw(e); //throws와 헷갈리지 마라
              // throw (new EmptyStackException()); 로 축약 가능
         }
}

예외 생성코드는 throw(new IOException()); 으로도 축약 가능하다. 

* 만약 예외 객체로 추가 코드를 작성하고 싶다면 한 줄로 쓰면 안 됨.

 

예외 클래스

1. 정의되어 있는 메소드

getMessage() : 발생한 예외를 String으로 반환. System.out.println을 이용해야함.

*setMessage는 없다. 이에 관한 건, 밑에 사용자 정의 예외 부분을 살펴보자.

printStackTrace() : 발생한 예외를 단계별로 출력. System.out.println 필요 없음.

import java.util.Scanner;

public class Test {
    
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		System.out.print("피젯수: ");
		int x = sc.nextInt();
		System.out.print("젯수: ");
		int y = sc.nextInt();
		
		try {
			int result = x / y; //예외 발생 가능성 있음
		} catch (ArithmeticException e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		} finally {
			sc.close();
		}
	}
}

//출력 결과
피젯수: 10
젯수: 0
/ by zero
java.lang.ArithmeticException: / by zero
	at Test.main(Test.java:14)

 

2. 사용자 정의 예외

예외를 직접 정의할 수 있다. 단, Exception 클래스의 하위 클래스로만 정의가 가능하다. (extends 사용)

 

*Exception 클래스의 생성자는 2개이다.

1. Exception()

2. Exception(String message)

-> setMessage() 메소드는 없으므로 2번 생성자를 이용하여 메시지를 저장해야 한다.

 

*노랑색 경고창 삭제하는 방법

@SuppressWarnings("serial"); 사용

@SuppressWarnings("serial")
class MyException extends Exception {
    public MyException() {
        super("사용자 정의 예외"); //Exception의 생성자에 "사용자 정의 예외" 예외임을 저장
    }
}

public class Test {
    public static void method1() throws MyException {
        throw (new MyException()); //억지로 예외 발생
    }
    
	public static void main(String[] args) {
		try {
		    method1();
		} catch (MyException e){
		    System.out.println("getMessage() 내용 : " + e.getMessage()); 
		    System.out.println("printStackTrace() 내용 \n");
		    e.printStackTrace(); //MyException에서 정의된 "사용자 정의 예외" 문구를 출력
		}
	}
}

//출력 결과
getMessage() 내용 : 사용자 정의 예외
printStackTrace() 내용 

MyException: 사용자 정의 예외
	at Test.method1(Test.java:10)
	at Test.main(Test.java:15)

 

'JAVA' 카테고리의 다른 글

컬렉션/ArrayList/HashSet - 객지설 11주차  (0) 2024.05.23
제네릭 - 객지설 10주차  (0) 2024.05.17
인터페이스 - 6주차  (0) 2024.05.05
다형성 - 객지설 5주차  (0) 2024.05.04
랩퍼 클래스 - 객지설 4주차  (0) 2024.05.03