예외처리
Java에서 가장 많이 발생하는 예외 목록
if를 활용한 예외처리
예외를 처리하는 가장 좋은 방법은 예외를 발생시키지 않는 것.
위 예외 목록은 if를 활용해 예외를 방지할 수 있다.
이렇게 먼저 확인하는 코드를 “Validation Logic” 이라고 하며 “입력값 검증“ 이라고 표현하기도 함.
NullPointerException이 발생하는 원인
public static void main(String[] args) {
String name = null;
// NullPointerException - null.equals("이름")
System.out.println(name.equals("이름"));
}
Null 은 Reference Type의 기본 값으로 아무것도 할당되지 않은 상태를 말한다.
할당된 것이 없으니 메모리에 아무것도 없는 상태이며, 존재하지 않는 메모리를 참조하려 한다는 에러가 발생한다.
public static void main(String[] args) {
String name = null;
if (name != null) {
System.out.println(name.equals("이름"));
}
}
인스턴스를 참조하기 전, null 인지 확인한다.
Name 변수의 값은 null 이므로 System.out.println(name.equals("이름")); 이 코드는 실행되지 않는다.
try – catch – finally 예외처리
if 로 예외 처리할 수 없거나 애매할 경우 try ~ catch ~ finally를 사용
try {
// ▼ NumberFormatException 발생
int number = Integer.parseInt("ABC");
System.out.println(number); // 이 코드는 실행되지 않습니다.
}
// ▼ NumberFormatException이 발생했을 때 catch가 실행됩니다.
catch ( NumberFormatException e ) {
// try에서 예외가 발생하면 catch가 실행
System.out.println("에러가 발생했습니다. " + e.getMessage());
e.printStackTrace(); // 에러내용 모두 출력
}
finally {
// 예외 발생 여부와 관계없이 무조건 실행
System.out.println("처리가 완료됐습니다.");
}
(finally까지 쓰는 경우는 드물다.)
아래는 클래스를 동적 로딩하는 코드다.
SomeAClass 라는 클래스를 불러오게 되는데 SomeAClass 라는 클래스는 없으니 에러(ClassNotFoundException)가 발생하게 된다.
Class.forName("SomeAClass");
forName을 Ctrl을 누른채로 클릭해보면
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName(className, caller);
}
ClassNotFoundException을 throws 하고 있다. ClassNotFoundException을 Ctrl + 클릭해보면 ReflectiveOperationException을 상속받은 것을 알 수 있다.
public class ClassNotFoundException
extends ReflectiveOperationException {
다시 ReflectiveOperationException을 Ctrl + 클릭해보면
public class ReflectiveOperationException extends Exception {
결국 ReflectiveOperationException은 Exception을 상속받은 예외클래스라는 것을 알 수 있다.
이 상속관계를 is a 관계로 정리해보면,
ClassNotFoundException은 Exception을 상속받은 예외 클래스이기 때문에 try ~ catch가 반드시 필요
이 코드 실행해보면,
public static void main(String[] args) {
Class.forName("SomeAClass");
}
Unhandled exception type ClassNotFoundException
이런 에러 발생하는데, ClassNotFoundException 타입의 예외를 처리하지 않았다라는 에러이다.
이 에러를 처리하기 위해 try ~ catch를 사용해보면
public static void main(String[] args) {
try {
Class.forName("SomeAClass");
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
}
cnfe.printStackTrace(); 코드에 의해 아래처럼 메시지가 나타나게 된다.
java.lang.ClassNotFoundException: SomeAClass
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:391)
at java.base/java.lang.Class.forName(Class.java:382)
** at App.main(App.java:4)**
Exception이 발생한 최초의 위치와 호출스택들이 한번에 노출되게 되며 가상 상위에 Exception이 발생한 원인에 대해 설명해준다.
public static void main(String[] args) {
String type = null;
String number = "abc";
try {
if (type.equals("number")) {
int num = Integer.parseInt(number);
System.out.println(num);
}
}
catch (NullPointerException | NumberFormatException e) {
e.printStackTrace();
}
}
예외 위임하기
사용자 예외 발생시키기
RuntimeException의 기본 생성자
public RuntimeException() {
super();
}
RuntimeException의 생성자들
public RuntimeException(String message) {
super(message);
}
public RuntimeException(String message, Throwable cause) {
super(message, cause);
}
public class InvalidUserIdException extends RuntimeException {
public InvalidUserIdException(String message) {
super(message);
}
public InvalidUserIdException(String message, Throwable cause) {
super(message, cause);
}
}
사용자 예외는 거창할 필요없이 필요한 생성자만 호출 해주면 된다.
InvalidUserIdException을 발생시켜 본다.
id가 root, admin, system 인 경우 위 예외를 발생시키는 코드이다.
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("ID를 입력하세요.");
String userId = input.nextLine();
if (userId.equals("root") || userId.equals("admin")
|| userId.equals("system")) {
InvalidUserIdException iuie =
new InvalidUserIdException(userId + "는 사용할 수 없습니다.");
throw iuie;
}
}
throw Exception객체 코드를 통해서 예외를 발생시키게 된다.
보통, 예외를 발생시킬 경우는 아래 코드처럼 한 줄로 사용한다.
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("ID를 입력하세요.");
String userId = input.nextLine();
if (userId.equals("root") || userId.equals("admin")
|| userId.equals("system")) {
throw new InvalidUserIdException(userId + "는 사용할 수 없습니다.");
}
}