4/23 예외처리

박세현·2024년 4월 23일

JAVA

목록 보기
11/22
post-thumbnail

예외처리

목적 : 서비스 중단을 막기 위해서

  • 예외는 항상 일어날 수 있음 그러나 예외상황이 일어나도 서비스가 중단되면 안됨
  • 예외처리를 하면 에러가 나도 서비스는 중단되지 않음
  • 예외가 발생하면 프로그램 중단! -> 프로그램 중단을 막기 위한 조치


1. 오류와 예외

  • 오류(Error) : 시스템의 오류, JVM 오류 ... : 통제 불가 오류
  • 예외(Exception) : 코드 상의 오류 : 통제 가능한 오류
    • 버그



2. 예외 클래스의 종류

	                        Throwable 
		  
	Error(시스템적문제)                       Exception(코드적 문제)
    
                                 RuntimeException⭕     RuntimeException❌

1. Throwable

  • 모든 예외의 가장 상위 클래스
    • Error(시스템적문제) : Throwable의 하위 클래스
    • Exception(코드적 문제) : Throwable의 하위 클래스

1) Error(시스템적문제)

  • Throwable의 하위 클래스

2) Exception(코드적 문제)

  • Throwable의 하위 클래스
    • RuntimeException⭕ : Exception의 하위 클래스
    • RuntimeException❌

참고) Exception을 바로 상속 받은 예외 클래스
예) java.io.IOException / 파일을 읽을때, 쓸때 (FileInputStream, FileOutputStream)


2-1) RuntimeException❌

  • 예) java.io.FileNotFoundException : 해당파일이 없을 때 발생
  • 예외있든 없든 처리가 안되어 있으면 컴파일 X
  • 예외의 체크는 컴파일시 체크, 예외가 있으면 컴파일 X
  • 예외가 발생하든 안하든 반드시 적절한 예외 처리가 필요한 예외
  • 엄격한 예외, 형식을 매우 중시

2-2) RuntimeException⭕

  • 예) java.lang.ArithmethicException : 0으로 나눌때 발생
    -> RuntimeException을 중간에 상속 받은 예외 클래스 (Runtime : 실행)
  • 예외가 발생하더라도 컴파일 O, class 파일 생성
  • 예외의 체크는 실행 중 체크, 실행이 되려면? class 파일 필요(컴파일은 된다...)
  • 유연한 예외, 형식은 X


참고) RuntimeException을 상속받았는지 구분하는 법 : 자바문서 보기

참고)
java.exe : 클래스파일 실행
javac.exe : java -> class 컴파일

참고) 컴파일

  • 자바에서 컴파일은 소스 코드를 기계어로 변환하는 과정을 의미해요. 이렇게 변환된 기계어 코드는 컴퓨터에서 실행될 수 있어요. 자바의 경우, 소스 코드는 .java 파일에 저장되고, 컴파일된 코드는 .class 파일로 저장돼요. 이 .class 파일은 JVM에서 실행될 때 사용돼요.
  • cd 파일경로 : 파일 이동
  • dir /w : 파일 확인 (.java -> .class 파일로 컴파일 되었는지 확인)
  • javac 파일명 : 컴파일




번외) 예외클래스의 주요 메서드

  • 예외 발생 : throw 예외객체; = 내부적으로 예외객체 던짐
  • 예외, 오류 발생 시 원인을 확인을 하는것이 중요 (원인을 알아야 해결하니까)
  • 그렇기 때문에 예외 클래스 주요 메서드는 정보확인이다 (정보가 있어야 원인이 뭔지 아니까)


1. java.lang.Throwable

1) String getMessage() : 오류 메세지 확인

  • 내부적으로 예외객체 던질때 출력되는 오류 메세지 보여줌



2) void printStackTrace() : 오류 발생위치

  • 오류가 발생한곳부터 오류가 파생된 부분까지 스택구조로 오류 발생위치를 보여줌


ㄴ 맨 아랫 줄 : 오류발생 코드라인 위치





예외 처리하기

1. try ~ catch문

  • 예외발생 시 내부적으로 보이지 않지만 예외객체를 던지기 때문에 catch를 통해 던져진 예외객체를 잡아서 예외처리를 해주어야 한다
 try {
	// 예외가 발생할 가능성이 있는 코드 
	
 } catch (예외 객체 ....) {
	// 예외 발생시 처리할 코드 
 }


예시)

ㄴ 예외발생 시 내부적으로 보이지 않지만 예외객체를 던진다 -> throw new FileNotFoundException
ㄴ 예외가 발생하자마자 던져지기 때문에 아직 예외처리 전인 System.out.println("파일 처리...");는 실행x
catch (FileNotFoundException e) {예외 발생시 처리할 코드}
-> FileNotFoundException : 무슨예외인지 명시
-> e : 던져진 예외객체가 담겨져 있는 변수(관례적으로 e를 많이 씀) -> e = new FileNotFoundException



