본문 바로가기

BackEnd/Java

[java] ch11 예외 처리 2

728x90
반응형

1. 리소스

  ▷ 데이터를 제공하는 객체

  ▷ 리소스는 사용하기 위해 열어야(open)하며, 사용이 끝난 다음에는 닫아야(close)합니다.

  ▷ 리소스를 사용하다가 예외가 발생될 경우에도 안전하게 닫는 것이 중요합니다.

  ▷ try-with-resources 블록을 사용하면 예외 발생 여부와 상관없이 리소스를 자동으로 닫아줍니다.

 

1. MyResource 클래스
public class MyResource implements AutoCloseable {
	private String name;
	
	public MyResource(String name) {
		this.name = name;
		System.out.println("[MyResource(" + name + ") 열기]");
	}
	
	public String read1() {
		System.out.println("[MyResource(" + name + ") 읽기]");
		return "100";
	}
	
	public String read2() {
		System.out.println("[MyResource(" + name + ") 읽기]");
		return "abc";
	}
	
	@Override
	public void close() throws Exception {
		System.out.println("[MyResource(" + name + ") 닫기]");
	}
}​


▷ try-with-resource 블록을 사용하면 예외 발생 여부와 상관없이 리소드를 자동으로 닫아줍니다.
 try 괄호에 리소스 여는 코드를 작성하면 try 블록이 정상적으로 실행을 완료했거나 도중 예외가 발생하면 자동으로 리소스의 close() 메소드가 호출됩니다.
마지막 close 함수의 throws는 close()가 나타나면 Exception으로 넘긴다는 의미입니다.


2. TryWithResourceExample 메인 클래스

public class TryWithResourceExample {
	public static void main(String[] args) {
		try (MyResource res = new MyResource("A")){
			String data = res.read1();
			int value = Integer.parseInt(data);
		} catch(Exception e) {
			System.out.println("예외 처리 : " + e.getMessage());
		}
		
		System.out.println();
		
		try (MyResource res = new MyResource("A")){
			String data = res.read2();
			// NumberFormatException 발생
			int value = Integer.parseInt(data);
		} catch(Exception e) {
			System.out.println("예외 처리 : " + e.getMessage());
		}
		
		System.out.println();

//		MyResource res1 = new MyResource("A");
//		MyResource res2 = new MyResource("B");
//		try(res1;res2){
//		java 9부터 위의 코드는 실행가능합니다.
		try(
			MyResource res1 = new MyResource("A");
			MyResource res2 = new MyResource("B");
		){
			String data1 = res1.read1();
			String data2 = res2.read1();			
		} catch(Exception e) {
			System.out.println("예외 처리 : " + e.getMessage());
		}
	}
}

//	출력 : 
//	[MyResource(A) 열기]
//	[MyResource(A) 읽기]
//	[MyResource(A) 닫기]
//	
//	[MyResource(A) 열기]
//	[MyResource(A) 읽기]
//	[MyResource(A) 닫기]
//	예외 처리 : For input string: "abc"
//	
//	[MyResource(A) 열기]
//	[MyResource(B) 열기]
//	[MyResource(A) 읽기]
//	[MyResource(B) 읽기]
//	[MyResource(B) 닫기]
//	[MyResource(A) 닫기]


▷ 값이 정상적으로 입력되면 열고, 읽고 닫는 것까지 원활하게 이어집니다. 
 예외가 있더라도 전체적인 동작과 함께 예외가 출력됩니다.

▷ 아래의 코드는 두가지 생성자를 동시에 처리할 수 있게 만듭니다.

 

 

2. 예외 떠넘기기 

  ▷ 메소드 내부에서 예외 발생시 throws 키워드 이용해 메소드를 호출한 곳으로 예외 떠넘기기

  ▷ throws는 메소드 선언부 끝에 작성합니다. 떠넘길 예외 클래스를 쉼표로 구분해서 나열합니다.

  ▷ 나열할 예외 클래스가 많으면 throws Exception 또는 throws Throwable 만으로 모든 예외 떠넘기기

 

◎  떠넘기기 예제 1

public class ThrowsExample1 {

	public static void main(String[] args) {
		try {
			findClass();
		} catch(ClassNotFoundException e) {
			System.out.println("예외 처리 : " + e.toString());
		}
		System.out.println("프로그램 종료");
	}
	
