버글버글

Java 수업 기록 (15) Exception 본문

java/java 수업 기록

Java 수업 기록 (15) Exception

Bugle 2022. 8. 7. 00:00
반응형

 Exception(예외처리) 

* 일정의 오류이다.

(부모) Throwable
(자식)Error (자식)Exception

1. Error : 개발자가 해결 할 수 있는 문제가 아니다.

   - 시스템 레벨의 심각한 오류

   - 시스템 수정으로 문제를 해결

2. Exception : 프로그램이 죽었을때, 프로그램을 죽이지 말고 다른 코드를 입력하여 처리를 해주는 과정.

   - 프로그램 래벨의 일반적인 오류

   - 프로그램의 실행 중 발생하는 여러가지 오류를 의미함

   - 예외가 발생하면 프로그램이 강제로 종료됨

   - 예외가 발생하더라도 프로그램이 종료되지 않도록 예외처리를 해야 함

   - try - catch문을 이용해서 예외 발생으로 인해 프로그램이 종료되는 것을 회피할 수 있음.

Exception 클래스 - 모든 예외 클래스의 슈퍼 클래스
- 명시적 예외 처리 여부에 따라 Checked/Unchecked Excetion으로 구분
Checked Exception  - RuntimeException 클래스의 자식클래스가 아닌 모든 예외 클래스
 - 반드시 try - catch문으로 예외 처리를 해야 함
   ( try - catch를 사용 안 할 시, 컴파일 오류가 발생. 실행 자체가 안됨)
 - IOException, SQLException 등
Unchecked Exception ** 상속구조 중요
- RuntimeException 클래스의 자식클래스로 등록된 모든 예외 클래스
 - try - catch문이 없어도 실행 가능
 - NullPointerException, NumberFormatException 등
 - 실행중에 예외가 발생

3. try - catch 문

- 예외를 처리할 때 사용하는 코드

- 실행할 코드는 try 블록에 두고 예외를 처리할 코드는 catch 블록에 두는 방식

- try 블록의 코드를 실행하다가 예외가 발생되면 발생된 예외는 자동으로 catch 블록으로 전달됨

- 모든 예외는 자바 클래스로 만들어져 있음

실행문
try {
    실행코드
    } catch(예외 타입 선언) { 
        예외 처리 코드
    }

try = 오류 코드 >>> catch로 보내면 잡는다

예외클래스를 저장할 곳을 생성하는게 catch(예외 타입선언) 에서 한다.

 

ex) 어떤 예외가 발생할 것을 알면 아래처럼 실행하면 된다.

	try{
		NullPointerException
		} catch(NullPointerException){
	}

* try - catch의 객체명은 'e'로 통일 함.

* NullPoinerException은 RuntimeException, Exception 로 사용 할 수 있다. (NullPoinerException의 부모기 때문에)

* catch안에 catch를 또 넣어도 된다. 하지만 순서대로 catch 찾아가기 때문에 순서가 중요하다.

- 모든 예외는 Exception이 부모기 때문에,

  아래처럼 Exception이 먼저 사용되면 자식인 NumberFormatException은 실행될수가 없기 때문에 오류가 뜬다.

- catch를 쓸때는 자식 먼저 앞으로 오게 써야 한다.

		  catch(Exception e){
			System.out.println("NumberFormatException 발생");
		} catch(NumberFormatException e){
			System.out.println("NumberFormatException 발생");
		}

▶ NullPointerException 회피 

null 값이 어떤 메소드를 호출할 때 발생

예외가 발생했을때, 아래부터 위로 읽는다. 아래부터 하나씩 연결된 것을 보여주는 과정. (Stack trace)

	public static void m2() {
		
		// NullPointerException 회피 
		String[] hobbies = new String[5];
		
		hobbies[1] = "수영";
		hobbies[2] = "골프";
		hobbies[3] = "영화";
		hobbies[4] = "집콕";
		
		for(int i = 0; i < hobbies.length; i++) {
			if(hobbies[i] != null && hobbies[i].equals("수영")) {
				System.out.println("취미가 나와 같군요");
			} 
			
		}
	}

