예외 처리

이동주·2025년 3월 11일

JAVA

목록 보기
21/30

예외

잘못된 사용 또는 코딩으로 인한 오류

  • 수식이나 값, 배열의 범위를 벗어나는 경우 등의 이유로 프로그램을 실행할 수 없는 상황
  • 에러와 달리 예외 처리를 통해 계속 실행 상태를 유지할 수 있음

예외의 종류

  • Exception(일반 예외) :
    -> 컴파일러가 예외 처리 코드 여부를 검사하는 예외
    -> 반드시 예외처리를 해줘야함 (중요한 것)
    -> 구문 오류를 포함함!
  • Runtime Exception(실행 예외) :
    -> 컴파일러가 예외 처리 코드 여부를 검사하지 않는 예외
    -> 배열의 범위를 벗어나는 상황과 같은 예외
    -> 부모가 해당 예외이면 예외 처리 안해줘도 됨
    -> 논리 오류를 포함함!

예외 처리

  • 예외 발생 시 프로그램의 갑작스러운 종료를 막고 프로그램의 정상 실행을 유지할 수 있게 처리하는 코드
  • try - catch - finally의 형태로 구성
  • try에서 예외를 던져줌 (예외가 발생할 수 있는 구문들을 넣어줌)
  • catch에서 예외를 잡아서 처리하고 프로그램을 계속 실행함
  • catch를 부모로 두면 예외를 다 잡아줌
  • finally는 예외 발생과 관계가 없는 블록으로 생략 가능

구조

try{
	// 예외가 발생할 수 있는 구문
}

catch(예외클래스 e) {
	//예외 처리
}

finally {
	// 예외와 관계없이 실행
}

예외 처리

package ch11.sec02.exam01;

public class ExceptionHandlingExam1 {
	public static void printLength(String data) {
		try {
			int result = data.length();
			System.out.println("문자 수: " + result);
		}
		// try : 예외가 발생할 수 있는 구문을 넣고 예외를 던짐
		// try에서 예외가 발생하면 catch로 이동, 그렇지 않으면 catch를 건너뜀
		
		catch(NullPointerException e) {
		// NullPointerException 예외가 발생했을 때 아래 구문 실행
		// NullPointerException 객체를 생성해주고 참조변수를 선언해주기
			
			System.out.println(e.getMessage());
			// .getMessage() : 특정 오류 문구를 출력하는 구문
			
			System.out.println(e.toString());
			// .toString() : 예외 클래스와 특정 오류 문구를 출력하는 구문
			
			e.printStackTrace();
			// .printStackTrace() : 함수의 실행 과정을 모두 출력하는 구문
		}
		// catch: try에서 받은 예외 사항을 처리하는 블록
		// 예외가 없으면 건너뜀
		
		finally {
			System.out.println("[마무리 실행]\n");
		}
		// finally : catch를 실행되고 마지막에 실행하는 구문
		// 예외의 영향을 받지 않는 구문으로 생략해도 됨
		
	}
	
	public static void main(String[] args) {
		System.out.println("[프로그램 시작]\n");
		printLength("ThisisJava");
		printLength(null);
		// 문자열 자체가 null이면 문자열 개수를 셀 수 없어서 NullPointerException 오류 발생
		System.out.println("[프로그램 종료]");
	}
}

예외 처리 확인 구문

  • .getMessage() : 특정 오류 메세지를 출력
  • .toString() : 예외 클래스명과 특정 오류 메세지를 출력
  • .printStackTrace() : 함수의 호출 현황을 모두 출력

예외 클래스

  • NullPointerException : null 값의 예외를 잡아주는 하나의 클래스!
    -> NullPointerException가 어떻게 만들어 졌는지
    java.lang.Object(최상위 클래스) > java.lang.Throwable > java.lang.Exception > java.lang.RuntimeException > java.lang.NullPointerException
  • java.lang.NumberFormatException : 숫자 형식이 아닌 예외를 잡는 클래스
  • java.lang.ArrayIndexOutOfBoundsException : 배열의 범위를 벗어난 예외를 잡는 클래스
package ch11.sec02.exam02;

