본문 바로가기
JAVA/JAVA Note

[JAVA Note] 20. 예외

by objade 2022. 11. 16.

 

1. 예외 처리

1) Exception

: 프로그램에서 발생할 수 있는 예외 상황을 객체로 취급하는 클래스

- java.lang.Exception

- 모든 예외의 최상위 클래스임       

① 예외 : 예측 할 수 있는 문제상황, 적절한 처리를 통해 처리할 수 있음            

② 에러 : 코드 상의 결함으로 발생하는 프로그램 내부에서 처리할 수 없는 문제상황

→ 코드의 방향이 잘못되었기 때문에 코드 자체를 수정해야함

 

 

2) 예시

① 정수를 입력받아 출력하도록 하는 코드

Scanner sc = new Scaner(System.in);
int n1;
		
System.out.print("정수 입력 : ");
n1 = sc.nextInt();
		
System.out.println("n1 : " + n1);
sc.close();

- 코드 실행 중, 만약 사용자가 정수가 아닌 값을 입력하면 예외가 발생함

- Exception in thread "main" java.util.InputMismatchException

~ 예외 처리

Scanner sc = new Scanner(System.in);
int n1;

String input;
		
System.out.print("정수 입력 : ");
input = sc.next();
		
if(isNumeric(input)) {	// if가 본래 목적과 다르게 사용됨
n1 = Integer.parseInt(input);
	System.out.println("n1 : " + n1);
}
		
else {
	System.err.println("정수를 입력하지 않았습니다 !");
}

sc.close();

② 배열에 정수를 저장하는 코드

int[] arr = new int[5];
arr[5] = 30;
System.out.println(arr[5]);

- 배열의 범위보다 높은 값을 넣으면 예외가 발생함

- Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException

 

3) try ~ catch

- if는 프로그램의 분기를 나눔

→ 예외처리에 if를 사용하는 경우에는 if가 본래 목적과 다르게 사용됨

→ 분기를 나누는 if인지, 예외를 처리하는 if인지 일일이 구별을 해야함

try ~ catch를 사용

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

public class Ex02 {
	
	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		int n1;
		
		System.out.print("정수 입력 : ");
		try {		// 다음 코드를 시도하여 
			n1 = sc.nextInt();
			System.out.println("n1 : " + n1);
			
		} catch(InputMismatchException e) {	
		// 지정한 타입의 예외가 발생하면 객체 e로 받음
			System.out.println(e.getMessage());
			e.printStackTrace();	// 예외 발생 스택을 순서대로 출력함
			
			System.err.println("정수를 입력해야 합니다.");
			
		}
		System.out.println("끝");	// 예외가 발생하더라도 코드는 실행함
		
		sc.close();
	}
}

- printStackTrace() : 예외 발생 스택을 순서대로 출력함

 

- try ~ catch를 사용하면, 예외가 발생하더라도 코드는 끝까지 실행됨

 

- 예시

① 배열의 범위를 넘어서는 값을 입력했을 때 발생하는 예외 처리

 

~ 방법 1)

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

public class Ex03 {
	public static void main(String[] args) {
		int[] arr = { 10, 30, 20, 40, 50 };
		Scanner sc = new Scanner(System.in);
		int index;
		
		System.out.print("몇번째 정수를 출력할까요 : ");
		
		try {
			index = sc.nextInt();
			System.out.printf("arr[%d] : %d\n", index, arr[index]);
		} catch(ArrayIndexOutOfBoundsException e) {
//			System.out.println(e);		// e.toString()
			
			System.err.println("index의 범위를 넘어서는 값을 입력했습니다.");
		}
	}
}

~ 방법 2)

try {
	System.out.print("몇번째 정수를 출력할까요 : ");
	index = sc.nextInt();
	System.out.printf("arr[%d] : %d\n", index, arr[index]);
    
} catch(ArrayIndexOutOfBoundsException e) {
//	System.out.println(e);		// e.toString()		
System.err.println("index의 범위를 넘어서는 값을 입력했습니다.");

} catch(InputMismatchException e) {
//	System.out.println(e);
	System.err.println("정수를 입력해야 합니다.");
    
} finally {		
		sc.close();
		System.out.println("프로그램 종료");
}