1) 여러 예외를 처리해야하는 경우

① catch를 여러번 쓸 수 있다

 try {
	// 예외가 발생할 가능성이 있는 코드 
	
 } catch ( 예외 객체1 ....) {
	// 예외 발생시 처리할 코드 
 }
 catch ( 예외 객체2 ....) {
	// 예외 발생시 처리할 코드 
 }

ㄴ 예외처리를 했기 때문에 시스템이 다운되지 않고 예외발생이후의 코드도 실행이 된다
= "매우 중요한 코드..." 출력 됨



② 다중예외처리

  • catch ( 예외 객체1 | 예외 객체2 ) {}
  • 예외발생시 처리할 코드가 같은경우 다중예외처리도 가능
 try {
	// 예외가 발생할 가능성이 있는 코드 
	
 } catch ( 예외 객체1 | 예외 객체2 ) {
	// 예외 발생시 처리할 코드 
 }



2) 모르는 예외를 처리하는 경우

  • catch (Exception e) {}
  • Exception 혹은 RuntimeException은 모든 예외의 공통부분(상위클래스)이다
    -> 다형성
  • 다형성을 이용하여 모르는 예외처리
  • 단, catch (Exception e) {} 는 가장 하단에 배치할 것
    -> catch구문은 위에서 아래로 로직이 실현되기 때문에 catch (Exception e) {}가 가장 상단에 있으면 하단의 다른 예외처리로 유입이 되지 않기 때문에 실현되지 못함 -> 에러 뜸




2. try-catch-finally문

  • 예외가 발생하든 안하든 항상 실행되는 코드
	try {
	
	} catch (...) {
		...
	} finally {
		// 예외가 발생하든 안하든 항상 실행되는 코드 
		// return 하더라도 코드가 실행 
	}


예시) 예외가 발생안해도 실행됨


예시) 예외가 발생해도 실행됨



1) 자원을 소비하는 객체

  • 자원을 소비하는 객체 : FileInputStream, FileOutputStream, Connection, PrepareStatement ...
  • 자원 해제 -> 애플리케이션 종료시에 해제 = 서버 종료 시 해제
  • 근데 서버는 종료 X (네이버서버가 9시에 열고 6시에 닫는게 아니니까...)
    -> 자원해제를 하지 않으면 메모리 부족 현상 발생
  • 메모리 부족 현상 발생을 예방하기 위해 자원해제를 적절하게 해야 된다.(close()...)


① FileInputStream

  • 자바에서 파일에서 바이트 단위로 데이터를 읽을 때 사용되는 클래스입니다.
  • 이 클래스는 InputStream 클래스를 상속받아 파일로부터 바이트 단위로 데이터를 읽어올 수 있습니다.
  • 주로 텍스트 파일이나 바이너리 파일과 같은 다양한 종류의 파일을 읽을 때 사용됩니다.


예시)

  • FileInputStream을 사용하여 파일을 읽을 때는 다음과 같은 단계를 따릅니다:
  1. FileInputStream 객체를 생성하고, 읽을 파일의 경로를 매개변수로 전달합니다.
  2. read() 메서드를 사용하여 파일에서 바이트 단위로 데이터를 읽어옵니다.
  3. 더 이상 읽을 데이터가 없을 때는 -1을 반환하므로 이를 이용하여 읽기를 종료합니다.
  4. 사용이 끝난 후에는 close() 메서드를 호출하여 FileInputStream을 닫아야 합니다.
import java.io.FileInputStream;
import java.io.IOException;

public class ReadFileExample {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("example.txt");
            int data;
            while ((data = fis.read()) != -1) {
                System.out.print((char) data);
            }
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

이 예제에서는 "example.txt" 파일을 FileInputStream을 사용하여 읽고, 읽은 데이터를 콘솔에 출력합니다.



② FileInputStream 자원해제 과정

예시1) 왜 예외처리를 IOException으로 했을까?

ㄴ 왜 에러가뜰까?

ㄴ close에 대한 예외처리(IOException) 안해줘서 뜸

ㄴ 근데 보면 FileNotException 의 상위클래스가 IOException 이다!

ㄴ 다형성이용해서 1석2조로 예외처리하자
ㄴ 하지만 처리가 다를 때는 분리해줘야 함


예시 1-1) 예외 발생❌ 시 자원해제

