문법/기본 문법

예외처리

코딩 화이팅 2023. 1. 30. 14:10

Error

  • 메모리 부족, stack overflow 와 같이 일단 발생하면 복구할 수 없는 상황->코드를 써서 해결 불가능
  • 프로그램의 비정상적 종료를 막을 수 없음 -> 디버깅 필요

Exception

  • 읽으려는 파일이 없거나, 네트워크 연결이 안 되는 등 수습될 수 있는 비교적 상태가 약한 것들
  • 프로그램 코드에 의해 수습될 수 있는 상황

예외<->에러

  • 둘 다 어떤 원인에 의해 오동작하거나 비정상적으로 종료되는 경우
  • 코드를 통해서 프로그램의 비정상적 종료를 막을 수 있는가
  • 프로그래밍을 통해서 대체가 가능한가? 그럴 필요가 있는가? 프로그램의 관심사인가?

예외처리

  • 예외 발생 시 프로그램의 비정상 종료를 막고 정상적인 실행 상태를 유지하는 것
  • 예외의 감지 및 예외 발생 시 동작할 코드 작성 필요
  • 쓰는 이유? : 주된 관심사와(메서드의 본래의 기능) 보조관심사 분리 / 미리 발생할 수 있는 문제점(메서드에 throws 발생할 수 있는 예외)을 알려주는 효과 / 메서드 잘못 사용 방지 ex)Integer.parsent("문자열")->정수

Checked exception(Exception 클래스의 상속(RuntimeException 상속 X))

예외에 대한 대처 코드가 없다면 컴파일이 진행되지 않음(강제)

Unchecked exception(RuntimeException의 하위 클래스)

예외에 대한 대처 코드가 없더라도 컴파일은 진행됨.

package test01;

public class test1 {
	public static void main(String[] args) {
		int[] nums= {10};//인덱스는 0뿐
		
		//java.lang.ArrayIndexOutOfBoundsException
		System.out.println(nums[1]);
		//java.lang.ArrayIndexOutOfBoundsException: 1
		System.out.println(nums[-1]);//오버플로우가 발생할 때 음수가 나올 수 있다->int보다는 long
		//java.lang.ArrayIndexOutOfBoundsException: -1
				
		int num=5/0;
		//java.lang.ArithmeticException: / by zero
		
		int num=Integer.parseInt("ssafy");
		//java.lang.NumberFormatException
		
		예외는 언제 발생? throw 키워드 사용해서
		new 예외생성자()->예외 객체
		예외 객체를 던질 때 발생
		예외가 발생? 내부적으로 예외 객체가 생성돼서 던져진 것
//		throw new ArrayIndexOutOfBoundsException(); //nums[2]
//		throw new ArithmeticException();
//		throw new NumberFormatException();
		
	
		
	}
}

예외 처리 키워드

  • 직접 처리
    • try
    • catch
    • finally
try {//try 문 안에서 예외처리가 난다면
			throw new Exception();
		} catch (Exception e) {//catch문 안에 있는 메소드 출력
			System.out.println("예외를 처리합니다.");
		}
		System.out.println("프로그램 끝.");
		//예외를 처리합니다.
//		프로그램 끝.
============================================================================
package test01;

public class test2 {
	public static void main(String[] args) {
		//try~catch 문 사용
		int[] nums={10};
		try {
			System.out.println(nums[1]);
		//발생한 예외 : ArrayIndexOutOfBoundsExceqtion
//		}catch(ArrayIndexOutOfBoundsException e) {//ArrayIndexOutOfBoundsExceqtiond이거나, 이 클래스의 자식 객체만
		}catch(Exception e) {//어떤 예외든 처리 가능	
			//catch가 잡을 수 있는 예외 객체
			//catch() 안에 온느 예외 클래스는
			//그 예외 클래스이거나,  그 클래스의 자식 클래스인 경우
			System.out.println("배열의 크기가 넘어가면 대처 코드");
		}
		System.out.println("프로그램이 정상 종료");
		}
	배열의 크기가 넘어가면 대처 코드
	프로그램이 정상 종료
	}
	try {
			int num=Integer.parseInt("ssafy");
		}catch(ArithmeticException e) {
			System.out.println("해당 문자열은 정수로 바꿀 수 없어요");
		}catch(ArrayIndexOutOfBoundsException e) {
			System.out.println("인덱스 범위가 넘어갔네요");
		}catch(Exception e) {//모든 예외를 처리할 수 있는 블록
			//다형성이 적용.
			System.out.println("모든 예외처리 가능");
		}
		//모든 예외처리 가능
	}
}
=======================================================================
package test01;

