17. 예외 (Exception) [ JAVA ]

duck-ach·2022년 8월 4일
0

JAVA

목록 보기
17/27
post-custom-banner

예외

JAVA에는 대표적으로 두가지의 에러가 존재하는데 ErrorException 두 가지가 있다.

  • Error : 컴퓨터의 메모리가 부족하는 등 코드의 문제가 아닌 시스템에 비정상적인 상황이 발생한 오류를 주로 말한다. 보통 잘 일어나지 않지만 일어나더라도 개발자가 코드로 해결할 수 없는 영역이다.
  • Exception : 사용자의 잘못된 조작이나 개발자의 코딩 실수로인해 발생하는 프로그램 오류이다. 대부분 코드를 수정하면 고칠 수 있다. (Exception 에러를 띄울때는 에러라고 표현하지않고 예외라고 표현한다.)

JAVA의 예외

자바의 예외에는 일반예외(Checked Exception)실행예외(Unchecked Exception)으로 부를 수 있다.

  • 일반예외의 경우 개발자가 반드시 예외처리를 직접 진행해야 한다. (예외처리를 하지 않으면 실행되지 않는다. 그래서 Checked Exception이라고 부른다.)
  • 실행예외의 경우는 개발자가 예외처리를 직접하지 않아도 된다. 명시적인 예외처리가 강제되는 것이 아니라서 unchecked라고 부른다. RuntimeException을 상속받는다.
예외일반예외 (Checked Exception)실행예외(Unchecked Exception)
정의Exception의 상속받는 하위 클래스 중 RuntimeException을 제외한 모든 ExceptionRuntimeException을 상속받는 모든 Exception
처리여부필수생략가능 (필요에 따라 처리)
확인시점Compile Time(컴파일시점), 이미 컴파일 시점 에러가 표시되기 때문에 확인하여 예외처리를 할 수 있다.Runtime(실행시점), 컴파일 이후 런타임 도중 예외를 확인할 수 있다. (로직상으로는 생략이 가능하지만 미리 막아두는 것이 좋다.)
트랜잭션예외 발생 시 롤백(rollback)을 진행하지 않는다.예외 발생 시 롤백(rollback)을 진행한다.

실행 예외(Unchecked Exception)의 종류

자주 출몰하는 대표적인 실행예외들을 소개해보겠다.

🐳 NullPointerException

  • 객체 참조가 없는 상태일 때 발생한다.
  • null값을 갖는 참조변수로 객체 접근 연산자인 도트(.)를 사용했을때 발생한다.
  • 객체가 없는 상태에서 객체를 사용할 때 발생한다.
public class Main {
	// static은 static을 부를 수 있다.
	// non-static인 경우 아직 떳떳한 클래스/메소드로써 태어나지 않은거다.
	public static void m1() {
		
		// NullPointerException : Null값이 어떤 메소드를 호출할 때 발생한다.
		String[] hobbies = new String[5];
        // hobbies[0]에 값을 할당해주지않아서 Null값인 상태
		hobbies[1] = "수영";
		hobbies[2] = "골프";
		hobbies[3] = "영화";
		hobbies[4] = "집콕";

		for(int i = 0; i < hobbies.length; i++) {
			if(hobbies[i].equals("수영")) {
				System.out.println("취미가 나와 같군요");
			}
		}
	}
	
	public static void main(String[] args) {
		
		m1();
		
	}

}


사진을 보면 Exception의 종류와함께 Exception이 일어난 곳을 추적(trace) 해서 알려준다.

⭐ 해결

public class Main {

	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("취미가 나와 같군요");
			}
		}
	}
	
	public static void main(String[] args) {
		
		m2();
		
	}

}

🐳 NumberFormatException

문자열로 되어있는 데이터를 숫자로 변경하는 경우가 많은데, 매개값인 문자열이 숫자로 변환될 수 있다면 숫자를 정상적으로 리턴하지만, 숫자로 변환할 수 없는 문자열이 포함되어있으면 발생한다.

import java.util.Scanner;

public class Main {
	
	
	public static void m1() { }
	public static void m2() { }
	public static void m3() { 
		//NumberFormatException : String을 Number타입으로 변경할 때 발생
		
		Scanner sc = new Scanner(System.in);
		
		System.out.print("이름 입력(필수) >>> ");
		String name = sc.nextLine();
		
		System.out.print("나이 입력(선택) >>> ");
		String strAge = sc.nextLine(); // 입력없이 Enter만 누르면 strAge는 빈 문자열을 가진다.
		int age = Integer.parseInt(strAge);
		System.out.println("이름 : " + name + ", 나이 : " + age + "살");
		
	}
	