- finally : 예외 발생 여부에 상관없이 무조건 수행하는 코드

 

* 함수에서 return은 종료를 의미하지만, return 이후에도 finally는 반드시 수행하고 함수가 종료됨

static void test() {
	try {
		System.out.println("try");
		return;
        
	} finally {
		System.out.println("Finally !");
	}
}
} finally {	
	sc.close();
	System.out.println("프로그램 종료");
}

test();

}	// end of main

 

- 직접 만든 클래스로 예외를 규정할 수 있음

 

- 배열에 중복된 값이 들어오면 예외가 발생하도록 하는 코드

~ 중복을 확인하는 함수

static boolean isDuplicate(String[] arr, String str) {
for(int i = 0; i < arr.length; i++) {
		if(str.equals(arr[i])) {
			return true;
		}
	}
	return false;
}

~ 배열의 모든 칸에 문자열이 들어있는지 확인하는 함수

static boolean isFull(String[] arr) {
	for(int i = 0; i< arr.length; i++) {
		if(arr[i] == null) {
			return false;
		}
	}
	return true;
}

~ 배열의 빈 칸에 요소를 추가하는 함수

static void insert(String[] arr, String str) {
	for(int i = 0; i < arr.length; i++) {
		if(arr[i] == null) {
			arr[i] = str;
			break;
		}
	}
}

~ main 함수

public static void main(String[] args) {
		
	String[] arr = new String[5];
	Scanner sc = new Scanner(System.in);
	String str;
		
	while(isFull(arr) == false) {		
		System.out.print("문자열 입력 : ");
		str = sc.nextLine();
		if(isDuplicate(arr, str)) {
				// 내가 규정한 새로운 예외 객체를 생성한다.
				
			try {
				// 내가 직접 만든 클래스로도 예외를 규정하고 사용할 수 있음
				MyException ex = new MyException();
				throw ex;	// throw로 예외 객체를 던지면
					
			} catch(MyException e) {	// catch가 받을 수 있음	
//				System.out.println("이미 입력된 데이터입니다.");
//				System.out.println(e);
				e.printStackTrace();
				continue;	
				// break의 반대. 반복문에서 위로 탈출하여 다시 반복문을 수행함
			}
		}
		insert(arr, str);	// 입력 받은 객체가 중복이 아니라면 insert 실행
	}

~ 배열 출력

	for(int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
		
		sc.close();
	}
}

 

② 두 정수와 연산자를 입력받아서 결과를 화면에 출력하는 코드를 작성하기 (해당 과정에서 발생할 수 있는 예외를 try ~ catch 구문으로 작성)

~ 강사님 풀이 : switch ~ case문 사용

public static void main(String[] args) {
	Scanner sc = new Scanner(System.in);
	int n1, n2, result;
	char oper;
		
	try {
		System.out.print("n1 : "); 	n1 = Integer.parseInt(sc.nextLine());
		System.out.print("연산 : "); 	oper = sc.nextLine().charAt(0);
		System.out.print("n2 : "); 	n2 = Integer.parseInt(sc.nextLine());
			
		switch(oper) {
			case '+':	result = n1 + n2; break;
			case '-':	result = n1 - n2; break;
			case '*':	result = n1 * n2; break;
			case '/':	result = n1 / n2; break;
			default:  	
				MyOperatorException ex = new MyOperatorException();
				throw ex;
		}
		System.out.println("결과 : " + result);
			
	} catch(NumberFormatException e) {	// 문자열을 숫자로 변환할 때 발생할 수 있는 예외
		System.out.println("정수를 입력해야 합니다");
			
	} catch(ArithmeticException e) {	// 정수를 0으로 나눌때 발생할 수 있는 예외
		System.out.println("정수를 0으로 나눌 수 없습니다");
			
	} catch(MyOperatorException e) {	// 연산자에 지정된 문자 이외 값을 입력할 경우 발생시키는 예외
		e.printStackTrace();
			
	} catch(Exception e) {		// 모든 예외를 받아서
		System.out.println(e);
		System.out.println(e.getMessage());
		e.printStackTrace();	// 예외 발생 현황을 추적하면서 출력한다 (기본값)
			
	} finally {
		sc.close();
	}
	System.out.println("종료");
}


~ 내 풀이 : if문 사용

