Language/Java

예외 처리

JUNGKEUNG 2021. 9. 27. 23:30
반응형

프로그램 오류


프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료되는 경우가 있다.

이러한 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 한다. 이를 발생시점에 따라 컴파일 에러와 런타임 에러로 나눌 수 있는데 컴파일 할 때 발생하는 에러를 컴파일 에러이고, 프로그램의 실행도중에 발생하는 에러를 런타임 에러라고 한다. 

 이 외에도 논리적 에러가 있는데, 컴파일도 잘되고 실행도 잘되지만 의도한 것과 다르게 동작하는 것을 말한다

 

  • 컴파일 에러 : 컴파일 시에 발생하는 에러
  • 런타임 에러 : 실행 시에 발생하는 에러
  • 논리적 에러 : 실행은 되지만, 의도와 다르게 동작하는 것

 

소스코드를 컴파일 하면 컴파일러가 소스코드에 대해 오타나 잘못된 구문, 자료형 체크 등의 기본적인 검사를 수행하여 오류가 있는지를 알려준다

그러나 컴파일러가 소스코드의 기본적인 사항은 컴파일 시에 모두 걸러줄 수 있지만, 실행도중에 발생할 수 있는 잠재적인 오류까지 검사할 수 없기 때문에 컴파일은 잘되었어도 실행 중에 에러에 의해서 잘못된 결과를 얻거나 프로그램이 비정상적으로 종료될 수 있다

예를 들어 프로그램이 실행 중 동작을 멈춘 상태로 오랜 시간 지속되거나, 갑자기 프로그램이 실행을 멈추고 종료되는 경우 등이 이에 해당되는데 런타임 에러를 방지하기 위해서는 프로그램의 실행도중 발생할 수 있는 모든 경우의 수를 고려하여 이에 대한 대비를 하는 것이 필요하다.

자바에서는 실행 시 발생할 수 있는 프로그램 오류를 에러와 예외 두 가지로 구분 할수 있다.

참고 : 자바의 정석

 

 

 

예외 클래스의 계층구조


자베에서는 실행 시 발생할 수 있는 오류(Exception과 Error)를 클래스로 정의

앞서 배운 것처럼 모든 클래스의 조상은 Object 클래스이므로 Exception과 Error 클래스 역시 Object 클래스의 자손이다.

참고자료 : 자바의 정석

모든 예외의 최고 조상은 Exception 클래스이며, 상속계층도를 Exception 클래스로부터 도식화하면 다음과 같다

참고 자료 : 자바의 정석

<Exception 클래스들> : 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외

ex) 존재하지 않는 파일의 이름을 입력(FileNotFoundException),

실수로 클래스의 이름을 잘못 적음(ClassNotFoundException),

입력한 데이터 형식이 잘못됨(DataFormatException)

 

 

 

예외처리하기 - try-catch 문


프로그램의 실행도중에 발생하는 에러는 어쩔 수 없지만, 예외는 프로그래머가 이에 대한 처리를 미리 해주어야 합니다.예외처리란, 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것이며,예외처리의 목적은 예외의 발생으로 인한 실행 중인 프로그램의 갑작스런 비정상 종료를 막고,정상적인 실행상태를 유지할 수 있도록 하는 것입니다.발생한 예외를 처리하지 못하면, 프로그램은 비정상적으로 종료되며,처리되지 못한 예외는 JVM의 예외처리기가 받아서 예외의 원인을 화면에 출력합니다.예외를 처리하기 위해서는 try-catch문을 사용하며, 그 구조는 다음과 같습니다.

try{//예외가 발생할 가능성이 있는 문장들을 넣는다.
}catch(Exception1 e1){//Exception1이 발생했을 경우, 이를 처리하기 위한 문장들을 적는다.
}catch(Exception2 e2){//Exception2가 발생했을 경우, 이를 처리하기 위한 적는다.
}...

이와 같이 하나의 try블럭 다음에는 여러 종류의 예외를 처리할 수 있도록 하나 이상의 catch 블럭이 올 수 있으며,이 중 발생한 예외의 종류와 일치하는 단 한 개의 catch 블럭만 수행됩니다.발생한 예외의 종류와 일치하는 catch 블럭이 없으면 예외는 처리되지 않습니다.또 if문과 달리, try블럭이나 catch 블럭 내에 포함된 문장이 하나뿐이어도 괄호 {}를 생략할 수 없습니다.

