예외(Exception)

Agnes Park·2022년 3월 12일
0

JAVA

목록 보기
25/34

1. 예외(Exception)

  • 컴파일 에러 : 문법상 오류
  • 예외(Exception) : 문법상의 오류 가 아닌 '실행중' 에 발생되는 오류상황
    -> (기본적으로) 예외 가 발생되면, 예외 관련 메세지 출력하고 프로그램이 종료 됩니다.

[Exception 화면 설명]
1. Exception 명
2. Exception 메세지
3. Exception 지점

[Exception 예시 코드1]

package com.lec.java.exception01;

import java.util.Scanner;

public class Exception01Main {

	public static void main(String[] args) {
		System.out.println("예외(Exception)");
		
		System.out.println("[1] ArithmeticException");
		int num1 = 123;
		int num2 = 0;

//		System.out.println("num1 / num2 = " + (num1 / num2));
		
		System.out.println("[2] ArrayIndexOutOfBoundsException");
		int[] numbers = new int[10];
		
//		numbers[10] = 100;
		
		System.out.println("[3] NegativeArraySizeException");

//		int size = -1;
//		int [] number2 = new int[size];
		
		
		System.out.println("[4] NullPointerException(NPE)");
		String str = null;
		
//		System.out.println("스트링 길이: " + str.length());
		
		System.out.println("[5] InputMismatchException");  
		Scanner sc = new Scanner(System.in);
	
//		sc.nextInt();
		
		sc.close();
		

	} // end main()

} // enc class Exception01Main

2. 예외처리 (try~catch)

try~catch를 사용하는 이유
1. if 문은 예외 처리 이외의 용도로 사용되기 때문에 프로그램 코드상에서 예외처리 부분을 구분하기가 쉽지 않다.
2. try {} 블럭은 '일반적인 흐름'을 ,catch {} 블럭을 '예외처리' 블럭으로 만듦으로 코드 분석이 훨씬 용이.

[예외처리 코드 예시1]

package com.lec.java.exception02;

public class Exception02Main {

	public static void main(String[] args) {
		System.out.println("예외(Exception) 처리");
		
		int num1 = 123;
		int num2 = 0;
		int result = 0;
		
		// if 문을 사용한 처리
		if (num2 != 0) {
			result = num1 / num2;
		} else {
			System.out.println("0으로 나눌 수 없습니다... ");
		} // end if
		
		System.out.println("결과: " + result);
		
		// 위의 코드를 try~catch 로 만들어 처리

		try {
			// try{...} 코드 수행 블럭
			result = num1 / num2;
			System.out.println("결과: " + result);
		} catch (ArithmeticException ex) {
			// catch(){...} 예외 처리 블럭, 위의 try 블럭에서 발생되는 예외를 처리(handling)하는 코드들
			System.out.println("0 으로 나누는 예외 발생");
			System.out.println(ex.getMessage());	// 예외 메세지 (String)
		}
		
		
		System.out.println();
		System.out.println("프로그램 종료...");

	} // end main()

	/* 
		특히 시스템 자원(resource), HW (파일, DB..) 등을 사용하는 프로그래밍에선
		'예외' 가 언제든지 발생할수 있으므로 try ~ catch 가 필수적이다.
		 
		try {
			// 1. DB connect
			// 2. DB 테이블 접속/쿼리
			// 3. SQL 문장 실행
			// 4. 결과 출력
		} catch (Exception e) {
			// Exception 처리
		}
		
		if (DB connect 성공) {
			if (DB table 쿼리 성공) {
				if (SQL 문장 실행) {
					System.out.println("결과"));
				} else {
					// SQL 문장 실패 처리
				}
			} else {
				// DB 테이블 쿼리 실패 처리
			}
		} else {
			// DB connect 실패 처리
		}
	 */
	
	
} // end class Exception02Main

[예외처리 코드 예시2]

package com.lec.java.exception03;