	public static void findClass() throws ClassNotFoundException{
		Class.forName("java.lang.String2");
	}
}

//	출력 : 
//	예외 처리 : java.lang.ClassNotFoundException: java.lang.String2
//	프로그램 종료


▷ 메인에서 메소드를 호출하는 것이 아니라 메소드를 찾으러 가는 것입니다.
메소드에서 throws를 사용하면 forName을 통해 원하는 클래스를 찾고 없으면 예외 처리를 합니다.

 

◎  떠넘기기 예제 2

public class ThrowsExample2 {

	public static void main(String[] args) throws Exception {
		findClass();
	}
	
	public static void findClass() throws ClassNotFoundException{
		Class.forName("java.lang.String2");
	}
}

//	출력 : 
//	Exception in thread "main" java.lang.ClassNotFoundException: java.lang.String2
//	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
//  .
//  .
//  .
//	at throws01.ThrowsExample2.findClass(ThrowsExample2.java:10)
//	at throws01.ThrowsExample2.main(ThrowsExample2.java:6)​
 

▷ main에서 findClass()를 불러오지 않으면 아무리 오류가 발생해도 console 창에 출력이 되지 않습니다.
▷ findClass에서 ClassNotFoundException으로 먼저 예외를 저장하고
    main에서 함수를 호출하면 JVM으로 Exception을 보내줍니다.
    ▶ 따라서 main에서 콘솔 창 호출과 JVM으로 보냈을때 concol 창에 발생하는 오류가 다릅니다.

 

 

3. 사용자 정의 예외

   ▷ 표준 라이브러리에는 없어 직접 정의하는 예외 클래스

   ▷ 일반 예외는 Exception의 자식 클래스로 선언, 실행 예외는 RuntimeException의 자식 클래스로 선언합니다.

 

◎ 예외 발생시키기

   ▷ throw 키워드와 함께 예외 객체를 제공해 사용자 정의 예외를 직접 코드에서 발생시킬 수 있습니다.

   ▷ 예외의 원인에 해당하는 메세지를 제공하려면 생성자 매개 값으로 전달됩니다.

 

 

◎ 사용자 정의 예외 예시 1

1. InsufficientException 클래스

public class InsufficientException extends Exception {
	public InsufficientException() {
	}
	
	public InsufficientException(String message) {
		super(message);
	}
}


▷ Exception을 자식 클래스로 선언하고 생성자를 선언합니다.


2. Account 클래스

public class Account {
	private long balance;
	
	public Account() {}
	
	public long getBalance() {
		return balance;
	}
	
	public void deposit(int money) {
		balance += money;
	}
	
	public void withdraw(int money) throws InsufficientException{
		if(balance < money) {
			throw new InsufficientException("잔고 부족 : " + (money-balance) + " 모자람");
		}
		
		balance -= money;
	}
}


throw new InsufficientException("잔고 부족 : " + (money-balance) + " 모자람");
    ▶ 다음 문구는 생성자를 예외로 만드는 것입니다.
    ▶ 생성자 안의 문구는 InsufficientException의 super로 메세지를 출력해줍니다.

3. AccountExample 클래스

public class AccountExample {

	public static void main(String[] args) {
		Account account = new Account();
		// 예금하기
		account.deposit(10000);
		System.out.println("예금액 : " + account.getBalance());
		
		// 출금하기
		try {
			account.withdraw(30000);
		} catch(InsufficientException e) {
			String message = e.getMessage();
			System.out.println(message);
		}
	}
}

//	출력 : 
//	예금액 : 10000
//	잔고 부족 : 20000 모자람

 

★ throw : 고의적으로 예외 발생
throws : 예외 던지기 호출한 쪽으로 예외를 넘김

 

throw를 통해 해당 문장을 고의적으로 예외로 만들어 줄 수 있습니다.

 

예외가 발생하면 catch를 통해 예외를 출력할 수 있도록 만들어주는 것이 중요하네요!!

 

다음 장은 문제들을 한 번 볼게요~!

 

많은 분들의 피드백은 언제나 환영합니다! 많은 댓글 부탁드려요~~

 

 

728x90
반응형