public class test4 {
	public static void main(String[] args) {
		//try ~ catch ~ catch
		try {
			int num=Integer.parseInt("ssafy");
			//다중 예외처리를 할 때는 순서가 중요
			//자식 예외를 먼저 검사하고, 나중에 큰 예외를 검사
			
		}catch(Exception e) {//모든 예외를 처리할 수 있는 블록
			//다형성이 적용.
			System.out.println("모든 예외처리 가능");
		}catch(ArithmeticException e) {
			System.out.println("해당 문자열은 정수로 바꿀 수 없어요");
		}catch(ArrayIndexOutOfBoundsException e) {
			System.out.println("인덱스 범위가 넘어갔네요");
	}
		//error
}
}
package test01;

public class test5 {
	public static void main(String[] args) {
		//try ~ catch ~ catch
		try {
			int num=Integer.parseInt("ssafy");
		}catch(ArithmeticException e) {
			System.out.println("해당 문자열은 정수로 바꿀 수 없어요");
		}catch(ArrayIndexOutOfBoundsException e) {
			System.out.println("인덱스 범위가 넘어갔네요");
		}
		//해당하는 catch 블록을 만나지 못한다면
		//예외는 처리되지 않는다.
		
//		}
	}
}
======================================================================
package test01;

public class test6 {
	public static void main(String[] args) {
		//try ~ catch ~ catch
		
		try {
			int num=Integer.parseInt("ssafy");
		//|연산자를 이용해서, 하나의  catch 블록에서
		//다양한 종류의 예외를 동시에 처리 가능
		}catch(ArithmeticException |ArrayIndexOutOfBoundsException| NumberFormatException e) {
			System.out.println("하나의 블록에서 세 가지 예외를 처리합니다.");
		}
		//하나의 블록에서 세 가지 예외를 처리합니다.
	}
}
  • 간접 처리
    • throws
  • 사용자 정의 예외 발생시킬 때
    • throw

try~catch 문에서의 흐름

  • try 블록에서 예외가 발생하면 JVM이 해당 Exception 클래스의 객체 생성 후 던짐(throw) : throw new XXException()
  • 던져진 exception을 처리할 수 있는 catch 블록에서 받은 후 처리 (적당한 catch 블록을 만나지 못하면 예외처리는 실패)
  • 정상적으로 처리되면 try-catch 블록을 벗어나 다음 문장 진행
  • try 블록에서 어떠한 예외도 발생하지 않은 경우 catch문을 거치지 않고 try-catch 블록의 다음 흐름 문장을 실행 

다중 exception handling

  • try 블록에서 여러 종류의 예외가 발생할 경우
  • 하나의 try 블록에 여러 개의 catch 블록 추가 기능(예외 종류별로 catch 블록 구성)

다중 catch 문장 작성 순서 유의 사항

  • JVM이 던진 예외는 catch 문장을 찾을 때는 다형성이 적용됨.(그 블록은 그 클래스 또는 그 클래스의 자식)
  • 상위 타입의 예외가 먼저 선언되는 경우 뒤에 등장하는 catch 블록은 동작할 기회가 없음
  • 상속 관계가 없는 경우는 무관
  • 상속 관계에서는 작은 범위(자식)에서 큰 범위(조상)순으로 정의
package test01;