/*  예외 처리: try ~ catch
  	연습 01패키지에서 발생하던 예외들을 try~catch로 잡아보자 
  	
 	 주의! : try 블럭 안에서 선언된 변수는 try 블럭안에서만 사용되는 지역변수가 된다.
	     catch 블럭등 다른 블럭에서도 사용 가능하게 하려면 try 바깥에서 선언해야 한다
 */
public class Exception03Main {

	public static void main(String[] args) {
		System.out.println("예외 처리: try ~ catch");
		
		System.out.println();
		System.out.println("[1] ArithmeticException");
		
		// 주의! : try 블럭 안에서 선언된 변수는 try 블럭안에서만 사용되는 지역변수가 된다.
		//		catch 블럭 등 다른 블럭에서도 사용 가능하게 하려면 try 바깥에서 선언해야 한다.
		int num1 = 0;
		int num2 = 0;
		
		try { 
			num1 = 123;
			num2 = 0;
			System.out.println("num1 / num2 = " + (num1 / num2));
		} catch(ArithmeticException ex) {
			System.out.println(num1 + " 은 " + num2 + "로 나누면 안돼요");
			System.out.println(ex.getMessage());
			ex.printStackTrace();
		} // end try
		
		
		System.out.println();
		System.out.println("[2] ArrayIndexOutOfBoundsException");
		try {
			int[] numbers = new int[10];
			numbers[100] = 111;
		
		} catch (ArrayIndexOutOfBoundsException ex) {
			System.out.println("예외 메세지: " + ex.getMessage());
		} // end catch
		
		
		System.out.println();
		System.out.println("[4] NullPointerException");
		try {
			String str = null;
			System.out.println("스트링 길이: " + str.length());

		} catch (NullPointerException ex) {
			System.out.println("예외 메세지: " + ex.getMessage());
			
		} // end catch
		
		System.out.println();
		System.out.println("[5] ClassCastException");
		try {
			Object obj = new int[10];
			String str = (String)obj;
		} catch (ClassCastException ex) {
			System.out.println("예외 메세지: " + ex.getMessage());
			// 배열도 Object 클래스를 상속하므로 배열 인스턴스는 Object 의 참조변수로 참조가 가능하다
		} // end catch
		
		
		System.out.println("\n프로그램 정상종료");
	} // end main

} // end class Exception03Main

3. 예외 클래스의 상속 관계 (★필수 암기★)

package com.lec.java.exception04;

/*	예외 클래스의 상속 관계 (★필수 암기★)
	java.lang.Object
	|__ java.lang.Throwable
	  |
	  |__ java.lang.Exception  : 복구 가능
	  |    |
	  |    |__ java.lang.RuntimeException
	  |    |    |__ ArithmeticException, NullPointerException, ...
	  |    |
	  |    |__ IOException, ParseException ...
	  |
	  |__ java.lang.Error    : 복구 불가
	         ...   OutOfMemoryError, StackOverFlowError ...  
	
	 getMessage(), printStackTrace() 등의 메소드는 Throwable 클래스에 정의
	 자식 클래스들은 Throwable에 정의된 메소드들을 사용할 수 있음
	 
	catch문은 여러개를 사용할 수는 있지만, 
	 상위 예외 클래스를 하위 예외 클래스보다 먼저 사용할 수는 없다. (컴파일 에러)
	 
	  ※ 이클립스 단축키 : CTRL + T , class hierarchy (계층도) 보기
*/
public class Exception04Main {

	
	public static void main(String[] args) {
		System.out.println("try ~ catch ~ catch ");
		
		int num1 = 123, num2 = 10, result = 0;
		String str = "Java";
		int[] numbers = new int[10];

		// TODO : try ~ catch ~ catch .. 사용
		
		try {
			result = num1 / num2;
			System.out.println("나눈 결과: " + result);
			
			int length = str.length();
			System.out.println("스트링 길이: " + length);
			
			numbers[9] = 11111;
			System.out.println("numbers: " + numbers[9]);
			
			Object obj = new Exception04Main();
			String str2 = (String)obj;
			
		} catch(ArithmeticException ex) {
			System.out.println("산술연산예외: " + ex.getMessage());
		} catch(NullPointerException ex) {
			System.out.println("Null 포인터 예외: " + ex.getMessage());
		} catch(ArrayIndexOutOfBoundsException ex) {
			System.out.println("배열 인덱스 예외: " + ex.getMessage());
		} catch(Exception ex) {	// 위의 catch 하는 예외들을 제외한 나머지들을 처리
			System.out.println("예외 발생: " + ex.getMessage());
		}
		
		
		
		System.out.println();
		System.out.println("프로그램 종료");

	} // end main()

	
} // end class Exception04Main