ㄴ 오 근데 b.txt파일은 없으니까 자원해제코드(close)로 유입되기도 전에 예외처리실행되서 catch문으로 갈테니까 a.txt 파일로 바꿔서 로직실현되게 하쟝
ㄴ "자원해제완료!"가 출력되는거 보니 자원해제가 되었겠군 굳굳~
ㄴ여기서 문제 발견!


예시 1-2) 예외 발생⭕ 시 자원해제

ㄴ 이 자원해제 로직은 예외가 없을 때만 실행 될 수 있군!
ㄴ 자원해제는 예외가 있든 없든 실행되야 하는뎅

ㄴ 이때는 try ~ catch 밖으로 빼줘야 함
FileInputStream fis = null; 을 try ~ catch 밖으로 빼주고 try ~ catch 밖 안의 fis = new FileInputStream("b.txt"); 에 대입해주는 식으로 하면 예외가 발생해도 자원해제코드(close) 실행됨❓

ㄴ 값이 있거나 혹은 객체가 안만들어 질 때도 있음
ㄴ 그럴때도 자원해제를 해야하니 if문 작성

ㄴ 처리로직 썻더니 에러 뜸 -> 예외처리안해서 에러 뜸

ㄴ try ~ catch로 감싸줌
ㄴ 이제 예외 발생해도 자원해제 로직 실행 됨

ㄴ 근데 try구문에도 close(예외발생❌시 자원해제코드)가 있고 catch구문에도 close(예외발생⭕시 자원해제코드)가 있고 중복되넹?
ㄴ finally 쓰면 되지 않낭?
-> finally : 예외가 발생하든 안하든 항상 실행되는 코드


예시1-3) finally

package exam02;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Ex04 {
    public static void main(String[] args) {

        FileInputStream fis = null;
        try {
            fis = new FileInputStream("b.txt");

            System.out.println("파일 작업...");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {

                }
            }
        }

    }
}

ㄴ finally를 씀으로서 예외가 발생하든 안하든 자원해제⭕
ㄴ 코드가 넘 길어...
-> try-with-resources문으로 코드 줄일 수 있음




3. try-with-resources문

  • JDK7에서 추가

  • AutoCloseable 인터페이스의 구현 클래스이면 close() 메서드를 자동 호출하여 자동으로 자원해제
    -> try-catch-finally문을 이용한 자원해제 시 코드가 긴 것을 보완

  • 자원 자동해제의 기준 : AutoCloseable 인터페이스의 구현 클래스
    -> close() 메서드를 자동 호출
    -> AutoCloseable 인터페이스의 구현 객체가 아니면 자원해제 안해 줌

  • 자원해제가 필요한 거의 모든클래스는 AutoCloseable 인터페이스의 구현체

	try ( 해제할 자원 객체1;
			해제할 자원 객체2 ...) {
		// 예외가 발생할 가능성이 있는 코드 
		
	} catch(예외 객체 ...) {
	
	}

참고)
instanceof



예시) try-with-resources문

ㄴ 자동으로 close호출됨



1) try-with-resources문 작동원리

ㄴ 클릭

ㄴ 자원해제가 필요한 모든 클래스에 AutoCloseable 인터페이스가 추가되어 있음
-> try-with-resources문을 쓰기 위해서

ㄴ close() : AutoCloseable 인터페이스의 추상메서드
ㄴ 이걸 정의하면 try-with-resources문이 자동으로 정의한 close()메서드를 호출해줌

ㄴ Resources클래스 : 자원해제가 필요한 클래스

@Override
    public void close() throws Exception {
        System.out.println("자원해제!!");
    }

ㄴ AutoCloseable 인터페이스의 추상메서드 정의함

ㄴ res가 AutoClosable 인터페이스 구현 객체인지 체크 -> close() 메서드 자동 호출
ㄴ 투입되는 객체가 다양할텐데 res가 AutoClosable 인터페이스 구현 객체인지 어떻게 체크하지?

// 보이지 않지만 이러한 처리로직에 의해서 close()가 자동호출된다

AutoCloseable auto = res;    // 내부적인 처리로직 -> 다형성이용해서 AutoClosable 인터페이스의 구현체인지 쳌 + 형변환
auto.close(); 				// 내부적인 처리로직 -> close()호출

ㄴ 투입되는 객체가 AutoClosable 인터페이스의 구현체이면 형변환이 되겠고 그러면 close가 호출 될 것이다
-> try-with-resources문을 쓸 때 내부적으로 이러한 처리로직이 담겨 있다ㅏ
-> 다형성

ㄴ "자원해제" 멘트가 출력된거보니 진짜 close() 메서드가 자동 호출되었넹





예외 처리 미루기