public class ExceptionHandlingExam {
	public static void main(String[] args) {
		
		// try: 예외가 생기면 catch에 던짐
		try { 
			Class.forName("java.lang.String");
			// forName() : Class 이름을 찾는 메소드
			System.out.println("Java.lang.String 클래스가 존재함");
		}
		
		// catch: try 구문 내부에서 예외가 발생하면 실행
		// ClassNotFoundException : 클래스가 존재하지 않을 때 출력됨
		catch(ClassNotFoundException e) {
			e.printStackTrace();
			// 함수의 실행을 모두 출력하는 구문
		}
		
		System.out.println();
		
		try {
			Class.forName("java.lang.String2");
			System.out.println("java.lang.String2 클래스가 존재함");
		}
		catch(ClassNotFoundException e){
			// java.lang.String2 클래스는 존재하지 않기 때문에
			// ClassNotFoundException 예외 발생
			e.printStackTrace();
		}
	}
}

다중 catch로 예외 처리

  • catch 블록이 여러 개 생성되더라도 catch 블록은 단 하나만 실행됨
  • 따라서 catch 블록을 서로 다른 예외 클래스를 넣어 실행하여 예외를 잡을 수 있음
  • 처리해야 할 예외 클래스들이 상속 관계에 있을 때는 하위 클래스 catch 블록을 먼저 작성하고 상위 클래스 catch 블록을 작성하는 것이 원칙
  • 하지만 상위 클래스의 예외 클래스를 먼저 작성할 경우 상위 클래스에서 하위 클래스의 예외 처리까지 모두 진행함
  • 따라서 편하게 Exception으로 예외 처리하는 것이 좋음
package ch11.sec03.exam01;

public class ExceptionHandlingExam {
	public static void main(String[] args) {
		String[] array = {"100", "1oo"};
		// 배열 선언
		
		for(int i=0;i<array.length;i++) {
			try {
				int value = Integer.parseInt(array[i]);
				// 배열 안의 값들을 숫자로 변환
				// 하지만 1oo은 숫자가 아니기 때문에 예외 발생
				
				System.out.println("array[" + i + "]: " + value);
			}
			
			catch(ArrayIndexOutOfBoundsException e) {
				System.out.println("배열 인덱스가 초과됨: " + e.getMessage());
			}
			// ArrayIndexOutOfBoundsException
			// 배열 인덱스가 초과됐을 때 나오는 예외 클래스
			
			catch(NumberFormatException e) {
				System.out.println("숫자로 변환할 수 없음: " + e.getMessage());
			}
			// NumberFormatException
			// 숫자가 아닐 때 나오는 예외 인덱스
			
		}
	}
}
package ch11.sec03.exam02;

public class ExceptionHandlingExam {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String[] array = {"100", "1oo"};
		for(int i=0;i<=array.length;i++) {
			try {
				int value = Integer.parseInt(array[i]);
				System.out.println("array[" + i + "]: " + value);
			}
			catch(ArrayIndexOutOfBoundsException e){
				System.out.println("배열 인덱스가 초과됨: " + e.getMessage());
			}
			// 배열 범위 초과 시 예외처리
			
			catch(Exception e) {
				System.out.println("실행에 문제가 있습니다.");
			}
			// 예외 클래스 중 최상위 클래스
			// 사실상 모든 예외 처리가 가능하기 때문에 Exception으로 예외 잡는게 좋음
			
			// 다중 catch문 작성 시 상위 예외 클래스가 하위 예외 클래스의
			// 아래에 배치해야함
		}
	}

}
package ch11.sec03.exam03;

public class ExceptionHandlingExample {
	public static void main(String[] args) {
		String[] array = { "100", "1oo", null, "200"};
		
		for(int i=0;i<=array.length;i++) {
			
			// try : 예외 처리가 발생할 수 있는 구문 넣기
			try {
				int value = Integer.parseInt(array[i]);
				System.out.println("array[" + i + "]: " + value);
			}
			
		
			catch(ArrayIndexOutOfBoundsException e) {
				System.out.println("배열 인덱스가 초과됨: " + e.getMessage());
			}
			
			// | (OR) 연산자를 사용하여 예외 클래스의 연산을 통해
			// 두 개 이상의 예외 처리를 동시에 진행함
			catch(NullPointerException | NumberFormatException e) {
				System.out.println("데이터에 문제가 있음: " + e.getMessage());
			}
		}
	}
}