4. multi-catch 와 finally

multi-catch

  • Java 7부터 하나의 catch문에서 여러개의 예외를 처리할 수 있는 방법을 제공
  • 절대로 같은 상속레벨의 exception 들만 multi-catch 하기.

finally

  • 예외(exception) 발생 여부와 상관없이 항상 실행되어야 할
  • 코드들은 finally 블록 안에서 작성.
  • 즉, finally 블록 안에 있는 코드들은 항상 실행이 됨.
    예외가 발생하지 않을 때는, try 블록 안의 코드들이 모두 실행된 후 finally 블록의 코드들이 실행
    예외가 발생할 때는, 해당 catch 블록의 코드들이 실행된 후에 finally 블록의 코드들이 실행

(주의)
try 블록이나 catch 블록 안에 return이 있더라도,
finally 블록 안의 코드들이 다 실행된 이후에
return이 실행되게 됨.
(주의)
try블럭, catch블럭, finally 블럭등에서 두루두루
사용할 변수는 try블럭 전에 선언하고, 초기화 까지 하자.
보통은 자원반납과 같은 것들을 할때 finally 활용
자원 : 키보드, 파일, 데이터베이스, 네트워크 ...

package com.lec.java.exception05;

import java.util.InputMismatchException;
import java.util.Scanner;

public class Exception05Main {

	public static void main(String[] args) {
		System.out.println("multi-catch");
		// Java 7부터 하나의 catch문에서 여러개의 예외를 처리할 수 있는 방법을 제공
		// 절대로 같은 상속레벨의 exception 들만 multi-catch 하기.
		
		
		// TODO : multi-catch 사용하기
		
		try {
			String str = "Java";
			str.length();
			int n = 123 / 0;
		} catch(ArithmeticException
//				| Exception		// 다른상위 레벨의 예외를 같이 나열하면 안된다.
				| NullPointerException
				| ArrayIndexOutOfBoundsException ex) {
			System.out.println(ex.getClass());
			System.out.println(ex.getMessage());
		}
		
		
		System.out.println();
		System.out.println("finally");
		// 예외(exception) 발생 여부와 상관없이 항상 실행되어야 할
		// 코드들은 finally 블록 안에서 작성.
		// 즉, finally 블록 안에 있는 코드들은 항상 실행이 됨.
		// 예외가 발생하지 않을 때에는 try 블록 안의 코드들이
		// 모두 실행된 후에 finally 블록의 코드들이 실행
		// 예외가 발생할 때는, 해당 catch 블록의 코드들이
		// 실행된 후에 finally 블록의 코드들이 실행
		// (주의) 
		// try 블록이나 catch 블록 안에 return이 있더라도,
		// finally 블록 안의 코드들이 다 실행된 이후에
		// return이 실행되게 됨.
		

		System.out.println("#1 try{} 직전");
		
		try {
			System.out.println("#2 try{} 시작");
			
			String str = null;
			str.length();
			
//			int [] numbers = new int[10];
//			numbers[10] = 123;
			
			System.out.println("#3 try{} 종료");
		} catch(NullPointerException ex) {
			System.out.println("#4 catch{}");
			System.out.println("예외: " + ex.getMessage());
			
//			return;
			
		} finally {
			System.out.println("#5 finally{}");
		}
		
		System.out.println("#6 try 종료후");
		
		
		
		
		// try블럭, catch블럭, finally 블럭등에서 두루두루
		// 사용할 변수는 try블럭 전에 선언하고, 초기화 까지 하자.

		// 보통은 자원반납과 같은 것들을 할때 finally 활용
		// 자원 : 키보드, 파일, 데이터베이스, 네트워크 ...

		Scanner sc = new Scanner(System.in);

		try {
			System.out.println("정수 입력하세요");
			sc.nextInt();
			System.out.println("try블록 종료");
		}catch(InputMismatchException ex) {
			System.out.println("예외 메시지: " + ex.getMessage());
			return;  // 설사 리턴하더라도
		}finally {
			System.out.println("finally 수행");
			sc.close();  // 자원 반납하는 부분은 반드시 finally{} 에서 처리
		}
		
		System.out.println();
		System.out.println("프로그램 종료...");

	} // end main

} // end class Exception05Main

