[TIL] 240428 <자바> 예외처리
- -
[10. 예외처리]
오류(Error) vs 예외(Exception)
오류 : 회복 불가
- 이는 시스템 레벨에서, 또는 주로 환경적인 이유로 발생
- 코드의 문제로 발생하는 경우도 있지만, 일단 발생하는 경우 일반적으로 회복이 불가능
- 에러가 발생한 경우 우리는 어떠한 에러로 프로그램이 종료되었는지를 확인하고 대응
예외 : 회복 가능
- 회복이 가능하다는 전제는 우리가 “그 예외가 발생할 수 있다는 것을 인지하고, 대응했을 것입니다”.
- 현실적으로 코드 레벨에서 할 수 있는 문제 상황에 대한 대응은 “예외 처리”에 속함
예외(Exception)의 종류
'코드 실행' 관점에서 예외의 종류
- 컴파일 에러(예외)
- .java 파일을 .class 파일로 컴파일할 때 발생하는 에러
- 대부분 자바 프로그래밍 언어의 규칙을 지키지 않았기 때문에 발생
- 해결 방법은 문법에 맞게 다시 작성하는 것
- 런타임 에러(예외)
- 문법적인 오류는 아니라서, 컴파일은 잘 되었지만 “프로그램”이 실행 도중 맞닥뜨리게 되는 예외
'예외 처리' 관점에서 예외의 종류
- 확인된 예외 (Checked Exception)
- 컴파일 시점에 확인하는 예외
- 반드시 예외 처리를 해줘야 하는 예외
- 이미 특정한 문제를 인지하고 있어서, 해당 예외를 정의해두었고, 정의해두었기 때문에 컴파일하는동안
이 예외에 대한 예외 처리를 했는지 확인(Check)할 수 있는 예외 - "Checked Exception에 대한 예외 처리를 하지 않으면 컴파일 에러가 발생" (컴파일 에러와 다르다!)
- 미확인된 예외 (Unchecked Exception)
- 런타임 시점에 확인되는 예외
- 예외 처리가 반드시 필요하지 않은 예외
예외 처리의 흐름
1. 예외 정의하기 - 예외 클래스를 생성하여 직접 예외 정의 가능
class OurBadException extends Exception { //Exception 상속
public OurBadException() {
super("위험한 행동을 하면 예외처리를 꼭 해야합니다!");
}
}
2. 예외가 발생할 수 있음을 알림 - 클래스를 만들고, 메서드를 만들며 메서드가 위험하다는 것을 미리 예측하여 위험하다고 알리기
(throw, throws)
class OurClass {
private final Boolean just = true;
//throws : 메서드 이름 뒤에 붙어 이 메서드가 어떠한 예외사항을 던질 수 있는지 알려줌
public void thisMethodIsDangerous() throws OurBadException {
if (just) {
//throw : 메서드 안에서, 실제로 예외 객체를 던질 때 사용
throw new OurBadException(); //실제로 던지는 예외 객체 하나와 같이 써야함
//throw문과 함께 메서드가 종료됨
}
}
}
- throws
- 메서드 이름 뒤에 붙어 이 메서드가 어떠한 예외사항을 던질 수 있는지 알려주는 예약어
- 여러 종류의 예외사항을 적을 수 있음
- throw
- 메서드 안에서, 실제로 예외 객체를 던질 때 사용하는 예약어
- 실제로 던지는 예외 객체 하나와 같이 써야함
- 일반 메서드의 return키워드처럼 throw 아래의 구문들은 실행되지 않고, throw문과 함께 메서드 종료됨4
3. 예외가 발생할 수 있음을 알고 예외를 핸들링하기 - 위험 감지했다면 try-catch(finally) 키워드 이용
public class StudyException {
public static void main(String[] args) { //메인 메서드
OurClass ourClass = new OurClass();
//try-catch, finally
try { // "시도" 해보는 코드
// 1. 위험한 메소드의 실행을 "시도" 해 봅니다.
ourClass.thisMethodIsDangerous(); //위험메서드
} catch (OurBadException e) { //예외가 발생하는경우 "handling" 하는 코드
// 2. 예외가 발생하면, "잡아서" handling 합니다.
// 즉 try 블럭 내의 구문을 실행하다가 예외가 발생하면
// 예외가 발생한 줄에서 바로 코드 실행을 멈추고
// 여기 있는 catch 블럭 내의 코드가 실행됨
System.out.println(e.getMessage());
} finally { // 무조건 실행되는 코드
// 3. 예외의 발생 여부와 상관없이, 실행시켜야 하는 코드가 들어감
System.out.println("우리는 방금 예외를 handling 했습니다!");
}
}
}
1. 위험 감지하기 : 특정 클래스의 메서드 사용 시, 이 메서드가 위험한지 아닌지 확인해야함
2. 위험 감지했다면, try-catch, finally 키워드 이용하기
- try : 위험한 메서드의 실행을 "시도"할 코드 담음
- catch : try 안의 코드 실행하다가 예외 발생 시, 거기서 바로 코드실행 멈추고 catch 안의 코드 실행
- catch 는 소괄호()를 통해 어떤 예외 클래스를 받아서 처리할지 정의해 주어야함
- catch로 모든 예외를 다 받고 싶으면 Exception 을 넣어주면됨
- 1개의 try 문에 catch 문은 여러 개 사용가능
- finally : 예외 발생 여부와 상관없이 무조건 실행시켜야 하는 코드 담기
위의 예시에서 Checked Exception을 다뤘음!
(예외가 발생하는 상황을 “인지” 했고, 어떠한 에러인지 “정의” 하였음)
메서드를 선언할 때 예외가 발생하는 위험한 메서드라는 것을 “알렸음 (throws/throw)”
checked exception을 정의하고 알렸으니 이 메서드를 사용할 때 예외 처리를 하지 않으면 컴파일 에러가 발생!!!
예외 클래스 구조
- RuntimeException을 상속한 예외들은 UncheckedException
- 반대로 상속하지 않은 예외들은 CheckedException으로 구현
▼ Java 예외 리스트
// Java 예외 리스트
// 여기에 없다면 특정 에러를 더 구체화해서 직접 정의&구현하면됨
// 출처 : https://programming.guide/java/list-of-java-exceptions.html
// java.io
IOException
CharConversionException
EOFException
FileNotFoundException
InterruptedIOException
ObjectStreamException
InvalidClassException
InvalidObjectException
NotActiveException
NotSerializableException
OptionalDataException
StreamCorruptedException
WriteAbortedException
SyncFailedException
UnsupportedEncodingException
UTFDataFormatException
UncheckedIOException
// java.lang
ReflectiveOperationException
ClassNotFoundException
InstantiationException
IllegalAccessException
InvocationTargetException
NoSuchFieldException
NoSuchMethodException
CloneNotSupportedException
InterruptedException
// 산술 예외
IndexOutOfBoundsException
ArrayIndexOutOfBoundsException
StringIndexOutOfBoundsException
ArrayStoreException
ClassCastException
EnumConstantNotPresentException
IllegalArgumentException
IllegalThreadStateException
NumberFormatException
IllegalMonitorStateException
IllegalStateException
NegativeArraySizeException
NullPointerException
SecurityException
TypeNotPresentException
UnsupportedOperationException
// java.net
HttpRetryException
SocketTimeoutException
MalformedURLException
ProtocolException
SocketException
BindException
ConnectException
NoRouteToHostException
PortUnreachableException
UnknownHostException
UnknownServiceException
URISyntaxException
// java.text
ParseException
// java.time
DateTimeException
// java.time.zone
ZoneRulesException
연결된 예외 (Chained Exception)
- 예외는 다른 예외를 유발할 수 있음
- 예외 A가 예외 B를 발생시켰다면, 예외 A는 B의 원인 예외
- 원인 예외를 새로운 예외에 등록한 후 다시 새로운 예외를 발생시킴 → 예외 연결
// 연결된 예외
public class main {
public static void main(String[] args) {
try {
// 예외 생성
NumberFormatException ex = new NumberFormatException("가짜 예외이유");
// initCause() : 원인 예외 설정(지정한 예외를 원인 예외로 등록)
ex.initCause(new NullPointerException("진짜 예외이유"));
// 예외를 직접 던집니다.
throw ex;
} catch (NumberFormatException ex) {
// 예외 로그 출력
ex.printStackTrace();
// getCause() : 예외 원인 조회, 후 출력
ex.getCause().printStackTrace();
}
// checked exception 을 감싸서 unchecked exception 안에 넣음
throw new RuntimeException(new Exception("이것이 진짜 예외 이유 입니다."));
}
}
// 출력
Caused by: java.lang.NullPointerException: 진짜 예외이유
- 원인 예외를 다루기 위한 메소드
- initCause() : 지정한 예외를 원인 예외로 등록하는 메소드
- getCause() : 원인 예외를 반환하는 메소드
- 예외를 연결하는 이유
- 여러 가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위함
- checked exception을 unchecked exception으로 포장(wrapping) 하는데 유용하게 사용됨
실제 예외 처리하는 방법
1. 예외 복구하기
public String getDataFromAnotherServer(String dataPath) {
try {
return anotherServerClient.getData(dataPath).toString();
} catch (GetDataException e) {
return defaultData;
}
}
- 실제로 try-catch로 예외를 처리하고 프로그램을 정상 상태로 복구하는 방법
- 가장 기본적인 방식이지만, 현실적으로 복구가 가능한 상황이 아닌 경우가 많거나 최소한의 대응만 가능한 경우가 많기 때문에 자주 사용되지는 않음
2. 예외 처리 회피하기
public void someMethod() throws Exception { ... }
public void someIrresponsibleMethod() throws Exception {
this.someMethod();
}
- someMethod()에서 발생한 에러가 someIrresponsibleMethod()의 throws를 통해서 그대로 다시 흘러나가게됨
(물론 같은 객체 내에서 X!, 예외 처리 회피를 보여주기위한 단순한 예시 코드) - 관심사를 분리해서 한 레이어에서 처리하기 위해서 이렇게 에러를 회피해서 그대로 흘러 보내는 경우도 있음
3. 예외 전환하기
public void someMethod() throws IOException { ... }
public void someResponsibleMethod() throws MoreSpecificException {
try {
this.someMethod();
} catch (IOException e) {
throw new MoreSpecificException(e.getMessage());
}
}
- 예외 처리 회피하기의 방법과 비슷하지만, 조금 더 적절한 예외를 던져주는 경우
- 보통은 예외 처리에 더 신경 쓰고 싶은 경우나, 오히려 RuntimeException처럼 일괄적으로 처리하기 편한 예외로 바꿔서 던지고 싶은 경우 사용
'TIL' 카테고리의 다른 글
[TIL] 240430 <자바> 제네릭 (0) | 2024.04.30 |
---|---|
[TIL] 240429 <자바> 계산기 예외 처리 (0) | 2024.04.29 |
[TIL] 240426 <자바> 계산기 만들기 (0) | 2024.04.26 |
[TIL] 240425 <자바> 상속,인터페이스 (1) | 2024.04.25 |
[TIL] 240424 <자바> 클래스 (0) | 2024.04.24 |
소중한 공감 감사합니다