자바의 정석 <기초편> 📌chapter8. 예외처리

모깅·2023년 2월 6일
0

📖 01. 프로그램 오류


📖 02. 예외 클래스의 계층구조 && 03. Exception과 RuntimeException


📖 04. 예외 처리하기 - try-catch문


📖 05. try-catch문에서의 흐름

<예제 8-1 >

✍️ 입력

class Ex8_1 {
	public static void main(String args[]) {
			System.out.println(1);			
			try {
				System.out.println(2);
				System.out.println(3);
			} catch (Exception e)    {
				System.out.println(4);  // 실행되지 않는다.
			} // try-catch의 끝
			System.out.println(5);
	}
}

💻 출력
1
2
3
5

<예제 8-2 >

✍️ 입력

class Ex8_2 {
	public static void main(String args[]) {
			System.out.println(1);
			try {
				System.out.println(0/0);
				System.out.println(2); 	// 실행되지 않는다.
			} catch (ArithmeticException ae)	{
				System.out.println(3);
			}	// try-catch의 끝
			System.out.println(4);
	}	// main메서드의 끝
}

💻 출력
1
3
4


📖 06. 예외의 발생과 catch블럭

<예제 8-3 >

✍️ 입력

class Ex8_3 {
	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 e){     // ArithmeticException대신 Exception을 사용.
			System.out.println(5);
		}	// try-catch의 끝

		System.out.println(6);
	}	// main메서드의 끝
}

💻 출력
1
2
3
5
6


📖 07. printStackTrace()와 getMessage()

<예제 8-5 >

✍️ 입력

class Ex8_5 {
	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)	{
			ae.printStackTrace();
			System.out.println("예외메시지 : " + ae.getMessage());
		}	// try-catch의 끝

		System.out.println(6);
	}	// main메서드의 끝
}

💻 출력
1
2
3
java.lang.ArithmeticException: / by zero
예외메시지 : / by zero
6
at Ex8_5.main(Ex8_5.java:8)

-> 예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨 있으며 예외 메서드를 통해 정보드를 얻을 수 있다.

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