5. throws

throws

  • 메소드 설계를 할 때 예외 처리를 직접 하지 않는 경우:
    메소드 이름 뒤에 throws Exception을 추가하면,
    예외가 발생한 경우에는 메소드를 호출한 곳으로 exception이 던져짐.
    'Exception' 및 이를 '직접 상속받은' Exception 을 throws 하는 메소드의 경우,
    이 메소를 호출하는 쪽에서 반.드.시 예외 처리 (handling) 해야 한다. 안하면 에러!
  • 반면 'RuntimeException' 및 이를 상속받은 예외를 throws 하는 메소드는 굳이 호출하는 쪽에서 매번 예외 처리 할 필요는 없다.
package com.lec.java.exception06;

public class Exception06Main {

	public static void main(String[] args) /*throws Exception*/ /* TODO */ {
		System.out.println("throws");
		
		System.out.println();
		TestClass test = new TestClass();
		int result = test.divide(123, 0);
		System.out.println("result = " + result);
		
		System.out.println();
		
		// divide2() 메소드가 throw Exception을 하고 있기 때문에
		// 메소드를 호출하는 곳에서 예외 처리를 해주지 않으면 컴파일 에러
		// 반드시 메소드 호출하는 곳에서 예외 처리를 해주어야 함.
		try {
			test.divide2(123, 0); // Unhandled exception
		} catch (Exception e) {
			e.printStackTrace();
		}
		
//		test.divide2(111, 0); // 만약에 try-catch 안할거면 main 메소드가 throws Exception 을 해줘야 한다.
						        // main() 메소드는 가상머신이 호출하는 메소드이다.  예외상황 처리는 가상머신에게 넘어간다
						        // 가상머신의 예외처리 순서
						        //    1 : getMessage 호출
						        //    2 : 예외상황이 발생해서 전달되는 과정 출력
						        //    3 : 프로그램 종료
		
		
		// RuntimeException 및 그 자식 객체들을
		// throws 하는 경우는 반드시 catch 안해줘도 에러는 안난다. > 그러나 죽음.
//		test.divide3(222, 0);	// RuntimeException 이므로 예외처리 필요x
		
		String num = "123";
		int n = Integer.parseInt(num);
//		NumberFormatException r;
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println("프로그램 종료...");

	} // end main()

} // end class Exception06Main
package com.lec.java.exception06;

public class TestClass {
	
	// 메소드를 설계를 할 때
	// 예외 처리를 메소드 내부에서 (try ~ catch) 로 처리
	public int divide(int x, int y) {
		int result = 0;

		// TODO : try ~ catch 처리하기
		try {
			result = x / y;
		} catch(ArithmeticException ex) {
			System.out.println(ex.getMessage());
		}

		return result;
	} // end divide()
	
	
	// 메소드 설계를 할 때 예외 처리를 직접 하지 않는 경우:
	// 메소드 이름 뒤에 throws Exception을 추가하면,
	// 예외가 발생한 경우에는 메소드를 호출한 곳으로 exception이 던져짐.
	// Exception 및 이를 '직접 상속받은' Exception 을 throws 하는 메소드의 경우,
	// 이 메소드를 호출하는 쪽에서 반.드.시 예외 처리 (handling) 해야 한다. 안하면 에러!
	
	
	// TODO : throws 사용하기
	public int divide2(int x, int y) throws Exception {
		return x / y;
	} // end divide2()
	
	
	// 반면 'RuntimeException' 및 이를 상속받은 예외를 throws 하는 메소드는
	// 굳이 호출하는 쪽에서 매번 예외 처리 할 필요는 없다
	