public class test7 {
	public static void main(String[] args) {
		//try catch finally
		
		try {
			String str="1234";
			System.out.println("code 1 - before pare : "+str);
			int num = Integer.parseInt(str);
			System.out.println("code 2 - after pare : "+str);
		}catch(Exception e) {
			System.out.println("code 3 - exception handling을 완료");
		}finally {
//			//언제나 실행 된다.
			System.out.println("code 4 - 언제나 실행? O");
//		}	
		System.out.println("code 5 - 언제나 실행? X");
		System.out.println("프로그램의 끝");
//		code 1 - before pare : ssafy
//		code 3 - exception handling을 완료
//		code 4 - 언제나 실행? O
//		code 5 - 언제나 실행? X
//		프로그램의 끝
try {
			String str="1234";
			System.out.println("code 1 - before pare : "+str);
			int num = Integer.parseInt(str);
			System.out.println("code 2 - after pare : "+str);
		}catch(Exception e) {
			System.out.println("code 3 - exception handling을 완료");
		}finally {
//			//언제나 실행 된다.
			System.out.println("code 4 - 언제나 실행? O");
		}
		System.out.println("code 5 - 언제나 실행? X");
		System.out.println("프로그램의 끝");
//		code 1 - before pare : 1234
//		code 2 - after pare : 1234
//		code 4 - 언제나 실행? O
//		code 5 - 언제나 실행? X
//		프로그램의 끝
try {
			String str="1234";
			System.out.println("code 1 - before pare : "+str);
			int num = Integer.parseInt(str);
			System.out.println("code 2 - after pare : "+str);
			return;
		}catch(Exception e) {
			System.out.println("code 3 - exception handling을 완료");
		}finally {
//			//언제나 실행 된다.
			System.out.println("code 4 - 언제나 실행? O");
		}
		System.out.println("code 5 - 언제나 실행? X");
		System.out.println("프로그램의 끝");
//		code 1 - before pare : 1234
//		code 2 - after pare : 1234
//		code 4 - 언제나 실행? O
try {
			String str="ssafy";
			System.out.println("code 1 - before pare : "+str);
			int num = Integer.parseInt(str);
			System.out.println("code 2 - after pare : "+str);
		}catch(Exception e) {
			System.out.println("code 3 - exception handling을 완료");
			return;
		}finally {
			//언제나 실행 된다.
			System.out.println("code 4 - 언제나 실행? O");
		}
		//언제나 실행이 되지 않는다.
		//try , catch, finally 블록 안에 return이 있다면
		//메인 메서드는 그 순간 종료되므로.
		System.out.println("code 5 - 언제나 실행? X");
		System.out.println("프로그램의 끝");
		//code 1 - before pare : ssafy
//		code 3 - exception handling을 완료
//		code 4 - 언제나 실행? O
			
		//finally가 필요한 이유?
		//DB 연결하는 상황
		//연결을 종료할 때, 관련된 리소스들을 정리할 필요
		//파일 입출력 - 입출력 stream, 관련된 리소스들을 정리

try~catch~finally구문

  • finally는 예외 발생 여부와 상관 없이 언제나 실행
  • 중간에 return을 만나는 경우도 finally 블록을 먼저 수행 후 return 실행
package test02;

import java.io.FileNotFoundException;
import java.io.FileReader;

public class test1 {
	public static void main(String[] args) {
		method1("123");
		try {
			method2("input.txt");
		} catch (FileNotFoundException e) {
			System.out.println("CheckedException이니까 반드시 처리");
		}
	}
	
	public static void method1(String str) {
		//numberformatexception : uncheckedexception
		//굳이 예외처리 하지 않아도 컴파일은 됨.
		//강제 사항 X
		int num=Integer.parseInt(str);
	}
	
	//throws 키워드를 사용해서
	//내 메서드 안에서 일어날 수 있는 예외를
	//나를 호출한 곳으로 전가시킴
	public static void method2(String filename) throws FileNotFoundException{
		//filenotfoundexcepion : checked exception ->컴파일이 되지 않음
		//강제 사항
		//try catch 또는 throws
		FileReader reader=new FileReader(filename);
	}
}

throws 키워드를 통한 처리 위임

  • method에서 처리해야 할 하나 이상의 예외를 호출한 곳으로 전달(처리 위임)
  • 예외가 없어지는 것이 아니라 단순히 전달됨
  • 예외를 전달받은 메서드는 다시 예외 처리의 책임 발생
  • 처리하려는 예외의 조상 타입으로 throws  처리 가능