	public static void main(String[] args) {
		
		m3();
		
		
	}

}

나이 입력을 공백란으로 두었을 때 이런 예외가 발생한다.

⭐해결

import java.util.Scanner;

public class Main {
	
	
	public static void m1() { }
	public static void m2() { }
	public static void m3() { 
		//NumberFormatException : String을 Number타입으로 변경할 때 발생
		
		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 + "살");
		
	}
	
	public static void main(String[] args) {
		
		m3();
		
		
	}

}

🐳 ArrayIndexOutOfBoundsException

배열에서 인덱스 범위를 초과하여 사용할 때 발생합니다.

public class Main {
	public static void main(String[] args) {
    
    int[] arr = new int[3];
    arr[0] = 1
    arr[1] = 2
    arr[2] = 3
    arr[3] = 4  // 배열의 크기는 3인데 4번째 index까지 사용하여 예외발생
    }
}

🐳 ArithmeticException

예외적인 산술 조건이 발생하면 발생한다. 예를 들어 정수 0으로 나누기를 실행하면 주로 발생한다.

	public static void m3() {
		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));
		
	}

🐳 InputMismatchException

정수를 입력해야 하는데 문자나 실수를 입력한 경우 예외 발생 (데이터타입을 다르게입력)

	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 + "=" + (double)(a / b));
			System.out.println(a + "%" + b + "=" + (double)(a % b));
		} catch (ArithmeticException e) {
			System.out.println("ArithmeticException이 발생했다.");
		} 
	}

이상태에서 문자열을 아무거나 입력한 경우 발생한다.


🐳 ArrayStoreException

객체배열에 배열 유형이 허락하지 않는 객체를 저장하려는 경우 발생한다.


🐳 ArrayIndexOutOfBoundsException

배열을 참조하는 인덱스가 잘못된 경우 발생한다.


🐳 ClassCastException

Class의 형변환를 적절하지 못하게 할 경우


🐳 NegativeArraySizeException

배열의 크기가 음수인 경우


🐳 NoClassDefFoundException

클래스를 찾을 수 없는 경우


🐳 OutOfMemoryException

사용 가능한 메모리가 없는 경우


🐳 IndexOutOfBoundsException

객체의 범위를 벗어난 색인(Index)를 사용하는 경우


🐳 Illegalargumentexception

메서드에 유형이 일치하지 않는 매개변수를 전달하는 경우


🐳 IllegalMonitorStateException

스레드가 스레드에 속하지 않는 객체를 모니터 하려고 기다리는 경우


🐳 IllegalStateException

적절하지 않은 때에 메서드를 호출하는 경우


일반 예외(Checked Exception)의 종류

🐰 IOException

  • 입출력 스트림과 관련한 명령어를 사용할 때 사용하는 예외처리이다.
  • 입출력 동작이 실패하는 경우에 발생한다.

🐰 ClassNotFoundException

클래스 이름을 찾을 수 없는 경우에 발생한다.


🐰 SQLException

데이터베이스 처리가 실패하는 경우에 발생한다.


🐰 FileNotFoundException

파일을 찾을 수 없는 경우에 발생한다.


🐰 NamingException

자원(Resource)의 이름을 확인할 수 없는 경우에 발생한다.


예외처리코드

예외처리(Exception Handling) 하는것에 대해 공부해보겠다.

📕Try-Catch문

예외가 발생했을때 예외를 해결할 수 있는 코드를 지정해줄 수 있다.

try{
	// 예외가 발생할 가능성이 있는 코드 작성
}
catch(예외클래스 e) {
	// 예외를 처리할 문장
}

📕다중 Catch문

발생하는 예외별로 다중 catch문을 구성할 수 있다.
Catch가 여러개 나오면 위에서부터 순서대로 방문한다.

  • 주의할 점 : 상위 예외 클래스가 하위 예외 클래스보다 밑에 위치하게 작성해야 한다.
try {
	//예외 1이 발생할 가능성이 있는 코드
    //예외 2가 발생할 가능성이 있는 코드
}
catch(예외1 e) {
	//예외1을 발생했을 때 처리할 코드
}
catch(예외2 e) {
	//예외2가 발생했을 때 처리할 코드
}

NullPointerException과 Try-Catch문

public class Main {

	public static void m1() {
		try {
			String[] hobbies = new String[3];
			hobbies[1] = "swimming";
			hobbies[2] = "running";
			
			for(String hobby : hobbies) {
				System.out.println(hobby.substring(0, 2));
			}
		} catch (NullPointerException e) { // RuntimeException, NullPointerException 가능
			System.out.println("NullPointerException 발생");
		}
	}
	