위의 예시에서 hobbies 배열의 [0] 인덱스가 null값이여서 생성되는 오류를

hobbies[i] != null

를 입력해서 해결해 준다.

 

예시) try - catch 문을 사용한 예외 처리 방법

	public static void m1() {
		try {
			String[] hobbies = new String[3];
			hobbies[1] = "swimming";
			hobbies[2] = "running";
			
			// 향상for문은 인덱스 없이 1개씩 꺼내 쓸때 적절하다.
			for(String hobby : hobbies) {
				System.out.println(hobby.substring(0, 2));
				}
		} catch(NullPointerException e) {
			System.out.println("NullPointerException 발생");
		}
		
	}

 


▶ NumberFormatException 회피 

- String을 Number 타입으로 변경할 때 발생

	public static void m4() {
		
		Scanner sc = new Scanner(System.in);
		System.out.print("이름 입력(필수) >>> ");
		String name = sc.nextLine();
		System.out.print("나이 입력(선택) >>> ");
		String strAge = sc.nextLine();	// 입력 없이 Enter만 누르면 strAge는 빈 문자열을 가짐
		
		int age;
		if(strAge.isEmpty()) {
			age = 0;
		} else {
			age = Integer.parseInt(strAge);
			
		}
		
		System.out.println("이름:" + name + ",나이:" + age + "살");
		sc.close();
		
	}

	public static void main(String[] args) {
		m4();
		

	}

위의 예시에서 if문을 통해 빈 문자열의 예외를 처리해 주었다.

 

예시) try - catch 문을 사용한 예외 처리 방법

	public static void m2() {
		try {
			String input = "20,21,,22,23,24,25";
			String[] inputs = input.split(",");
			int[] ages = new int[inputs.length];
			
			for(int i = 0; i < inputs.length; i++) {
				ages[i] = Integer.parseInt(inputs[i]);
				System.out.println("변환 값 : " + ages[i]);
			}
		} catch(NumberFormatException e){
			System.out.println("NumberFormatException 발생");
		}

	}

▶ 그 외 Exception 회피 (catch 안에 catch 예시)

	public static void m3() {
		try {
			Scanner sc = new Scanner(System.in);
			System.out.print("정수1 >>> ");
			int a = sc.nextInt();
			System.out.print("정수2 >>> ");
			int b = sc.nextInt();		
			System.out.println(a + "+" + b + "=" + (a + b));
			System.out.println(a + "-" + b + "=" + (a - b));
			System.out.println(a + "*" + b + "=" + (a * b));
			System.out.println(a + "/" + b + "=" + (a / b));
			System.out.println(a + "%" + b + "=" + (a % b));
			sc.close();
			} catch (ArithmeticException e) {
				System.out.println("ArithmeticException 오류 발생");
			} catch (InputMismatchException e) {
				System.out.println("InputMismatchException 오류 발생");
			}
		
	}

 

finally 블록 

1. try-catch문 마지막에 추가하는 블록
2. 예외 발생 여부와 상관 없이 언제나 마지막에 실행되는 블록
3. 필요 없는 경우 finally 블록 생략 가능

* 실제로 finally는 자원을 반납할 때 주로 사용된다. (close 같은)

	public static void main(String[] args) {

		try {
			Scanner sc = new Scanner(System.in);
			System.out.println("나이 입력 >>> ");
			String steAge = sc.nextLine();
			int age = Integer.parseInt(steAge);
			System.out.println(age >= 20 ? "성인" : "미성년자");
			sc.close();			
		} catch (Exception e) {
			System.out.println("예외 발생");
		}

	}

위의 코드에서 나이 입력이 잘못되었을때 Scanner가 닫히지 않고 코드가 종료된다.

그래서 위와 같은 코드는 좋지않다.