import java.util.Scanner;

class MyExcept extends Exception {
	
	private static final long serialVersionUID = 1L;

	@Override
	public String toString() {	// Override from Object
		
		return "연산자가 아닌 다른 값을 입력받았습니다.";
	}
	
	@Override
	public String getMessage() {	// Override from Throwable
		
		return "연산자가 아닌 다른 값을 입력받았습니다.";
	}
}

public class Ex05 {
	
	static boolean notOperation(char oper) {
		if(oper == '+' || oper == '-' || oper == '*' || oper == '/' || oper == '%') {
			return true;
		}
		return false;
	}

	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		int n1;	
		int n2;	
		char oper;
		double result = 0;
		

		try {
			System.out.print("정수1 입력 : ");
			n1 = Integer.parseInt(sc.nextLine());
			
			System.out.print("정수 2 입력 : ");
			n2 = Integer.parseInt(sc.nextLine());
			
			System.out.print("연산자 입력 : ");
			oper = sc.nextLine().charAt(0);
            
            	if(notOperation(oper) == false) {
        		try {	
				MyExcept ex = new MyExcept();
				throw ex;	
				} catch(MyExcept e) {	
                	// 연산자에 지정된 문자 이외 값을 입력할 경우 발생시키는 예외
				System.err.println("연산자가 아닌 값을 입력했습니다.");
					
				} 
			}
			
			else {
				if(oper == '+') result = n1 + n2;
				else if(oper == '-') result = n1 - n2;	
				else if(oper == '*') result = n1 * n2;
				else if(oper == '/') result = n1 / n2;
				else if(oper == '%') result = n1 % n2;

				System.out.printf("%d %c %d = %.2f", n1, oper, n2, result);
			}
            
		} catch(NumberFormatException e){	// 문자열을 숫자로 변환할 때 발생할 수 있는 예외
			System.err.println("int형 정수가 아닌 값을 입력했습니다.");
        
		} catch(ArithmeticException e) {	// 정수를 0으로 나눌때 발생할 수 있는 예외
			System.err.println("0으로는 나눌 수 없습니다.");
        
		} catch(Exception e) {		// 모든 예외를 받아서
			System.out.println(e);
			System.out.println(e.getMessage());
			e.printStackTrace();		// 예외 발생 현황을 추적하면서 출력함 (기본값)
			
   			// 가급적이면 마지막에 사용할 것
            
		}  finally {
			sc.close();
		}
		System.out.println("종료");
	}
}

4) throws

: 현재 함수에서 발생한 예외의 처리를 caller에게 전가시킴

void method1 throws NullPointerException {
	String str = null;		
	System.out.println(str.charAt(0));
}

void method2() throws ArrayIndexOutOfBoundsException{
	int[] arr = { 2, 7, 8, 4, 6 };
	System.out.println(arr[5]);
}

- 예외를 처리할 때에는 해당 함수 내부에서 try ~ catch를 사용해서 처리할 지, 아니면 throws를 사용

public static void main(String[] args) {
	Test6 ob = new Test6();

	try {
		ob.method1();

	} catch(NullPointerException e) {	
		// throws NullPointerException (런타임 예외라서 처리 안해도 실행 가능)
		System.out.println("NullPointerException");
	} 
		
	try {
		Thread.sleep(1000);		
		// throws InterruptedException (런타임 예외가 아니라서 의무적으로 처리)
	} catch (InterruptedException e) {
		e.printStackTrace();
	}

	try {
		ob.method2();
	} catch(ArrayIndexOutOfBoundsException e) {
		System.out.println("ArrayIndexOutOfBoundsException");
  }
}

- RuntimeException : 의무적으로 처리하지 않아도 되는 예외

→ throws NullPointerException (런타임 예외라서 처리 안해도 실행 가능) 

→ throws InterruptedException (런타임 예외가 아니라서 의무적으로 처리)

 

2. Thread

 

1) Thread 클래스

: 하나의 프로그램에서 여러 함수가 동시다발적으로 실행되도록 처리하는 클래스

 

2) 사용 순서

Thread 클래스를 상속한 새로운 클래스를 작성함

Thread 클래스의 public void run() 메서드를 오버라이딩하여 원하는 내용을 작성함