class ExceptionEx{
	public static void main(String[] args) {
		int num = 100;
		int result = 0;

		for ( int i = 0; i < 10; i++){
			result = num/(int)(Math.random()*10);
			System.out.println(result);
		}
}

 

 

 

try-catch문에서의 흐름


try-catch 문에서, 예외가 발생한 경우와 발생하지 않았을 때의 흐름이 달라진다.

<try 블럭 내에서 예외가 발생한 경우>

1.발생한 예외와 일치하는 catch 블럭이 있는지 확인한다.

2.일치하는 catch블럭을 찾게 되면, 그 catch 블럭 내의 문장을 수행하고 전체 try-catch 문을 빠져나간다.`

<try 블럭 내에서 예외가 발생하지 않은 경우>

3.catch 블럭을 거치지 않고 전체 try-catch 문을 빠져나가서 수행을 계속한다.

class ExceptionEx{
	public static void main(String[] args){
		System.out.println(1);
		System.out.println(2);
		try {
				System.out.println(3);
				System.out.println(3/0);  //예외처리
				System.out.println(4);   //실행안됨
		}catch(ArithmeticException ae) {
				System.out.println(5);
		}
	}
}

try 블럭에서 예외가 발생하면, 예외가 발생한 위치 이후에 있는 try블럭의 문장들은 수행되지 않으므로, try 블럭에 포함시킬 코드의 범위를 잘 선택해야합니다.

 

 

 

메서드 예외 선언하기


메서드 예외를 선언하려면, 메서드 선언부에 throws키워드를 사용해 메서드 내에 발생할 수 있는 예외를 작성, 예외가 여러개일 경우 ( , ) 로 구분한다.

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

모든 예외 최고 조상인 Exception클래스를 메서드에 선언하면, 모든 종류의 예외가 발생할 가능성이 존재한다.

  • 예외를 선언하면, 이 예외 뿐만아니라 그 자손타입의 예외까지도 발생할 수 있음
  • 오버라이딩 할 때는 단순히 선언된 예외의 개수가 아니라 상속관계까지도 고려해야함
  • public static void main(String[] args) throws Exception {
    	method1();
    }
    
    static void method1() throws Exception {
    	method2();
    }
    static void method2() throws Exception {
    	throw new Exception();
    }
  • 예외가 발생했을 때, 3개의 모든 메서드가 호출스택에 있음
  • 예외가 발생한 곳은 제일 윗줄에 있는 method2()라는것
  • main()가 method1()을, method1()은 method2()를 호출했단느 것을 알 수 있음
  • 예외처리를 해주지 않았기 때문에 호출한 메서드에 계쇡 예외를 전달하고 main()이 종료된면서 프로그램이 비정상적으로 처리됨
public static void main(String[] args){
	method1();
}

static void method1(){
	try{
		throw new Exception();
	} chatch(Exception e){
		System.out.println("method1()에서 예외처리");
		e.printStackTrace();
	}
}
  • method1()에서 예외가 처리되면 main()에서는 예외가 발생했다는 것을 알지 못함
public static void main(String[] args){
	String fileName = " ";
	File file = createFile(fileName);
	System.out.println(file.getName() + "파일이 성공적으로 생성됨.");
}

static File createFile(String fileName){
	try{
		if(fileName ==null || fileName.equals(""))
			throw new Exception("파일이름이 유효하지 않음.");
	} catch (Exception e) {
		fileName = "제목없음.txt";
	} finally {
		File file = new File(FileName);
		createNewFile(file);
		return file;
	}
}

static void createNewFile(File file){
		try{
			file.createNewFile();
		} catch (Exception e) {
	}
}
  • 예외가 발생한 메서드에 직접 예외를 처리
  • createFile()를 호출한 main()은 예외가 발생한 사실을 모름
  • 파일명이 없거나 공백일 경우, 파일명을 제목없음.txt로 설정하여 파일을 생성
  • finally블럭에서는 예외의 발생 여부와 관계 없이 파일을 생성하도록 처리

 

 

 

메서드 예외의 처리 방법


  • 예외가 발생할 경우 메서드 내에서 자체적으로 처리해도 되는 것은 메서드 내에서 try-catch문을 사용해 처리
  • 메서드에 호출한 값을 다시 넘겨받아야 할 경우(메서드 내에서 자체적으로 해결이 안될 경우) 예외를 메서드에 선언해서 호출한 메서드에서 처리

 

 

 

finally 블럭


  • finally블럭은 예외발생여부와 관계 없이 수행되어야 하는 코드를 넣는다
  • try블럭 안에 return문이 있어서 try블럭을 벗어나갈 때도 finally블럭이 실행된다
try{
		// 예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch (Exception1 e1) {
		// 예외처리를 위한 문장을 적는다
} finally {
		//예외의 발생여부에 관계없이 항상 수행되어야하는 문장들을넣는다
		// finally블럭은 try-catch문의 맨 마지막에 위치해야한다
}

 

 

 

사용자 정의 예외 만들기


  • 직접 예외 클래스를 정의할 수 있다
  • 조산은 Exception 과 RuntimeException중에서 선택
class MyException extends Exception {
	MyException(String msg) { //문자열을 매개변수로 받는 생성자
			super(msg); // 조상인 Exception클래스의 생성자를 호출한다
	}
}

 

 

 

예외 던지기 (exception re-throwing)


  • 예외를 처리한 후에 다시 예외를 발생시키는 것
  • 호출한 메서드와 호출된 메서드 양쪽 모두에서 예외처리하는 것
calss Ex8_12 {
	public static void main(Stirng[] args) {
		try{
				method1();
		} catch(Exception e) {
				System.out.println("main메서드에서 예외가 처리되었습니다.");
		}
	} //main 메서드의 끝

	static void method1() throws Exception {
		try {
			throw new Exception();
		} catch (Exception e) {  // 예외발생
			System.out.println("method1메서드에서 예외가 처리되었습니다.");
			throw e;
		}    // 다시 예외를 발생시킨다
	}   // method1메서드의 끝
}

 

 

 

연결된 예외(chained exception)


  • 한 예외가 다른 에외를 발생시킬 수 있다.
  • 예외 A가 예외 B를 발생시키면, A는 B의 원인 예외(cause exception)
  • Throwable initCause(Throwable cause) // 지정한 예외를 원인 예외로 등록 Throwable getCause() // 원인 예외를 반환
## 연결된 예외(chained exception)

- 한 예외가 다른 에외를 발생시킬 수 있다.
- 예외 A가 예외 B를 발생시키면, A는 B의 원인 예외(cause exception)
- Throwable initCause(Throwable cause) // 지정한 예외를 원인 예외로 등록
 Throwable getCause()                           // 원인 예외를 반환

 

 

 

참고 자료


자바의 정석

'Language > Java' 카테고리의 다른 글

StringBuffer클래스  (0) 2021.10.03
java.lang패키지와 유용한 클래스  (0) 2021.10.03
인터페이스  (0) 2021.09.26
내부 클래스  (0) 2021.09.25
싱글톤  (0) 2021.09.19