위의 코드는 Scanner(자동으로 닫히기도 함)이기 때문에 실행은 되겠지만, 다른 클래스들은 문제가 발생할 수 있다.

아래와 같이 finally 블록을 사용하여 수정 할 수 있다.


 

	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		try {
			System.out.print("나이 입력 >>> ");
			String steAge = sc.nextLine();
			int age = Integer.parseInt(steAge);
			System.out.println(age >= 20 ? "성인" : "미성년자");		
		} catch (Exception e) {
			System.out.println("예외 발생");
		} finally {
			sc.close();
			System.out.println("finally 블록 실행"); // finally 실행 확인용
		}

	}

 

throw 

1. 개발자가 직접 예외를 던지는 경우에 사용

2. 자바는 예외로 인식하지 못하지만 실제로는 예외인 경우 개발자가 직접 예외를 발생시켜서 던짐

   ( 나이 입력을 예 : 데이터타입을 int로 주고 0이하의 수를 입력하면 자바 자체에서는 오류가 아니지만,

    사람 나이가 -인 경우는 없기 때문에, 개발자는 오류인걸 안다. 이러한 사항을 막기 위해 개발자가 직접 예외 사항을 만들어 주는게 throw다.)

3. 개발자가 직접 예외를 발생시킬 때는 RuntimeException을 사용하는 경우가 일반적임
4. 개발자가 직접 만든 예외클래스를 던지는 것도 가능함.(MyException)

* throw

throw new RuntimeException(String message);
= 예외 일 시 메세지 출력
throw new RuntimeException(String message); catch (Exception e)
String message는 catch exception 객체의 e에 들어있다.

 

예시) 

	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		try {
			System.out.print("나이 입력 >>> ");
			String steAge = sc.nextLine();
			int age = Integer.parseInt(steAge);
			if(age < 0 || age > 100) {
				throw new RuntimeException("나이는 0 이상 100 이하만 가능 합니다.");
			}
			System.out.println(age >= 20 ? "성인" : "미성년자");		
		} catch (Exception e) {
			System.out.println(e.getMessage());
		} finally {
			sc.close();
			System.out.println("finally 블록 실행");
		}

	}

위 예시의 작동 원리

// 위에 예시가 작동하는 원리 풀이

class Exception{
	private String message;
    public Exception(String message){
    	this.message = message);
    }
    public String getMessage(){
    	return message;
    }
}

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

Exception e = new RuntimeException("이 나이는 잘못되었어요");
System.out.println(e.getMessage());

이해가 안된다면 아래 두줄을 암기하자..

 

 throws 

1. 메소드에서 예외를 처리하는 방식

  1) 메소드 내부에 try-catch문을 두고 직접 처리하는 방식

  2) 메소드 외부로 예외를 던져서 메소드를 호출하는 곳에서 try-catch문으로 처리하는 방식

2. 메소드 외부로 예외를 던질 때 throws문을 이용해 던지는 예외를 명시함

3. 2개 이상의 예외를 명시할 수 있기 때문에 throw가 아닌 throws 라고 함.

 

예시)

import java.util.Scanner;

public class ParkingLot {
	
	private Car[] cars;
	private int idx;
	private Scanner sc; 
	
	public ParkingLot() {
		cars = new Car[10];
		sc = new Scanner(System.in);
	}
	
	public void addCar() throws RuntimeException {

		if(idx == cars.length) {
		}

	}
	
	public void deleteCar() throws RuntimeException {
		if(idx == 0) {
			throw new RuntimeException("EMPTY");
		}

	}
	
	public void findCar() throws RuntimeException {
		if(idx == 0) {
			throw new RuntimeException("EMPTY");
		}
	}
	
	public void printAllCars()throws RuntimeException  {
		if(idx == 0) {
			throw new RuntimeException("EMPTY");
		}
	}
	
