서론
오늘은 자바 예외 처리, 쓰레드, 문자열에 대해서 배웠는데 바로바로 정리를 안하니깐 내용이 쌓여간다... 그래서 쓰레드랑, 문자열은 정리를 못했다 정리와 기록을 습관하자잇..
본론
예외 개념
자바에서 예외란 프로그램 실행 중에 발생하는 예기치 않은 상황이나 오류를 말한다. 예외가 발생하면 프로그램의 정상적인 흐름을 방해하고 프로그램이 비정상적으로 종료될 수 있다.
🌳 Object
|
|-- 💥 Throwable
|
|-- 🚨 Error
| |
| |-- 🔗 LinkageError
| | |
| | |-- 📜 NoClassDefFoundError
| | |-- 🔍 ClassFormatError
| |
| |-- 🚀 VirtualMachineError
| |
| |-- 🧠 OutOfMemoryError
| |-- 🧩 StackOverflowError
|
|-- ⚠️ Exception
|
|-- 🔄 RuntimeException
| |
| |-- ➗ ArithmeticException
| |-- 📉 ArrayIndexOutOfBoundsException
| |-- 🚫 NullPointerException
| |-- 🔄 ClassCastException
| |-- ❓ IllegalArgumentException
| |-- 📉 IndexOutOfBoundsException
|
|-- ✔️ CheckedException
|
|-- 💻 IOException
| |
| |-- 📂 FileNotFoundException
| |-- 📜 EOFException
| |-- 🌐 MalformedURLException
|
|-- 🗄️ SQLException
|-- 🧩 ClassNotFoundException
|-- 🏷️ NoSuchFieldException
|-- 🛠️ NoSuchMethodException
|-- 🏗️ InstantiationException
|-- 🚫 IllegalAccessException
자바에서 예외는 모두 클래스 형태로 표현되며 이 클래스들은 계층적 구조를 띄고 있다.
- 🌳 Object: 모든 클래스의 최상위 클래스이다.
- 💥 Throwable: 모든 예외와 오류의 최상위 클래스이다.
- 🚨 Error: 메모리 부족이나 심각한 오류와 같이 애플리케이션에서 복구 불가능한 시스템 예외를 말한다.
- 🔗 LinkageError: 클래스의 링크 문제를 나타낸다.
- 🚀 VirtualMachineError: JVM 수준의 문제를 나타낸다.
- ⚠️ Exception: 프로그램에서 발생할 수 있는 예외 상황을 나타낸다.
- 🔄 RuntimeException: 컴파일시에 체크하지 않는 언체크 예외를 말한다/
- ✔️ CheckedException: 컴파일시에 체크하는 체크 예외를 말한다.
- 💻 IOException: 입출력 작업에서 발생하는 예외이다.
- 🗄️ SQLException: 데이터베이스 접근에서 발생하는 예외이다.
- 🌐 MalformedURLException: 잘못된 URL 형식을 나타낸다.
- 🧩 ClassNotFoundException: 클래스를 찾을 수 없을 때 발생한다.
- 🏷️ NoSuchFieldException: 존재하지 않는 필드 접근 시 발생한다.
- 🛠️ NoSuchMethodException: 존재하지 않는 메서드 접근 시 발생한다.
- 🏗️ InstantiationException: 객체 생성 불가능 시 발생한다.
- 🚫 IllegalAccessException: 접근이 금지된 클래스 접근 시 발생한다.
자바에 예외는 크게 두 가지로 분류된다.
- 체크 예외 : 컴파일러가 확인하는 예외로 프로그램 작성 시 반드시 예외처리를 해주어야 한다.
- 언체크 예외 : 실행 중에 발생하는 예외로 컴파일러가 확인하지 않는다.
체크 예외란?
체크 예외는 자바에서 컴파일 시점에 컴파일러가 확인하는 예외로 체크 예외가 존재할 시 반드시 예외를 처리하거나 예외를 호출자에게 넘겨주어 처리를 해주어야 한다. 적절한 예외 처리가 이루어지지 않으면 컴파일이 되지 않고 오류 메시지를 출력한다.
언체크 예외란?
언체크 예외란 프로그램 실행 중 발생하는 예외로 컴파일러가 검사하지 않는다. 언체크 예외는 반드시 처리할 필요가 없으며 개발자가 적절히 예외를 처리해야 한다.
예외 처리 개념
예외 처리 개념에 대해 나의 언어로 바꿔 다시 정리해 보았다. 먼저, 자바에서 예외는 프로그램 실행 중에 발생한 오류나 문제를 말한다. 예외가 발생하게 되면 프로그램이 비정상적으로 종료되지만, 적절한 예외 처리를 통해 프로그램을 다시 정상적인 상태로 복구할 수 있다.
먼저 자바 프로그램이 실행되면 자바 컴파일러가 Java언어로 작성된 소스 코드를 컴파일한다. 이 과정에서 체크 예외가 발생할 수 있는지 확인한다. 만약 체크 예외가 발견되면 적절한 예외를 처리하는지 확인하고 만약 체크 예외를 적절히 처리하지 않으면, 컴파일러는 오류 메시지를 출력하고 프로그램은 컴파일되지 않는다.
만약 프로그램 실행 중 예외가 발생하게 되면 먼저 예외 클래스의 객체가 생성된다. 이 객체에는 오류에 대한 정보를 가지고 있으며, 예외가 발생한 지점부터 호출 스택을 따라 올라가면서 'try-catch' 블록을 찾는다.
호출 스택을 따라 올라가 'try-catch' 블록을 찾으면, 해당 블록에서 예외를 처리하게 되고 프로그램은 다시 정상적인 흐름으로 바뀌게 됩니다. 만약 호출 스택의 최상위인 main() 메서드까지 올라가서도 예외를 처리할 수 없다면, JVM이 이 예외를 처리하게 됩니다. 이 경우 프로그램이 비 정상적으로 종료되며 에러 메시지를 출력하게 된다.
예외 처리하기
자바 예외처리 기본 개념
자바의 예외 처리는 폭탄 돌리기와 같다 잡아서 처리하거나 처리할 수 없다면 밖으로 던져야 한다.
예외의 기본 규칙
- 예외를 잡아서 처리하거나 처리할 수 없다면 밖으로 던져야 한다.
- 예외를 잡거나 던질 때 예외뿐만 아니라 그 예외의 자식예외들도 함께 처리된다.
예외 처리하기
자바에서 예외를 처리하기 위해 자바에서는 `try-catch-finally` 블록을 사용하여 예외를 처리한다.
try {
// 예외가 발생할 수 있는 코드
} catch (예외 클래스) {
// 예외를 처리할 코드
} finally {
// 예외 발생여부와 상관없이 실행되는 코드
}
try 블록
예외가 발생할 수 있는 코드를 작성한다. 이 블록에서 예외가 발생되면 catch 블록으로 전달된다.
catch 블록
try 블록에서 발생한 예외를 처리한다. catch 블록은 여러 개 선언할 수 있으며 작성된 순서로 예외를 처리할 수 있는지 확인한다. 이때 부모 타입의 예외를 먼저 선언하게 되면 자식 타입의 예외들도 함께 처리되기 때문에 자식 타입의 예외에 도달하지 못하는 경우가 발생할 수 있다. 따라서 자식 타입의 예외를 먼저 선언하고 이후에 부모 타입의 예외를 나중에 선언하여 예외를 적절하게 처리해야 한다.
fianlly 블록
예외 발생여부와 상관없이 실행되는 코드이다. 주로 자원 정리 등의 작업을 수행할 때 사용한다.
예외 던지기
메서드 내부에서 예외가 발생할 때 try-catch 문을 이용하여 예외를 처리하는 것이 일반적이지만 마치 폭탄 돌리기처럼 메서드를 호출한 곳으로 예외를 던질 수 있다.
throw 예외 발생시키기
먼저 throw란 자바에서 예외를 명시적으로 발생시킬 때 사용한다. 개발자가 직접 예외를 생성하고 이를 던짐으로써 예외 처리를 강제할 수 있다. 예외를 던지는 것은 코드에서 "문제가 생겼다"라고 신호를 보낸다고 이해할 수 있다.
throws 예외 던지기
thorws 키워드는 메서드 선언부에서 사용되며 해당 메서드에서 발생할 수 있는 예외를 명시적으로 나열한다. 이는 컴파일러에게 이 메서드를 호출하는 코드에서 이 예외를 처리하도록 강제하는 역할을 한다. 즉, 예외 처리와 책임을 메서드 외부로 던지겠다는 것을 의미한다.
예외 처리 실습
public class Main {
public static void main(String[] args) {
method1();
}
public static void method1() {
System.out.println("Main.method1");
method2();
}
public static void method2() {
System.out.println("Main.method2");
method3();
}
public static void method3() {
System.out.println("Main.method3");
System.out.println(0 / 0);
}
}
먼저, main 메서드가 실행되면서 프로그램이 시작된다. 이후 main() 메서드는 method1 메서드를 호출한다. method1 메서드는 콘솔에 "Main.method1"을 출력한 후, method2 메서드를 호출한다. method2 메서드는 "Main.method2"를 출력하고, 그 다음 method3 메서드를 호출한다.
그 후 호출 스택을 살펴보자 호출스택은 메서드의 호출 순서에 따라 쌓이게 되며 가장 최근에 호출된 메서드가 스택의 최상단에 위치한다. 즉 이 코드의 호출스택을 그림으로 표현하면 다음과 같다.
이제 method3 메서드는 "Main.method3"을 출력한 뒤, System.out.println(0 / 0); 구문이 실행된다. 이 구문에서 0 / 0 연산이 발생하고, 이로 인해 ArithmeticException 예외가 발생한다. 이때 예외 클래스 ArithmeticException 클래스의 객체가 생성되며 예외가 발생한 지점부터 호출 스택을 따라 `try-catch` 블록을 찾는다.
호출 스택에 역순인 mathod3부터 main() 메서드까지 올라가 try-catch 블록을 찾는다. 만약 main() 메서드 까지에서도 예외를 처리하지 못한다면 JVM이 예외를 처리하게 되어 프로그램이 비정상적으로 종료되고 프로그램 종료 시, JVM은 예외에 대한 정보를 포함한 스택 트레이스를 콘솔에 출력한다.
- Exception in thread "main"은 예외가 main 스레드에서 발생했음을 나타낸다.
- java.lang.ArithmeticException: / by zero는 발생한 예외의 종류와 원인을 설명한다..
- at Main.method3(Main.java:12) 등은 예외가 발생한 위치와 호출 스택의 각 메서드에서 예외가 전파된 경로를 보여준다.
예외 처리
method1에서 thr-catch문을 사용하여 예외를 처리하였다.
public class Main {
public static void main(String[] args) {
method1();
}
public static void method1() {
System.out.println("Main.method1");
try {
method2();
}catch (ArithmeticException e) {
System.out.println(e.getMessage());
}
}
public static void method2() {
System.out.println("Main.method2");
method3();
}
public static void method3() {
System.out.println("Main.method3");
System.out.println(0 / 0);
}
}
예외 처리 궁금한점
try-catch문을 사용하여 예외를 처리하는 과정에 궁금한점이 생겼었다. 만약 예외가 발생하면 예외 발생 지점 이후 작성된 코드는 실행될까? 라는 생각을하였다.
예를 들어 다음과 코드이다. 코드를 실행하면 main() -> method1() -> method2 -> method3() 로 호출스택이 쌓일것이다. 예외가 발생하지 않는 경우, method3() 호출이 끝난 후에는 method2()의 "실행될까?" 문장이 정상적으로 출력될것이다. 그럼 예외가 발생하면 호출스택을 따라 올라간다고 하였는데 아래 "실행될까"라는 문장이 출력되는지 궁금하였다.
public class Main {
public static void main(String[] args) {
method1();
}
public static void method1() {
System.out.println("Main.method1");
try {
method2();
}catch (ArithmeticException e) {
System.out.println(e.getMessage());
}
}
public static void method2() {
System.out.println("Main.method2");
method3();
System.out.println("실행될까?");
}
public static void method3() throws ArithmeticException{
System.out.println("Main.method3");
System.out.println(0 / 0);
}
}
결과적으로, 실행되지 않았다라는것을 알수 있었다. 예외가 발생하면 예외 발생 지점 이후의 코드는 실행되지 않고, 호출 스택을 따라 위로 전파되며 적절한 try-catch 블록을 찾아서 예외를 처리하게 된다는것을 이해하였다. 이 과정에서 예외가 발생한 코드 이후의 모든 코드는 무시되며, 예외는 호출 스택을 통해 전파되어 가장 가까운 catch 블록에서 처리된다는것이다.
음 마지막으로 예외를 처리하고 나서는 어떻게 될까 궁금하였다...
public class Main {
public static void main(String[] args) {
method1();
}
public static void method1() {
System.out.println("Main.method1");
try {
method2();
}catch (ArithmeticException e) {
System.out.println(e.getMessage());
}
System.out.println("실행될까?");
}
public static void method2() {
System.out.println("Main.method2");
method3();
}
public static void method3() throws ArithmeticException{
System.out.println("Main.method3");
System.out.println(0 / 0);
}
}
실행된다. 예외가 발생하더라도 적잘한 예외를 처리하게되면 프로그램이 다시 정상적인 흐름으로 돌아간다는것을 이해했다.
'TIL' 카테고리의 다른 글
[TIL | 2024-08-30] 웹의 동작 원리와 웹 구성 요소 (0) | 2024.08.30 |
---|---|
[TIL | 2024-08-29] JVM, 자바 메모리 구조 (0) | 2024.08.29 |
[TIL | 2024-08-27] 추상 클래스, 추상 메서드, 인터페이스 (0) | 2024.08.29 |
[TIL | 2024-08-26] 상속, 오버라이딩 (0) | 2024.08.26 |
[TIL | 2024-08-24] 메서드, 프로그램의 기능적 분할, 객체 지향 프로그래밍 (0) | 2024.08.23 |