1. 예외 처리를 미루는 throws 사용하기

  • 메서드를 호출 하는쪽에서 예외 처리를 하도록 전가

  • RuntimeException을 상속받은 경우

    • 전가시키는 예외에 대해서 명시(throws) 하지 않아도 됨
      -> 형식의 유연성
    • try ~ catch 안써도 오류 발생x
      -> 형식의 유연성
      -> 근데 이건 위험한 방법 , 만약 예외가 발생하면 서비스 다운 -> 조심히 써야한다
  • Exception을 상속 받은 경우(RuntimeException이 없는 경우)

    • 전가시키는 예외에 대해서 명시(throws)해야 함
      -> 형식의 엄격성
    • 예외가 발생하든 안하든 try ~ catch 무족건 써야 함
      -> 형식의 엄격성
  • 전가시키는 예외에 대해서 명시(throws)
    -> 메서드 매개변수 뒤쪽에 throws 전가할 예외 작성
    -> 메서드 매개변수 + throws 전가할 예외



예시) Exception을 상속 받은 경우

ㄴ 전가시키는 예외에 대해서 명시(throws)

ㄴ 메서드를 호출 하는쪽에서 예외 처리

ㄴ 예외처리로직이 동일하니 다형성을 이용하여 처리해야할 예외 2개를 Exception e 1개로 퉁치자


예시) RuntimeException을 상속 받은 경우
Exception을 상속 받은 경우 형식을 엄격히 유지해야 하기 때문에 유연성이 떨어져 잘 안씀
↔ RuntimeException을 상속 받은 경우 형식이 유연하기 때문에 많이 사용

ㄴ UserPwException이 RuntimeException을 상속받음

ㄴ UserIdException이 RuntimeException을 상속받음

ㄴ Exception을 상속 받은 경우와 달리 RuntimeException을 상속받은 경우 전가시키는 예외에 대해서 명시(throws)하지 않아도 됨
-> 형식의 유연성

ㄴ Exception을 상속 받은 경우와 달리 RuntimeException을 상속받은 경우 try ~ catch 안써도 오류 발생x -> 형식의 유연성
ㄴ 근데 이건 위험한 방법 , 만약 예외가 발생하면 서비스 다운 -> 조심히 써야한다




2. 다중 예외 처리




3. 사용자 정의 예외

  • JDK 기본 정의 예외 외에 따로 작성하는 예외
  • 예외클래스 상속 + 생성자 정의 + 상위클래스 생성자에게 처리 위임(super(...))
  • 예외클래스 관례 : 클래스이름 + Exception으로 명명
    ex) UserIdException


예시)
유저 아이디 : user01, 비번 : 123456이 아니면 검증이 실패했다는 예외를 발생시켜 사용자에게 알려주기 = 필요한 경우에 정의하는 사용자 정의 예외

ㄴ 아이디 예외클래스 : 사용자 정의 예외
ㄴ UserIdException이 Exception의 바로 하위클래스라소 바로 상속시행
-> 만약 중간에 다른 클래스ex) RuntimeException...가 껴있으면 상속에 상속을 해줘야 함

ㄴ 비번 예외클래스 : 사용자 정의 예외
ㄴ 예외클래스 상속 + 생성자 정의 + 상위클래스 생성자에게 처리 위임(super(...))
ㄴ 참고) 생성자 매개변수 여러개 설정 가능

ㄴ 예외객체 생성 후 던지기
ㄴ 빨간색으로 오류 발생 왜?
-> UserIdException, UserPwException은 Exception의 바로 하위클래스이니 RuntimException을 중간에 상속받지 않았기 때문에 엄격한 예외임
-> 예외가 발생하든 안하든 예외처리 해줘야 함
-> try ~ catch 정의하자

ㄴ 예외처리함
e.getMessage() : "아이디가 일치하지 않습니다", "비밀번호가 일치하지 않습니다."

ㄴ 예외가 발생하지 않을 때 출력할 멘트 설정함

ㄴ 음 사용자 정의 예외처리가 잘 시행 됬군





컴파일러가 자동 추가해 주는 것들

1. 기본생성자

  • 생성자를 정의하지 않은 경우

2. 모든 생성자 메서드의 첫줄super()

  • super()를 정의하지 않은 경우

3. 참조변수.toString()

  • 참조 변수를 출력 -> 참조변수.toString()

4. final : 지역변수의 상수화


5. 인터페이스의 추상메서드 - public abstract


6. 인터페이스의 변수 정의(정적 상수) : public static final


7. import java.lang.*;

  • java.lang 에 정의된 클래스는 바로 사용 가능

8. extends java.lang.Object

  • 모든 클래스는 java.lang.Object의 하위 클래스이다
  • 보이지 않지만 컴파일러가 자동으로 상속관계 명시함
  • 모든 클래스는 java.lang.Object에 정의된 모든 메서드 접근 가능
profile
귤귤

0개의 댓글