③ 새로운 클래스의 객체를 생성하여 start() 메서드를 호출함

 

- 예시 )  숫자와 알파벳을 동시에 출력하는 코드

 

~ 0 ~ 25까지의 숫자를 출력하는 클래스 만들기
Thread 클래스를 상속한 클래스를 작성하고, Thread 클래스의 run()을 오버라이딩하여 다중 작업하고 싶은 내용을 작성함

→ 함수로 작성해도 되고, 함수의 내용을 바로 작성해도 됨

class N extends Thread {
	void show() {
		for(int i = 0; i < 25; i++) {
			System.out.print(i + " ");
		}
	}
	
	@Override
	public void run() {	
		show();	
	}
}

 

~ A ~ Z 까지의 알파벳을 출력하는 클래스 만들기

class A {
	void show() {
		for(char ch = 'A'; ch <= 'Z'; ch++) {
			System.out.print(ch + " ");
		}
		
	}

- 실행내용을 작성한 함수이지만, 다중실행은 되지 않음

- run() 메서드의 내용을 별도의 스레드에서 실행하도록 함

 

~ main 함수

public static void main(String[] args) {
		
		N ob1 = new N();
		A ob2 = new A();
		
//		ob1.show();
//		ob1.run(); 	// 실행내용을 작성한 함수이지만, 다중실행은 되지 않음
		ob1.start(); // run() 메서드의 내용을 별도의 스레드에서 실행하도록 함
		
		ob2.show();
		
		System.out.println();
		
	}

 

4) Runnable 인터페이스

- 자바에서 다중 스레드를 구현하려면, Thread 클래스를 상속해야 함

- 자바에서는 다중 상속을 허용하지 않음

- 이미 슈퍼클래스를 가지는 클래스를 스레드로 처리하기 위해서는 Runnable 인터페이스를 상속받아야함

class NumberThread extends Object implements Runnable {

	@Override
	public void run() {
		for(int i = 0; i < 10; i++) {
			System.out.print(i + " ");
		}
	}
}
public static void main(String[] args) {
	NumberThread ob = new NumberThread();		
	Thread th = new Thread(ob);		
	th.start();

→ Runnable 객체를 Thread 생성자 매개변수로 전달함

→ start를 호출하면, run()의 내용을 별도의 스레드에서 실행함

 

~ Runnable은 함수형 인터페이스라서, 람다식 객체 생성이 가능함

Thread th2 = new Thread(() ->  {
	for(char ch = 'a'; ch <= 'z'; ch++) {
		System.out.print(ch + " ");
	}
});
		
th2.start();
new Thread(() -> System.out.print("Hello ")).start();

for(char ch = 'A'; ch <= 'G'; ch++) {
	System.out.print(ch + " ");
}
	System.out.println();
}

 

- 예시) 10초동안 얼마나 많은 문자열을 입력할 수 있는지 확인하는 코드

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

class Timers extends Object implements Runnable {
	
	private int second;
	private boolean over;
	
	public Timers(int second) {
		this.second = second;
	}
	
	public void check() {
		for(int i = second; i != -1; i--) {
			System.out.printf("\t[%02d:%02d]\n", i / 60, i % 60);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {}
		}
		over = true;
	}
	
	public boolean isOver() {
		return over;
	}
	@Override
	public void run() {
		check();	
	}
}

~ 10초가 지나면 지금까지 입력받은 모든 문자열을 한줄씩 출력함

public class Ex09 {
	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		List<String> list = new ArrayList<String>();
		
		
		Timers timer = new Timers(10);
		Thread th = new Thread(timer);
		th.start();
//		timer.check();
		
		while(timer.isOver() == false) {
			System.out.print("문자열 입력 : ");
			list.add(sc.nextLine());
		}
		
		list.forEach(str -> System.out.println(str));
		sc.close();

 

'JAVA > JAVA Note' 카테고리의 다른 글

[JAVA Note] 22. 직렬화  (1) 2022.11.16
[JAVA Note] 21. 파일 입출력  (1) 2022.11.16
[JAVA Note] 19. Collection  (1) 2022.11.16
[JAVA Note] 18. Object  (0) 2022.11.06
[JAVA Note] 17. 추상화  (0) 2022.11.06

댓글