리소스

  • 데이터를 제공하는 객체로 리소스를 사용하기 위해 열고, 사용이 끝나면 닫아야함

  • 리소스를 사용하다가 예외가 발생될 경우에도 안전하게 닫는 것이 중요함

  • try - with - resource 블록을 사용하면 예외 발생 여부와 상관없이 자동으로 닫아줌

  • Autocloseable : 리소스를 자동으로 해제할 수 있도록 도와주는 인터페이스

package ch11.sec04;

public class MyResource implements AutoCloseable{
	// AutoCloseable :  리소스를 자동으로 닫을 수 있도록 도와주는 인터페이스
	// close 메소드를 호출해줘야함
	
	private String name;
	
	public MyResource(String name) {
		this.name = name;
		System.out.println("[MyResource(" + name + ") 열기]");
	}
	
	public String read1() {
		System.out.println("[MyResource(" + name + ") 열기]");
		return "100";
	}
	
	public String read2() {
		System.out.println("[MyResource(" + name + ") 열기]");
		return "abc";
	}
	
	@Override
	public void close() throws Exception {
		System.out.println("[MyResource(" + name + ") 닫기]");
	}
	
}
package ch11.sec04;

public class TryWithResourceExample {
	public static void main(String[] args) {
		try (MyResource res = new MyResource("A")){
			String data = res.read1();
			int value = Integer.parseInt(data);
		}
		catch(Exception e) {
			System.out.println("예외 처리: " + e.getMessage());
		}
		
		System.out.println();
		
		try (MyResource res = new MyResource("A")){
			String data = res.read2();
			int value = Integer.parseInt(data);
		}
		catch(Exception e) {
			System.out.println("예외 처리: " + e.getMessage());
		}
		
		System.out.println();
		
		MyResource res1 = new MyResource("A");
		MyResource res2 = new MyResource("B");
		
		try (res1; res2){
			String data1 = res1.read1();
			String data2 = res2.read1();
		}
		catch(Exception e) {
			System.out.println("예외 처리: " + e.getMessage());
		}
	}
}

예외 떠넘기기

  • 메소드 내부에서 예외 발생 시 throws 키워드 이용해 메소드를 호출한 곳으로 예외 떠넘기기
  • throws는 메소드 선언부 끝에 작성. 떠넘길 예외 클래스를 쉼표로 구분하여 나열
  • 나열할 예외 클래스가 많으면 throws Exception 또는 throws Throwable만으로 모든 예외 떠넘기기가 가능
package ch11.sec05;

public class ThrowsExample1 {
	public static void main(String[] args) {
		try {
			findClass();
		} catch(ClassNotFoundException e) {
			System.out.println("예외 처리: " + e.toString());
		}
	}

	public static void findClass() throws ClassNotFoundException {
		Class.forName("java.lang.String2");
	}
}
package ch11.sec05;

public class ThrowsExample2 {
	public static void main(String[] args) throws Exception {
		findClass();
	}

	public static void findClass() throws ClassNotFoundException {
		Class.forName("java.lang.String2");
	}
}

사용자 정의 예외

  • 표준 라이브러리에는 없어 직접 정의하는 예외 클래스
  • 일반 예외는 Exception의 자식 클래스로 선언
  • 실행 예외는 RunTime Exception의 자식 클래스로 선언

예외 발생시키기

  • throw 키워드와 함께 예외 객체를 제공해 사용자 정의 예외를 직접 코드에서 발생시킬 수 있음
  • 예외의 원인에 해당하는 메세지를 제공하려면 생성자 매개값으로 전달
package ch11.sec06;

public class Account {
	private long balance;
	
	public Account() { }

	public long getBalance() {
		return balance;
	}
	public void deposit(int money) {
		balance += money;
	}
	public void withdraw(int money) throws InsufficientException {
		if(balance < money) {
			throw new InsufficientException("잔고 부족: "+(money-balance)+" 모자람");
		}
		balance -= money;
	}
}
package ch11.sec06;

public class AccountExample {
	public static void main(String[] args) {
		Account account = new Account();
		//예금하기
		account.deposit(10000);
		System.out.println("예금액: " + account.getBalance());

		//출금하기
		try {
			account.withdraw(30000);
		} catch(InsufficientException e) {
			String message = e.getMessage();
			System.out.println(message);
		}
	}
}
package ch11.sec06;

public class InsufficientException extends Exception {
	public InsufficientException() {
	}

	public InsufficientException(String message) {
		super(message);
	}
}
profile
끄작끄작

0개의 댓글