본문 바로가기
JAVA

[예외처리] 예외화 & 예외미루기

by amoomar 2022. 1. 13.
반응형

이번 포스팅에서는 예외란 무엇인지, 예외의 처리방법, 예외가 아닌 것을 예외화 하는 법, 예외 미루기에 대한 내용을 정리해보았다.

 

1. 예외

: 사용자가 서비스를 이용하는데에 불편함을 주는 사항을 어떻게 해결해야하는지를 분석할 수 있도록 한다.

 

1) 예외란?

  • Exception 혹은 오류라고도 표현 할 수 있다
  • 문법상(코드상) 문제가 없었으나, 실제 수행과정에서 발생하는 문제
  • 예외가 발생하면 프로그램은 즉시 종료된다(ex. 온라인게임 도중 네트워크가 안잡히면 종료되는 현상)
  • 프로그램을 끝까지 사용할 수 없게 됨 : 서비스를 이용하는데 불편함을 준다
  • Exception도 다양한 class가 존재한다!

2) 예외의 분석

: 예외가 발생하면 콘솔창에 Exception이라는 문구와 함께 오류와 관련된 3가지의 정보를 담고있는 메세지가 출력된다.

  1. 어떤 class 혹은 type의 예외인지
  2. 오류에 대한 상세 설명
  3. 오류가 발생한 ilne number

위의 내용을 토대로 예시를 하나 해석하자면 아래와 같다

	Scanner sc = new Scanner(System.in);
	System.out.println("티");
	int num=sc.nextInt();

	System.out.println(num/10);

	System.out.println("모");

출력 결과: Exception in thread "main" java.lang.ArithmeticException: / by zero at class01.Test01.main(Test01.java:13)

-> ArithmeticException클래스와 관련된 예외이다(1)

-> 13번 라인에 0으로 입력할 수 없다(2)

-> 오류 발생 라인은 13번(3)

 

2. 예외처리

: 오류해결의 의미와 비슷하다. 어떤 예외가 발생할지 예상이 불가능하고, if문과 유사한의미의 try catch문을 사용한다

1) try catch

try{
	//예외가 발생할지도 모르는 부분
      //감싸줘야하는 부분
} catch (Exception클래스명 객체명) { //Exception클래스에 해당하는 오류 발생시
	출력문구		  //여기 출력
}

선언 방법은 위와 같으며, 구체적으로 적용하면 아래와 같은 형태가 있다. 

package class01;

//Exception 클래스에 따라 임폴트의 추가여부 결정됨
import java.util.InputMismatchException;
import java.util.Scanner;

public class Test01 {

	public static void main(String[] args) {

		Scanner sc = new Scanner(System.in);
		System.out.println("티"); //확인개념
		
		try {
			
			System.out.println(">>");
			int num=sc.nextInt();
			System.out.println("확인1"); //예외가 발생한다면 즉시 catch로 이동
			System.out.println(num/10); 
			System.out.println("확인2");

		}catch(ArithmeticException e) { //0이 입력되는것을 방지

			System.out.println("계산불가능"); 

		}catch(InputMismatchException e) { //정수외의 입력 방지
			
			System.out.println("정수로 입력하시오");
			
		}
		
		System.out.println("모"); //확인개념

	}
}

[출력결과 : = 입력]


>>

정수로 입력하시오

---------------------------------------

>>
0
확인1
계산불가능

---------------------------------------

>>
30
확인1
3
확인2

 

확인개념의 출력문구를 통해 어떠한 로직으로 구현되어있는지 대략적인 이해가 가능하다.

 

 

2) 예외 파악

: 예외처리를 통해 발생할 수 있는 예외를 파악하고 대비하는 것을 예외 파악이라고 한다. 예외 파악의 방법으로는 언리쳐블코드에 유의하며 catch문과  getMessage( ) 혹은 printStackTrace( )을 통해 예외를 파악하고 분석하여 대비할 수 있다.

 

*언터쳐블코드 : 다운캐스팅과 비슷한 개념으로, 상위개념이 더 위에 선언되어 하위개념의 내용이 평생 출력되지 못하는 Ded Code가 발생하는 것

 

*언터쳐블코드 예시