	public static void main(String[] args) {
		
		m1();

	}

}

RuntimeException을 이용한 NullPointerException 해결

public class Main {

	public static void m1() {
		try {
			String[] hobbies = new String[3];
			hobbies[1] = "swimming";
			hobbies[2] = "running";
			
			for(String hobby : hobbies) {
				System.out.println(hobby.substring(0, 2));
			}
		} catch (RuntimeException e) { // RuntimeException, NullPointerException 가능
			System.out.println("NullPointerException 발생");
		}
	}
	
	public static void main(String[] args) {
		
		m1();

	}

}

📕Try-Catch-Finally문

finally는 자원을 반납할 때 주로 사용한다.

try {
	//예외가 발생할 가능성이 있는 코드
}
catch(예외클래스 e) {
	//예외처리;
}
finally {
	//무슨일이 있든 항상 실행
}

아래 예제는 Scanner를 닫아주는 것을 표현해보았다.
실제로 Scanner는 close()처리로 닫아주지않아도 정상적으로 잘 실행이 되지만, 다른메소드들 중에서는 close()처리를 안해주면 데이터를 손실낼 수도 있는 것도 많기 때문에 주의해야한다.

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// fanally 블록
		// 1. try-catch문 마지막에 추가하는 블록
		// 2. 언제나 마지막에 실행되는 블록
		Scanner sc = new Scanner(System.in);
		
		try {
			
			System.out.print("나이 입력 >>> "); // null이나 다른데이터타입을 입력하면 예외가 발생한다.
			String strAge = sc.nextLine();
			int age = Integer.parseInt(strAge);
			System.out.println(age >= 20 ? "성인" : "미성년자");
//			sc.close();  // 13번에서 예외가 발생했을 경우 바로 catch문으로 가기 때문에 닫지못한다.
		} 
		catch(Exception e) {
			System.out.println("예외 발생");
		}
		finally {
			sc.close();
			System.out.println("finally 블록 실행");
		}
	}

}

만약 예외가 발생한다면 그 즉시 바로 catch문으로 가기 때문에 닫지못하기 때문에 예외와 상관없이 꼭 실행해야 하는 코드를 finally안에 배치한다.

📕throw문

메소드 내에서 직접 에러를 처리하지 않고, 해당 메소드를 호출한 쪽으로 예외처리를 던져 호출한 쪽에서 예외처리를 하게 회피하는 방식이다.

  • 예외 객체를 만들어서 직접 throw 할 수 있다.
  • 자바는 예외로 인식하지 않지만 실제로는 예외인 경우 주로 사용한다.

아래는 나이에 따른 신분을 출력하는 문장이다. 하지만 20000을 입력하더라도 JAVA는 오류를 띄우지 않을 것이다. 그 부분을 throw문으로 처리해보았다.

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {

		Scanner sc = new Scanner(System.in);
		
		try {			
			System.out.print("나이 입력 >>> "); // null이나 다른데이터타입을 입력하면 예외가 발생한다.
			String strAge = sc.nextLine();
			int age = Integer.parseInt(strAge);
			if(age < 0 || age > 100) {
				throw new RuntimeException("나이는 0 이상 100 이하만 가능합니다."); // 예외 객체 생성.
			} // throw가 던지면 catch가 받는다.
			System.out.println(age >= 20 ? "성인" : "미성년자");
//			sc.close();  // 13번에서 예외가 발생했을 경우 바로 catch문으로 가기 때문에 닫지못한다.
		} 
		catch(Exception e) {
			System.out.println(e.getMessage());
		}
		finally {
			sc.close();
			System.out.println("finally 블록 실행");
		}
	}

}

📕throws문

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

  • 메소드 내부에 try-catch문을 두고 직접 처리하는 방식
  • 메소드 외부로 예외를 던져서 메소드를 호출하는 곳에서 try-catch문으로 처리하는 방식
  • 2개 이상의 예외를 명시할 수 있기 때문에 throw가 아닌 throws 라고 한다.

메소드 내부에서 예외를 처리

public class ThrowEx {
	public static void method() {
    	try{
    		// 메소드 내부 코드
    	} catch {
    		// 예외 처리 코드
        }
    }
    
    public static void main(String[] args) {
    	method();
    }
}

메소드 외부로 예외를 던지고, 호출영역에서 예외를 처리

public class ThrowEx {
	public static void method() throws Exception {
    		// 메소드 내부 코드
    }
    
    public static void main(String[] args) {
        try{
    		method();
    	} catch {
    		// 예외 처리 코드
        }
    }
}
profile
자몽 허니 블랙티와 아메리카노 사이 그 어딘가
post-custom-banner

0개의 댓글