📖 08. 멀티 catch블럭

  • JDK 1.7부터 여러 catch블럭을 '|'으로 합칠 수 있게 되었다.

  • 두 예외 클래스가 조상과 자손의 관계에 있다면, 그냥 조상 클래스만 써주는 것과 똑같기 때문에 에러발생한다.
    -> 예외객체의 메서드는 조상의 메서드만 사용 가능하다(공통부분만 사용가능하다) 만약, 자식의 메서드를 사용하고 싶다면 if-else문과 instanceof로 확인 후 형변환 해서 자식의 메서드를 호출한다.

    try{
       ...
       } catch (ExceptionA | ExceptionB e)
            e.methodA();  //에러. ExceptionA 선언된 methodA()는 호출불가
            
            if(e instanceof ExceptionA) {
                 ExceptionA e1 = (ExceptionA)e;
                 e1.methodA();
            } else { // if (e instanceof ExceptionB)
                 ...

    -> 예외 A, B가 상관없는 관계일 때 다음과 같이 해결 할 수도 있다.


📖 09. 예외 발생시키기

<예제 8-6 >

✍️ 입력

class Ex8_6 {
	public static void main(String args[]) {
		try {
			Exception e = new Exception("고의로 발생시켰음.");
			throw e;	 // 예외를 발생시킴
		//  throw new Exception("고의로 발생시켰음.");

		} catch (Exception e)	{
			System.out.println("에러 메시지 : " + e.getMessage());
			e.printStackTrace();
		}
		System.out.println("프로그램이 정상 종료되었음.");
	}
}

💻 출력
에러 메시지 : 고의로 발생시켰음.
java.lang.Exception: 고의로 발생시켰음.
프로그램이 정상 종료되었음.
at Ex8_6.main(Ex8_6.java:4)

-> Exception인스턴스를 생성할 때, 생성자에 String을 넣어 주면, 이 String이 Exception인스턴스에 메시지로 저장된다. 이는 getMessage()를 이용해 얻을 수 있다.


📖 10. checked예외, unchecked예외

<예제 8-7 >

✍️ 입력

class Ex8_7 {
	public static void main(String[] args) {
		throw new Exception();		// Exception을 고의로 발생시킨다.
	}
}

💻 출력
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unhandled exception type Exception

at Ex8_7.main(Ex8_7.java:3)

-> 컴파일 하면, 위와 같은 에러가 발생하며 컴파일이 완료되지 않는다. 예외처리가 되어 있지 않다는 에러이다.
-> 'Exception클래스와 그 자손들(checked예외)'가 발생 할 경우 예외처리를 해주지 않으면 컴파일조차 되지 않는다.

<예제 8-8 >

✍️ 입력

class Ex8_8 {
	public static void main(String[] args) {
		throw new RuntimeException();	// RuntimeException을 고의로 발생시킨다.
	}
}

💻 출력
Exception in thread "main" java.lang.RuntimeException
at Ex8_8.main(Ex8_8.java:3)

-> 성공적으로 컴파일은 되지만, RuntimeException이 발생하여 비정상적으로 종료될 것이다.
-> 'RuntimeException클래스과 그 자손(unchecked예외)'에 해당하는 예외는 프로그래머가 실수로 발생하는 것들이기 때문에 예외처리를 강제하지 않는다.


📖 11. 메서드에 예외 선언하기

-> interrupterException은 Exception(checked)의 자식이므로 예외처리를 꼭 해주어야 한다.
-> lllegalMonitorStateException은 RuntimeException(unchecked)의 자식이므로 예외처리 생략 가능!

  • 오버라이딩할 때는 단순히 선언된 예외의 개수가 아니라 상속관계까지 고려해야한다. (오버라이딩한 자식 메서드의 예외처리는 부모의 예외처리보다 작아야 한다.)

📖 12. 메서드에 예외 선언하기 예제 1

<예제 8-9 >

✍️ 입력

class Ex8_9 {
	public static void main(String[] args) throws Exception {
		method1();	 // 같은 클래스내의 static멤버이므로 객체생성없이 직접 호출가능.
  	}	// main메서드의 끝

	static void method1() throws Exception {
		method2();
	}	// method1의 끝

	static void method2() throws Exception {
		throw new Exception();
	}	// method2의 끝
}

💻 출력
Exception in thread "main" java.lang.Exception
at Ex8_9.method2(Ex8_9.java:11)
at Ex8_9.method1(Ex8_9.java:7)
at Ex8_9.main(Ex8_9.java:3)


📖 13. 메서드에 예외 선언하기 예제 2

<예제 8-10 >

✍️ 입력

import java.io.*;

class Ex8_10 {
	public static void main(String[] args) {
		try {
			File f = createFile("test.txt");
			System.out.println( f.getName()+"파일이 성공적으로 생성되었습니다.");
		} catch (Exception e) {
			System.out.println(e.getMessage()+" 다시 입력해 주시기 바랍니다.");
		}
	}	// main메서드의 끝

	static File createFile(String fileName) throws Exception {
		if (fileName==null || fileName.equals(""))
			throw new Exception("파일이름이 유효하지 않습니다.");
		File f = new File(fileName);		//  File클래스의 객체를 만든다.
     	// File객체의 createNewFile메서드를 이용해서 실제 파일을 생성한다.
		f.createNewFile();
		return f;		// 생성된 객체의 참조를 반환한다.
	}	// createFile메서드의 끝
}	// 클래스의 끝

💻 출력
test.txt파일이 성공적으로 생성되었습니다.


📖 14. finally블럭

-> 중복된 코드를 줄일 수 있다.


📖 15. 사용자 정의 예외 만들기


📖 16. 사용자 정의 예외 만들기 예제

<예제 8-11 >

✍️ 입력

class Ex8_11 {
	public static void main(String args[]) {
		try {
			startInstall();		// 프로그램 설치에 필요한 준비를 한다.
			copyFiles();		// 파일들을 복사한다. 
		} catch (SpaceException e)	{
			System.out.println("에러 메시지 : " + e.getMessage());
			e.printStackTrace();
			System.out.println("공간을 확보한 후에 다시 설치하시기 바랍니다.");
		} catch (MemoryException me)	{
			System.out.println("에러 메시지 : " + me.getMessage());
			me.printStackTrace();
			System.gc();         // Garbage Collection을 수행하여 메모리를 늘려준다.
			System.out.println("다시 설치를 시도하세요.");
		} finally {
			deleteTempFiles();		// 프로그램 설치에 사용된 임시파일들을 삭제한다.
		} // try의 끝
	} // main의 끝

   static void startInstall() throws SpaceException, MemoryException { 
		if(!enoughSpace()) 		// 충분한 설치 공간이 없으면...
			throw new SpaceException("설치할 공간이 부족합니다.");
		if (!enoughMemory())		// 충분한 메모리가 없으면...
			throw new MemoryException("메모리가 부족합니다.");
   } // startInstall메서드의 끝

   static void copyFiles() { /* 파일들을 복사하는 코드를 적는다. */ }
   static void deleteTempFiles() { /* 임시파일들을 삭제하는 코드를 적는다.*/ }
   
   static boolean enoughSpace()   {
		// 설치하는데 필요한 공간이 있는지 확인하는 코드를 적는다.
		return false;
   }
   
	static boolean enoughMemory() {
		// 설치하는데 필요한 메모리공간이 있는지 확인하는 코드를 적는다.
		return true;
   }
} // ExceptionTest클래스의 끝

class SpaceException extends Exception {
	SpaceException(String msg) {
	   super(msg);	
   }
}

class MemoryException extends Exception {
	MemoryException(String msg) {
	   super(msg);	
   }
}

💻 출력
에러 메시지 : 설치할 공간이 부족합니다.
공간을 확보한 후에 다시 설치하시기 바랍니다.
SpaceException: 설치할 공간이 부족합니다.
at Ex8_11.startInstall(Ex8_11.java:22)
at Ex8_11.main(Ex8_11.java:4)


📖 17. 예외 되던지기(exception re-throwing)

<예제 8-12 >

✍️ 입력

class Ex8_12 {
	public static void main(String[] 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메서드의 끝
}

💻 출력
method1메서드에서 예외가 처리되었습니다.
main메서드에서 예외가 처리되었습니다.


📖 18. 연결된 예외(chained exception)

  • 예외 A가 예외 B를 발생시켰다면, A를 B의 '원인 예외(cause exception)'라고 한다.

  • 왜 사용할까?

  1. 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위함이다.

    • catch블럭을 직성하면, 실제로 발생한 예외가 어떤 것인지 알 수 없다는 문제가 생긴다.
    • 상속 관계를 바꿔야 하는 부담이 생긴다.
  2. checked <-> unchecked 바꿀 수 있다.

<예제 8-13 >

✍️ 입력

class Ex8_13 {
	public static void main(String args[]) {
		try {
			install();
		} catch(InstallException e) {
			e.printStackTrace();
		} catch(Exception e) {
			e.printStackTrace();		
		}
	} // main의 끝

	static void install() throws InstallException {
		try {
			startInstall();		// 프로그램 설치에 필요한 준비를 한다.
			copyFiles();		// 파일들을 복사한다. 
		} catch (SpaceException2 e)	{
			InstallException ie = new InstallException("설치 중 예외발생");
			ie.initCause(e);
			throw ie;
		} catch (MemoryException2 me) {
			InstallException ie = new InstallException("설치 중 예외발생");
			ie.initCause(me);
			throw ie;
		} finally {
			deleteTempFiles();		// 프로그램 설치에 사용된 임시파일들을 삭제한다.
		} // try의 끝
	}

	static void startInstall() throws SpaceException2, MemoryException2 { 
		if(!enoughSpace()) { 		// 충분한 설치 공간이 없으면...
			throw new SpaceException2("설치할 공간이 부족합니다.");
		}

		if (!enoughMemory()) {	// 충분한 메모리가 없으면...
			throw new MemoryException2("메모리가 부족합니다.");
//			throw new RuntimeException(new MemoryException("메모리가 부족합니다."));
		}
	} // startInstall메서드의 끝

   static void copyFiles()       { /* 파일들을 복사하는 코드를 적는다.   */ }
   static void deleteTempFiles() { /* 임시파일들을 삭제하는 코드를 적는다.*/ }
   
   static boolean enoughSpace() {
		// 설치하는데 필요한 공간이 있는지 확인하는 코드를 적는다.
		return false;
   }
   static boolean enoughMemory() {
		// 설치하는데 필요한 메모리공간이 있는지 확인하는 코드를 적는다.
		return true;
   }
} // ExceptionTest클래스의 끝

class InstallException extends Exception {
	InstallException(String msg) {
	   super(msg);	
   }
} 

class SpaceException2 extends Exception {
	SpaceException2(String msg) {
	   super(msg);	
   }
} 

class MemoryException2 extends Exception {
	MemoryException2(String msg) {
	   super(msg);	
   }
}

💻 출력
InstallException: 설치 중 예외발생
at Ex8_13.install(Ex8_13.java:17)
at Ex8_13.main(Ex8_13.java:4)
Caused by: SpaceException2: 설치할 공간이 부족합니다.
at Ex8_13.startInstall(Ex8_13.java:31)
at Ex8_13.install(Ex8_13.java:14)
... 1 more




(정리)




[출처] 자바의 정석 <기초편> (남궁 성 지음)

profile
멈추지 않기

0개의 댓글