	// TODO : throws RuntimeException 사용하기
	public int divide3(int x, int y) throws RuntimeException {
		return x / y;
	} // end divide3()

	
} // end class TestClass

6. Exception 클래스 만들어 사용하기 & throw

  • Exception 또는 RuntimeException 클래스를 상속 받아서 만듬

[예시코드1]

package com.lec.java.exception07;

import java.util.Scanner;

public class Exception07Main {

	static Scanner sc = new Scanner(System.in);
	
	
	// TODO : ScoreException 을 throws 하는 메소드 만들기
	public static int inputScore() throws ScoreException {
		int score = sc.nextInt();

		if (score < 0 || score > 100) {
//			ScoreException ex = new ScoreException();
			ScoreException ex = new ScoreException(score + "값은 입력할수 없는 점수 (0 ~ 100)");
		
		
			throw ex; 	// 생성한 예외 객체를 언제든지 인위적으로 throw
		}
		
		return score;
	} // end inputScore()
	
	
	public static void main(String[] args) {
		System.out.println("예외 클래스 만들기, throw");
		
		System.out.println();

		// TODO : ScoreException 을 catch 해보자
		try {
			System.out.println("국어 점수 입력:");
			int kor = inputScore();
			System.out.println("kor = " + kor);
			
			System.out.println("영어 점수 입력:");
			int eng = inputScore();
			System.out.println("eng = " + eng);
		} catch	(ScoreException ex) {
			System.out.println(ex.getMessage());
		} finally {
			sc.close();
		}
		
			
		System.out.println("프로그램 종료");
	} // end main()

} // end class Exception07Main
package com.lec.java.exception07;

// 우리가 만드는 예외 클래스
// Exception 또는 RuntimeException 클래스를 상속 받아서 만듬

// TODO : Exception 을 상속받아 예외 클래스 정의하기
public class ScoreException extends Exception {

	// 생성자
	public ScoreException() {
		super("점수 입력 오류");
		// Exception(String message) 생성자 호출
	}
	
	public ScoreException(String msg) {
		super(msg);
	}
	
} // end class ScoreException

[예시코드2]

package com.lec.java.exception08;

import java.util.Scanner;

public class Exception08Main {

	static Scanner sc = new Scanner(System.in);
	
	// TODO : AgeInputException 을 throws 하는 메소드 정의
	public static int inputAge() throws AgeInputException {
		System.out.println("나이 입력:");
		int age = sc.nextInt();

		// age 값이 음수이면
		// AgeInputException 을 throw 하기
		if(age < 0) {
			AgeInputException ex = new AgeInputException();
			throw ex;
		}
		
		return age;
		
	} // end inputAge()
	
	
	public static void main(String[] args) {
		System.out.println("예외 클래스 만들기 2");
		

		// 제대로 입력받을때까지 예외 처리 하면서 입력받게 하기
		
		int age;
		
		while(true) {
			try {
				age = inputAge();
				System.out.println("나이: " + age);
				break;
			} catch (AgeInputException e) {
				System.out.println(e.getMessage());
				System.out.println("다시 입력하세요");
				sc.nextLine();
			}
		}

		sc.close();
		System.out.println("프로그램 종료...");
		
	} // end main()

} // end class Exception08Main
package com.lec.java.exception08;

// TODO : Exception 상속받은 예외 클래스 만들기
public class AgeInputException extends Exception {

	public AgeInputException() {
		super("나이 입력 오류");
	}
	
} // end class AgeInputException

✨ Tips!

  1. 족보 확인 : crtl + t
  2. exception이 발생하다(=던지다) = throw

0개의 댓글