if(짝수라면){

}else if(6배수라면){
	여기 든건 평생 못본다 ! ded
}
try{

}catch(Exception e){

}catch(InputMismatchException e){
	하위개념이라 평생 출력XX
}

 

3) 예제( + finally  )

: int 배열에 값을 담고, index를 입력받아 해당 index의 값을 출력하는 코드. if 잘못된 범위의 입력이라면 예외처리 

package class01;

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

public class Test02 {

	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		int [] data = {10 ,20, 30};
		
		System.out.println("0~2의 숫자를 입력해주세요");
		System.out.println("정수입력 : ");
		
		try {
			
			//사용자가 입력할 자료형이 무엇인지 모르므로
			//출력일지 미출력일지 갈리는 부분
			int num = sc.nextInt();
			System.out.println(data[num]); 
			
		}catch(ArrayIndexOutOfBoundsException e) {//오류라면
			
			System.out.println(e.getMessage()); //오류메세지 띄워라
			System.out.println("데이터가 존재하지 않습니다");
		
		}catch(Exception e) {//그외 파악되지 않은 오류라면
			
			e.printStackTrace(); //오류에 대한 상세정보 출력
			
		} finally {
			System.out.println("오류의 유무와 상관없이 출력됨 / logging 개념");
		}
		
		
	}
}

[출력결과 :  = 입력]

0~2의 숫자를 입력해주세요
정수입력 : 

오류의 유무와 상관없이 출력됨 / logging 개념
java.util.InputMismatchException
   at java.util.Scanner.throwFor(Unknown Source)
   at java.util.Scanner.next(Unknown Source)
   at java.util.Scanner.nextInt(Unknown Source)
   at java.util.Scanner.nextInt(Unknown Source)
   at class01.Test02.main(Test02.java:20)
---------------------------------------------------------------------------
0~2의 숫자를 입력해주세요
정수입력 : 
5
5
데이터가 존재하지 않습니다
오류의 유무와 상관없이 출력됨 / logging 개념
---------------------------------------------------------------------------
0~2의 숫자를 입력해주세요
정수입력 : 
2
30
오류의 유무와 상관없이 출력됨 / logging 개념

 

3. 예외화

: 내가 원하는 범위가 아니라면 유효성 검사나 경계값 검사를 통해 사용자의 입력값을 제한할 수 있다. 하지만 이때 if문을 사용하기에 데이터 차지가 효율적이지 않으니, 효율성을 높이기 위해 일부러 예외화를 시켜서 처리할 수 있다. 

1) 예외화의 배경이론

: 예외화의 방법을 알아보기에 앞서, 평상시에 생략되어있는 Exception클래스의 내용을 다시한번 상기할 필요가 있다.

class Exception{
	
	private String message; //멤버변수
	
	Exception(){}//생성자
	
    	Exception(String message){ //생성자 오버로딩
		this.message = message;
	}
    
	public String getMessage() { //message의 get화
		return message;
	}
	
}

즉, Exception클래스의 생성자가 오버로딩 되어있음을 알아야한다!

2) 예외화의 방법

: 종료되지 않는 반복문을 선언하고, 내가 원하는 범위의 입력이 아니라면 "범위 외의 입력"을 출력하는 코드

package class01;

import java.util.Scanner;

//예외클래스를 상속받아 나만의 예외클래스 정의
//1. 상세설명(message) 설정강제
//2. 미확인 예외들(Exception)과 섞임 방지
class MyException extends Exception{
	
	MyException(String message){
		//자식클래스의 생성자는
		//부모클래스의 기본 생성자를 호출하므로
		super(message); 
	}
	
}

public class Test03 {

	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		
		MyException myExcep = new MyException("범위 외의 입력"); //내가 만든 예외
		// Exception클래스의 생성자는 오버로딩 되어있음을 알 수 있다.
		
		while(true) {
			
			System.out.println("입력 : ");
			
			try {
				
				int num = sc.nextInt();
				if(num<1 || num>3) { //내가 원하는 범위가 아니라면
					
					// throw : 일부러 예외를 만드는 역할
					// 즉, try catch를 반드시 필요로 함
					throw myExcep; 
				}
				
			}catch(Exception e) {
				System.out.println(e.getMessage()); //범위외의 입력이라면
			}
			
		}
		
		

	}
}

