1. 프로그램 오류
프로그램 수행 시 치명적 상황이 발생하여 비정상 종료 상황이 발생한 것
1) 오류의 종류
- 컴파일 에러: 프로그램 실행을 막는 소스 상의 문법 에러 → 소스 구문을 수정하여 해결
- 런타임 에러: 입력값이 틀렸거나 배열의 인덱스 범위를 벗어났거나, 계산식 오류 등
- 실행 예외는 개발자가 알아서 예외 처리 코드 작성 ⇒ 초보 개발자는 일단 실행을 시켜보고 어디에 무슨 예외가 발생하는지를 보고 처리 코드를 추가하면 된다!
→ 주로 if문 사용으로 에러 처리
- 실행 예외는 개발자가 알아서 예외 처리 코드 작성 ⇒ 초보 개발자는 일단 실행을 시켜보고 어디에 무슨 예외가 발생하는지를 보고 처리 코드를 추가하면 된다!
- 시스템 에러: 컴퓨터 오작동으로 인한 에러, 리소스 부족 등 → 컴퓨터 자체의 문제로 개발자가 근본적 해결이 어려움
2) 오류 해결 방법
- 소스 수정으로 해결 가능한 에러(예외)라면 예외 상황 구문을 처리하는 방법인 예외처리를 통해 해결
2. 예외(Exception)란?
하나의 런타임 에러
- 예외 발생시 프로그램 즉시 종료되고 예외내용이 출력됨 ⇒ 예외 발생시 어떻게 처리해서 종료되지 않도록 하는 것 = 예외 처리
- 개발자가 예외를 만들어서 발생시킬 수도 있음
1) 컴파일러의 역할
- 예외 발생 가능성이 높은 코드를 컴파일할 때 컴파일러는 예외 처리 유무를 확인함
- 없을 시 자바가 개발자에게 컴파일 오류로 알림을 주고 작성할 수 있도록 안내해줌
2) 예외의 종류
- 예외 클래스
- 자바에서는 예외를 클래스로 관리함
- 프로그램 실행 중 예외 발생시 해당 예외 클래스로 객체를 생성하고 예외 처리 코드에서 예외 객체를 이용할 수 있도록 해줌
- Object클래스의 자손이며, 모든 예외의 최고 조상은 java.lang.Exception 클래스
- 종류의 구분
- RuntimeException 클래스를 기준으로 하위 클래스가 아니면 일반 예외 클래스가 되며, 하위 클래스라면 실행 예외 클래스가 된다.
- 일반 예외(Exception)
- 컴파일러 체크 예외 = 자바 소스 컴파일 과정에서 해당 예외 처리 코드가 있는지 검사 O
- [예외의 상속 관계]와 관련
- 자바의 모든 예외는 Exceoption이라는 부모를 가짐 ⇒ Exceoption = 예외 클래스의 최상위 부모
- 일반 예외란 해당 Exception을 상속받은 예외 클래스를 의미
- 종류: ClassNotFound, Interrupted 등
- 실행 예외(runtime exception) 클래스
- 컴파일러 넌 체크 예외 = 실행 시 예측할 수 없이 갑자기 발생하여 예외처리코드 검사 X
- 어디서 발생하는지 잘 기억해둬야 함 ⇒ 보통 컴파일러의 체크가 없더라도 개발자의 경험에 의해서 예외 처리 코드를 작성해야 함
- RuntimeException이라는 부모를 상속함
- 종류: NullPointer, ClassCast, NumberFormat 등
- 컴파일러 넌 체크 예외 = 실행 시 예측할 수 없이 갑자기 발생하여 예외처리코드 검사 X
- 사용자 정의 예외
- 가장 빈번하게 발생
- 객체 참조가 없는 상태의 참조 변수로 객체 접근 연산자 도트(.)를 사용할 경우 발생 </aside>
3) 자주 보는 예외 모음
예외 | 설명 | 종류 |
ClasNotFoundException | 클래스를 찾지 못한 경우 | 일반 예외 |
NoSuchMethodException | 메소드를 찾지 못한 경우 | 일반 예외 |
ArithmeticException | 0으로 나누는 등 산술 연산에 예외가 발생한 경우 | 실행 예외 |
NullPointerException | 존재하지 않는 객체를 참조한 경우 - 가장 빈번하게 발생 - 객체 참조가 없는 상태의 참조 변수로 객체 접근 연산자 도트(.)를 사용할 경우 발생 | 실행 예외 |
ArrayIndexOutOfBoundsException | 배열 인덱스 범위를 초과할 경우 - if문 사용을 통해서 실행을 조절하면 에러를 줄일 수 있음 | 실행 예외 |
NegativeArraySizeException | 0보다 작은 값으로 배열의 크기를 지정한 경우 | 실행 예외 |
NumberFormatException | 문자열을 숫자로 변환하려고 할 때 발생 - Integer.parseInt(String s) : 정수로 변환 - Double.parseDouble(String s) : 실수로 변환 | 실행 예외 |
ClassCastException | 잘못된 관계의 클래스를 강제 타입변환(캐스팅)할 때 발생 - instanceof 연산자로 객체 타입 확인 후 캐스팅 진행 | 실행 예외 |
InputMismatchException | 입력값 타입이 일치하지 않을 때 발생 | 실행 예외 |
IllegalArgumentException | 잘못된 인자를 전달한 경우 | 실행 예외 |
IOException | 입출력 처리가 중단된 경우 | 일반 예외 |
ArithmeticException | 수학적 계산이 불가능할 때 발생 | 실행 예외 |
FileNotFoundException | 파일이 존재하지 않거나, 파일에 접근할 수 없을 때 발생 | 일반 예외 |
예외와 에러
예외는 처리가 가능하도록 세팅되어있지만, 에러는 시스템 수준에서 발생하는 해결할 수 없는 것으로 간주된다. try-catch문으로 발생을 잡을 순 있지만 근본적으로 에러가 발생한다면 로직을 검토하는 것이 좋다.
에러설명
예외 | 설명 |
StackOverflowError | 스택 메모리가 가득 차서 발생. 주로 종료 조건 없는 재귀 호출이나 과도한 함수 호출이 원인. |
OutOfMemoryError | 힙 메모리가 부족해서 객체를 생성할 수 없을 때 발생. JVM 메모리 할당 초과. |
VirtualMachineError | JVM이 심각한 내부 문제를 겪을 때 발생. JVM 실행 자체가 불가능한 상태를 나타냄. |
NoClassDefFoundError | 클래스 파일이 존재하지 않거나, 런타임 시 로드할 수 없는 경우 발생. |
UnsupportedClassVersionError | 클래스 파일의 버전이 현재 JVM에서 지원하지 않는 경우 발생. |
ClassFormatError | 클래스 파일 형식이 손상되었거나 잘못된 경우 발생. |
InternalError | JVM 내부에서 예기치 않은 상황이 발생한 경우. |
IncompatibleClassChangeError | 클래스의 변경이 기존 코드와 호환되지 않을 때 발생. |
VerifyError | 클래스 파일 검증 단계에서 문제가 발견되었을 때 발생. |
LinkageError | 클래스가 연결(linking)되는 동안 문제가 발생. 예: 클래스의 정의가 중복되거나 호환되지 않을 때. |
ThreadDeath | Thread.stop() 메서드를 호출해 스레드를 강제 종료하면 발생. |
3. 예외 처리란?
- 반드시 예외처리를 해야되는 Checked Exception와 예외처리 구문을 작성하지 않아도 되는 Unchecked Exception으로 나뉘어서 설명 가능
- 전자라면 예외 처리 프로그램 통해 정상 실행상태 유지 가능
- 자바 컴파일러가 예외 처리 코드를 강제로 요구하여 실행이 안됨 = unhandled exception … 빨간줄뜸
- 후자는 주로 RuntimeException을 부모로 가지는 예외들임
- 실행 이후에 발생하고, 발생시 무조건 에러뜨면서 멈춰버림
- 참고 : Java Development Kit Version 17 API Specification
- 전자라면 예외 처리 프로그램 통해 정상 실행상태 유지 가능
예외 처리의 목적
- 프로그램의 갑작스러운 종료 방지
- 정상 실행 유지
4. 예외 처리 방법
특정 메소드를 사용하려면 반드시 명시된 예외 클래스를 처리해줘야 한다.
1) 예외 처리 코드 = [try-catch]
- 작성 방법
try{
실행하고 싶은 코드(예외 발생 가능 코드) //예외 발생시 실행 X
} catch(예외클래스 또는 변수명 e) {
예외 발생했을 때 실행할 로직
} [finally { //있을수도 없을수도
예외 여부를 떠나 항상 실행할 내용;
}]
catch
- 여러개의 처리가 가능하지만 exception간의 상속관계도 고려해야한다.
- unchecked는 프로그램이 돌아갈수 있도록 하는 용도로 작성하고, checked는 로그를 받도록 작성한다
- “|”를 사용하여 한개의 catch에 여러 예외를 적을 수도 있다
finally
예외 발생 상관없이 무조건 처리해야하는 로직 작성
- 보통 자원을 반납해야하는 로직 작성을 많이 한다. 예) stream 닫기 등
try-catch 종료 직후에 작성하는 것과 무슨 차이가 있는지? → catch에 return이 있어도 finally는 실행됨
- 🚨 System.exit();를 만나면 무조건 프로그램 종료됨
- 주로 java.io나 java.sql 패키지의 메소드 처리시 이용한다.
- 생성자 및 메소드 내부에서 작성되어 일반 예외와 실행 예외가 발생할 경우 예외 처리 가능케 함
- 각 try {}, catch {}, finally{}의 실행문은 각기 다른 구역으로 서로 간섭할 수 없는 구역임(서로 다른 캡슐). 모두 이용할 수 있는 변수가 필요하다면 try 전에 선언해야함!
try {
//아래 주소로 코드를 보냄
URL url = new URL("<https://naver.com>");
//추상화 객체라서 임포트, 연결 후 가져오기
HttpURLConnection connect = (HttpURLConnection) url.openConnection();
input = connect.getInputStream();
//문자열 형태로 전환해주는 보조스트림
isr = new InputStreamReader(input);
int data = 0;
StringBuffer sb = new StringBuffer();
while((data = isr.read()) != -1) {
sb.append((char)data);
}
System.out.println(sb);
//출력시 홈페이지 html코드가 가져와짐(한글깨짐) 바이트 단위로 가져와서
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
input.close();
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
다중 catch
- 발생하는 예외별로 예외 처리 코드를 다르게하는 다중 catch 블록을 가질 수 있음 ⇒ 예외명에 따라 해당하는 catch 블록만 실행할 수 있음
- catch를 잘못 작성하면 잘못 실행될 수 있음 ⇒ 구체적인 예외명 작성 필요
- 순차적으로 실행되기 때문에 순서를 바꾸면 정상 작동 가능, 모든 예외를 처리할 수 있는 캐치블록으로 활용 가능
- 상속관계인 에러를 catch로 쓸 때
- 부모 예외는 자식 예외 처리를 할 수 있다.
- 예외는 계층구조이기 때문
- → 아래로 갈 수록 높은 예외를 작성하면 됨
- 🚨주의: 맨앞 catch문에 Exception 작성시 모든 예외가 해당되어 예외처리되고 다른 캐치 블록은 실행되지 않을 수 있어 새로운 예외가 발생함
- 모든 예외는 Exception을 상속받기 때문에 발생
- 내가 예상치 못한 예외가 발생할 경우를 대비해서 마지막에 적기도 함
- 부모 예외는 자식 예외 처리를 할 수 있다.
- 상속관계인 에러를 catch로 쓸 때
- 예외 내용(어디서, 왜) 표시해주는 코드 → catch 에 작성
- e.printStackTrace(); 작성 (가장 많이 씀)실제 우리가 콘솔창에서 보는 것과 가장 유사하고 아래 두개 메소드보다 내용이 더 정교함 ⇒ 개발 동안에만 디버깅 목적으로 사용 가능 (개발을 다 하고 나면 삭제 필수)
- catch(FileNotFoundException e) { e.printStackTrace(); }
- : 에러 메세지에 대한 정보를 출력할 때 사용가능한 함수를 제공
- e.getMessage();
- 에러 정보 출력
- getStackTrace();
- 에러 정보를 배열로 가져와 출력
2) try ~ with ~ resource
자바 7에서 추가된 기능
컴퓨터의 자원을 되돌려주는 것
finally에 넣어줄 close 처리를 try문에서 자동으로 처리함
3) 예외 떠넘기기 throws
메소드를 호출한 곳으로 예외처리를 떠넘기는 것 → 해당 메소드를 호출하는 곳은 어디던 try catch를 적어줘야함.
checked exception일때 많이 사용
메소드 선언부에 메소드명 뒤에 throws 예외클래스명[예외클래스2, 3…] 작성
- throws 키워드 뒤에 떠넘길 예외 클래스를 쉼표로 구분하여 나열
: 리턴타입 메소드명(매개변수, …) throws 예외클래스1, 예외클래스2, …{} - method2가 여러 곳에 있다면 호출하는 곳마다 떠넘기기 작성해서 예외처리 코드 내용을 다양하게 구성할 수 있게 됨.
- 해당하는 예외가 check exception이라면 처리를 해줘야함
- 계속 위임만 하면 결국 메인 메소드까지 위임하게 되고 거기서도 처리되지 않으면 비정상 종료됨
public void method1() {
try {
method2();
} catch(ClassNotFoundException e) {
//예외 처리 코드
}
}
public void method2() throws ClassNotFoundException {
Class z = Class.forName("java.lang.String2");
}
4) 사용자 정의 예외(내가 만든 예외 발생시기키기)
예외 조상 클래스를 상속받는 클래스 생성
checked 예외로 만들려면
- Exception을 상속받는 클래스와 생성자 선언
- 클래스 생성후 클래스명과 동일한 메소드 생성
package com.exception.common.exception;
import java.time.LocalDateTime;
public class MyCheckedException extends Exception{
//발생시간을 명시하고 싶을 때
private LocalDateTime triggerTime;
public MyCheckedException() {
}
//생성자
public MyCheckedException(String msg) {
super(msg);
triggerTime = LocalDateTime.now();
//System.out.println(triggerTime);
}
public LocalDateTime getTriggerTime() {
return triggerTime;
}
public void setTriggerTime(LocalDateTime triggerTime) {
this.triggerTime = triggerTime;
}
}
unchecked 예외로 만들려면
- RuntimeException 상속 받는 클래스와 생성자 선언
- 클래스 생성후 클래스명과 동일한 메소드 생성
package com.exception.common.exception;
public class MyUnCheckedException extends RuntimeException{
public MyUnCheckedException(String msg) {
super(msg);
}
}
에러 발생시간을 명시하고 싶다면
: 시간을 받는 필드 명시, getter, setter 등록
//발생시간을 명시하고 싶을 때
private LocalDateTime triggerTime;
예외 오버라이딩
- throws하는 예외 개수와 상관없이 처리 범위가 아래에 있을 수록 더 좁아져야함
반응형
'JAVA' 카테고리의 다른 글
14-1. 자바(java) : 컬렉션(Collection) - List 개념과 활용 정리 (0) | 2024.04.09 |
---|---|
13. 자바(java) : 문자열 및 파일 입출력(IO)을 위한 스트림(stream) 간단 정리 (0) | 2024.04.09 |
11. 자바(java) : 다형성(Polymorphism)과 캐스팅(casting), 추상(substract) 개념 정리 (0) | 2024.04.01 |
10. 자바(java) : 상속(Inherit), super, 오버라이딩(overriding) 간단 정리 (0) | 2024.04.01 |
9. 자바(java) : 제어자 (modifier), 접근제한자, static 개념 간단 정리 (0) | 2024.04.01 |