	public void manage() throws RuntimeException {
		
		try {
			while(true) {
				
				System.out.print("1.추가 2.제거 3.조회 4.전체목록 0.종료 >>> ");
				int choice = sc.nextInt();
				switch(choice) {
				case 1 : addCar(); break;
				case 2 : deleteCar(); break;
				case 3 : findCar(); break;
				case 4 : printAllCars(); break;
				case 0 : System.out.println("시스템종료"); return;
				default : throw new RuntimeException("Bad Request");
				}
			}
		} catch (RuntimeException e) {
			System.out.println(e.getMessage());
		}
		
	}
	
	public static void main(String[] args) {
		new ParkingLot().manage();

	}

1. 4개의 메소드가 throws RuntimeException을 통해

2. manage 메소드의 호출한 메소드(ex:case 4 : printAllCars(); 에 던져져서,

3. catch문의 RuntimeException e 의 객체한테 던져진다.

* 예외 발생 시 프로그램 종료를 안시키려면, while문의 스코프위치를 수정해 주면 된다.

	public void manage() throws RuntimeException {
		
		while(true) {
			try {		
				System.out.print("1.추가 2.제거 3.조회 4.전체목록 0.종료 >>> ");
				int choice = sc.nextInt();
				switch(choice) {
				case 1 : addCar(); break;
				case 2 : deleteCar(); break;
				case 3 : findCar(); break;
				case 4 : printAllCars(); break;
				case 0 : System.out.println("시스템종료"); return;
				default : throw new RuntimeException("Bad Request");
					}
				
			} catch (RuntimeException e) {
				System.out.println(e.getMessage());			
			} 
		}
		
	}

 

MyException 

- 사실상 사용은 잘 하지 않는다. 

1. 사용자 정의 예외 클래스

2. Exception 클래스를 상속 받는다.

3. Serializable 인터페이스 : 이 인터페이스를 구현하면 직렬화가 가능. serialVersionUID 값을 가져야 함.

       △
Throwable 클래스 : serialVersionUID 값이 필요함
       
Exception 클래스 : serialVersionUID 값이 필요함

       

MyException 클래스 : serialVersionUID 값이 필요함 

public class MyException extends Exception {

	private static final long serialVersionUID = -7774118171104436322L;

}

예시) BankException (사용자화)

public class BankException extends Exception {
	
	private static final long serialVersionUID = -6507898939727359613L;
	private int errorCode;

	public BankException(String message, int errorCode) {
		super(message);
		this.errorCode = errorCode;
	}
	public int getErrorCode() {
		return errorCode;
	}
	public void setErrorCode(int errorCode) {
		this.errorCode = errorCode;
	}
}

 

 주요예외클래스 

구분 예외클래스 예외가 발생되는 경우
Unchecked Exception ArithmeticException 산술  연산의  문제로  인해  발생(0으로  값을  나누는  경우)
ClassCastException 어떤  객체를  변환할  수  없는  클래스타입으로  변환(Casting)하는  경우에  발생
IndexOutOfBoundsException 배열의  인덱스가  범위를  벗어난  경우에  발생
NullPointerException null값을  참조하는  경우에  발생(null값으로  메소드를  호출할  때)
NumberFormatException String을  Number타입으로  변환하지  못하는  경우에  발생
Checked Exception ClassNotFoundException 클래스이름을  찾을  수  없는  경우에  발생
FileNotFoundException 파일을  찾을  수  없는  경우에  발생
NamingException 자원(Resource)의  이름을  확인할  수  없는  경우에  발생
IOExc 입출력  동작이  실패하는  경우에  발생
SQLException 데이터베이스  처리가  실패하는  경우에  발생

 

 기타사항 

- static은 static을 부를 수 있다. static이 없으면 호출이 불가능

public class Main {
	
	public static void m1() {
		
	}

	public static void main(String[] args) {
		m1();

	}

- 스코프 범위 : 어느 블록에서 선언을 했는지, 스코프 범위를 조정하라

{scope}

 

- 클래스에서 RuntimeException 실행 시, 계속해서 throws를 해서 main까지 던지면, 자바버추얼머신(JVM(Java Virtual Machine))이 처리를 한다.

 

 

반응형