[출력결과 :  = 입력]

입력 : 
6
범위 외의 입력
입력 : 
1
입력 : 
78
범위 외의 입력
입력 : 

3) 예제

1번예제 : 0~100 사이의 정수를 입력받고, 10과 입력받은 값을 나눈 결과를 출력하라. 단, 예외를 파악하고 처리할 것.

-> 해당문제 풀이를 통해 다른 타입의 입력을 받았을때 발생하는 버퍼문제를 해결할 수 있다.

package class01;

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

//예외가 아닌 것을 예외화 할 목적(: 0~100이외 예외화)
class MyException extends Exception{ 

	MyException(String message){
		super(message);
	}

}

public class Test04 {

	public static void main(String[] args) {

		MyException myExcep = new MyException("0~100사이의 정수를 입력하세요");

		Scanner sc = new Scanner(System.in);
		int num=10;
		System.out.println("10과 나눌 수를 입력하세요");


		while(true) {

			try {
				
				System.out.println("입력: ");
				int x = sc.nextInt();

				if(x<0 || x>100) { //범위외의 입력이면

					throw myExcep;

				}
				//if문을 통과해야 값을 조회해야하므로
				System.out.println(num + "/" + x + "=" + num/x); 

			}catch(ArithmeticException e) { //0이 입력되면
				
				System.out.println("0으로 나눌 수 없습니다");
				
			}catch(InputMismatchException e) { //정수 외의 입력이면

				// 버퍼문제(무한루프) : 콘솔화면을 UI로 사용하기때문에 문제가 되는 부분
				// 문제해결 : Double이나 String의 입력을 받아줄 친구 생성!
				sc.nextLine(); 
				System.out.println("정수만 입력할 수 있습니다");
				 
			}catch(Exception e) { //미확인 예외라면

				e.printStackTrace();
				
			}

		}


	}
}

[출력결과 :  = 입력]

10과 나눌 수를 입력하세요
입력: 
4
10/4=2
입력: 
70
10/70=0
입력: 

정수만 입력할 수 있습니다
입력: 
6.22
정수만 입력할 수 있습니다
입력: 

 

2번예제 : 학생 관리 시스템

package class01;

import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.Iterator;
import java.util.Scanner;

class Student{
	//멤버변수
	String name;
	private int pk;
	int score;

	//생성자
	Student(String name, int pk, int score){
		this.name = name;
		this.pk = pk;
		this.score = score;
	}

	//getter
	public int getPk() {
		return pk;
	}
	//setter
	public void setPk(int pk) {
		this.pk = pk;
	}

	@Override
	public String toString() {
		return pk +" | "+ name + " | " + score + "점";
	}

}

class MyException extends Exception{

	MyException(String message){
		super(message);
	}

}

public class Test05 {

	public static void main(String[] args) {

		Scanner sc = new Scanner(System.in);
		ArrayList <Student> data = new ArrayList <Student> ();
		MyException myExcep1 = new MyException("없는 메뉴입니다");//메뉴선택
		MyException myExcep2 = new MyException("0~100사이로 입력하세요");//성적
		int pkNum = 2022001; //학번 시작번호
		int score;//점수
		String name;//이름
		String mainMsg = "1. 추가 \n2. 삭제 \n3. 학생조회 \n4. 종료";
		int choice;//메뉴선택
		int num; //입력받을 학번

		while(true) {

			System.out.println("=====화면=====");
			System.out.println(mainMsg);
			System.out.println("=============");
			System.out.println(">>");

			try {
				choice = sc.nextInt();

				if(choice==1) { //1. 추가

					System.out.println("이름 : ");
					name = sc.next();
					System.out.println("점수 : ");
					score = sc.nextInt();

					if(score<0 || score>100) {
						throw myExcep2;
					}

					data.add(new Student(name, pkNum++, score));
					System.out.println("추가 완료");


				}else if(choice==2) { //2. 삭제

					//경계값 검사
					while(true) { 
						System.out.println("삭제할 학번 : ");
						num = sc.nextInt();	
						if(num>2022000 && num<2023000) {
							break;
						}
					}
					
					//유효성검사
					boolean flag = true; // T=미존재 / F=존재
					for(int i=0; i<data.size(); i++) {
						if(num==data.get(i).getPk()) {
							flag = false;
							data.remove(i);
							System.out.println("삭제완료");
							break; //for문 종료
						}
					}
					if(flag) {
						System.out.println("존재하지 않는 학번입니다");
					}

				}else if(choice==3) { //3. 학생조회

					Iterator itr = data.iterator();
					while(itr.hasNext()) {
						System.out.println(itr.next());
					}

				}else if(choice==4) { //4. 종료

					System.out.println("프로그램 종료");
					break;

				}else {
					throw myExcep1;
				}

			}catch(InputMismatchException e) {
				sc.nextLine();//무한루프방지
				System.out.println("입력 범위를 확인하세요");
			}catch(MyException e) {
				System.out.println(e.getMessage());
			}

		}

	}
}

[출력결과 :  = 입력]

=====화면=====
1. 추가 
2. 삭제 
3. 학생조회 
4. 종료
=============
>>
1
이름 : 
김핑구
점수 : 
-3
0~100사이로 입력하세요
(생략)
점수 : 
68
추가 완료
=====화면=====
1. 추가 
2. 삭제 
3. 학생조회 
4. 종료
=============
>>
3
2022001 | 김핑구 | 68점
=====화면=====
1. 추가 
2. 삭제 
3. 학생조회 
4. 종료
=============
>>
2
삭제할 학번 : 
2022004
존재하지 않는 학번입니다
(생략)
삭제할 학번 : 
2022001
삭제완료
=====화면=====
1. 추가 
2. 삭제 
3. 학생조회 
4. 종료
=============
>>
4
프로그램 종료

4. 예외처리 미루기

 

1) 예외처리 미루기란?

: 똑같은 예외가 여러군데에서 발생할 수 있기 때문에, 예외처리에 대한 로직이 여러개 중복될 수 있다. 이럴 경우 유지보수에 굉장히 불리하므로 예외를 미루어 처리하는 경우가 발생한다. "

2) 예외처리 미루기 선언

: "예외 처리를 호출한 대상에게(공간으로) 미루겠습니다."라는 의미이다. 함수를 먼저 선언하고, 메인에서 해당 함수를 호출한다. 일련의 과정일때와 그렇지 않을때로 구분된다.

 

  • throws를 이용한 함수 선언 ( 공통적인 부분 )
public class Test06 {
	
	static void func1() throws Exception{ //머리부분
		int a=10, b=0;
		System.out.println(a/b);
		System.out.println("func1() 함수 종료!");
	}
	
	static void func2() throws Exception{ //팔부분
		Exception excep = new Exception("예외!");
		throw excep;
	}

Throws로 인해 (main혹은 지정한 곳으로) 던져지고, catch로던져진 예외를 받는다!

 

* static 이란? "객체와 무관하게"라는 의미이다. 사용되는 두가지 경우는 순서대로 1. 클래스 변수(공유자원), 2. 클래스 외부에서함수 단독선언이 있다.

 

  • 일련의 과정일때 : func1이 오류이면 func2가 진행되지 않아야할때
try {
//함수 입력시 try catch문 필수 생성 : 오류발생
	func1(); 
	func2();
} catch (Exception e) {
		e.printStackTrace();
	}
  • 일련의 과정이 아닐때 : 일련의 과정이 아닐때 : func1이 오류여부와 func2의 작동이 별개일때
try {
	func1();
} catch (Exception e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
} finally {
	System.out.println("48번 라인 통과");
}
		
try {
	func2();
} catch (Exception e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
} finally {
	System.out.println("57번 라인 통과");
}

[출력결과] -> 출력할때마다 순서 뒤죽박죽임

java.lang.ArithmeticException: / by zero
at class01.Test06.func1(Test06.java:15)
at class01.Test06.main(Test06.java:30)
java.lang.ArithmeticException: / by zero
at class01.Test06.func1(Test06.java:15)
at class01.Test06.main(Test06.java:41)
java.lang.Exception: 예외!
at class01.Test06.func2(Test06.java:20)
at class01.Test06.main(Test06.java:50)
48번 라인 통과
57번 라인 통과

반응형