본문 바로가기

Java

try-catch-finally

try-catch-finally

 

try-catch-finally는 자바에서 예외 처리를 위한 구문입니다. 이 구문은 예외가 발생할 수 있는 코드 블록을 지정하고, 예외가 발생했을 때 예외를 처리하는 코드를 작성하며, 예외 발생 여부와 관계없이 항상 실행되어야 하는 코드를 정의하는 데 사용됩니다.

try-catch-finally 구문 형식은 다음과 같습니다

try {
    // 예외가 발생할 수 있는 코드
} catch (예외 타입1 변수1) {
    // 예외 처리 코드
} catch (예외 타입2 변수2) {
    // 예외 처리 코드
} finally {
    // 항상 실행되어야 하는 코드
}
  • try 블록: 예외가 발생할 수 있는 코드를 포함합니다. 이 블록 안에서 예외가 발생하면 해당 예외를 처리할 catch 블록으로 이동합니다.
  • catch 블록: 예외를 처리하는 코드를 작성하는 블록입니다. catch 블록은 예외 타입과 해당 예외를 참조할 변수를 지정하여 어떤 종류의 예외를 처리할지 정의합니다. 여러 개의 catch 블록을 사용하여 다양한 종류의 예외를 처리할 수 있습니다. 예외가 발생한 경우, 해당 예외와 일치하는 첫 번째 catch 블록으로 이동하여 예외 처리 코드를 실행합니다.
  • finally 블록: 예외 발생 여부와 관계없이 항상 실행되어야 하는 코드를 작성하는 블록입니다. finally 블록은 선택적으로 사용할 수 있으며, try-catch 구문의 마지막 부분에 위치합니다.

 

다음은 try-catch-finally 구문을 사용한 예제 코드입니다

public class TryCatchFinallyExample {
    public static void main(String[] args) {
        try {
            int result = divide(10, 0);
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Caught ArithmeticException: " + e.getMessage());
        } finally {
            System.out.println("Finally block executed");
        }
    }

    public static int divide(int dividend, int divisor) {
        return dividend / divisor;
    }
}


위의 예제에서 divide() 메서드에서는 0으로 나누는 연산을 수행하고 있습니다. 이 연산은 ArithmeticException을 발생시킵니다. try 블록에서는 이 코드를 실행하고, 예외가 발생하면 catch 블록으로 이동하여 해당 예외를 처리합니다. 마지막으로 finally 블록은 항상 실행되는 코드를 포함하고 있습니다.

다양한 try catch finally 예제들

1. 파일 입출력에서의 예외 처리

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FileExample {
    public static void main(String[] args) {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader("example.txt"));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("Error reading the file: " + e.getMessage());
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                System.out.println("Error closing the file: " + e.getMessage());
            }
        }
    }
}

위의 예제에서는 파일을 읽어오는 동안 발생할 수 있는 IOException을 처리하는 예제입니다. 파일을 열고 읽는 동안 예외가 발생하면 catch 블록으로 이동하여 예외를 처리하고, finally 블록에서는 파일을 닫는 작업을 수행합니다.

2. 네트워크 통신에서의 예외 처리

import java.io.IOException;
import java.net.Socket;

public class NetworkExample {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket("example.com", 8080);
            // 네트워크 통신 수행
        } catch (IOException e) {
            System.out.println("Error connecting to the server: " + e.getMessage());
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    System.out.println("Error closing the socket: " + e.getMessage());
                }
            }
        }
    }
}

위의 예제에서는 네트워크 통신 시 발생할 수 있는 IOException을 처리하는 예제입니다. 서버에 연결하는 동안 예외가 발생하면 catch 블록에서 예외를 처리하고, finally 블록에서는 소켓을 닫는 작업을 수행합니다.

3. 데이터베이스 연결에서의 예외 처리

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseExample {
    public static void main(String[] args) {
        Connection connection = null;
        try {
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "username", "password");
            // 데이터베이스 작업 수행
        } catch (SQLException e) {
            System.out.println("Error connecting to the database: " + e.getMessage());
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    System.out.println("Error closing the database connection: " + e.getMessage());
                }
            }
        }
    }
}

위의 예제는 데이터베이스 연결 시 발생할 수 있는 SQLException을 처리하는 예제입니다. 데이터베이스에 연결하는 동안 예외가 발생하면 catch 블록에서 예외를 처리하고, finally 블록에서는 데이터베이스 연결을 닫는 작업을 수행합니다.

4. 배열 인덱스 예외 처리

public class ArrayExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3};
        try {
            for (int i = 0; i <= numbers.length; i++) {
                System.out.println(numbers[i]);
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Array index out of bounds: " + e.getMessage());
        } finally {
            System.out.println("Finally block executed");
        }
    }
}

위의 예제에서는 배열의 범위를 벗어나는 인덱스에 접근하는 예외를 처리하는 예제입니다. 예외가 발생하면 catch 블록에서 예외를 처리하고, finally 블록은 항상 실행됩니다.

5. 숫자 변환 예외 처리

public class NumberExample {
    public static void main(String[] args) {
        String number = "abc";
        try {
            int value = Integer.parseInt(number);
            System.out.println("Parsed value: " + value);
        } catch (NumberFormatException e) {
            System.out.println("Error parsing the number: " + e.getMessage());
        } finally {
            System.out.println("Finally block executed");
        }
    }
}

위의 예제는 숫자로 변환할 수 없는 문자열을 숫자로 변환하려고 할 때 발생하는 NumberFormatException을 처리하는 예제입니다. 예외가 발생하면 catch 블록에서 예외를 처리하고, finally 블록은 항상 실행됩니다.

6. 외부 리소스 해제

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

public class ResourceExample {
    public static void main(String[] args) {
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream("file.txt");
            // 파일에서 데이터를 읽어옴
        } catch (IOException e) {
            System.out.println("Error reading the file: " + e.getMessage());
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    System.out.println("Error closing the file: " + e.getMessage());
                }
            }
        }
    }
}

위의 예제에서는 파일을 읽어오는 동안 발생할 수 있는 IOException을 처리하고, 파일을 닫는 작업을 finally 블록에서 수행합니다. try-catch-finally 구문을 사용하여 예외 처리와 리소스의 해제를 보장합니다.

7. 사용자 정의 예외 처리

class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}

public class CustomExceptionExample {
    public static void main(String[] args) {
        try {
            int result = divide(10, 0);
            System.out.println("Result: " + result);
        } catch (CustomException e) {
            System.out.println("Caught CustomException: " + e.getMessage());
        } finally {
            System.out.println("Finally block executed");
        }
    }

    public static int divide(int dividend, int divisor) throws CustomException {
        if (divisor == 0) {
            throw new CustomException("Divisor cannot be zero");
        }
        return dividend / divisor;
    }
}

위의 예제에서는 사용자 정의 예외인 CustomException을 정의하고, 해당 예외가 발생할 경우 예외를 처리하는 예제입니다. divide() 메서드에서는 0으로 나누는 연산 시 CustomException을 발생시키고, catch 블록에서 해당 예외를 처리합니다.

8. 다중 예외 처리

public class MultipleExceptionExample {
    public static void main(String[] args) {
        try {
            String str = null;
            int length = str.length();
            int result = divide(10, 0);
            System.out.println("Result: " + result);
        } catch (NullPointerException | ArithmeticException e) {
        
        	if (e instanceof NullPointerException) {
                System.out.println("Caught NullPointerException: " + e.getMessage());
            } else if (e instanceof ArithmeticException) {
                System.out.println("Caught ArithmeticException: " + e.getMessage());
            } else {
                System.out.println("Caught exception: " + e.getMessage());
            }
            
        } finally {
            System.out.println("Finally block executed");
        }
    }

    public static int divide(int dividend, int divisor) {
        return dividend / divisor;
    }
}

위의 예제에서는 NullPointerException과 ArithmeticException을 동시에 처리하는 다중 예외 처리를 보여줍니다. `try` 블록에서 NullPointerException이 발생하면 해당 예외를 처리하고, 이어서 ArithmeticException이 발생하면 같은 catch 블록에서 처리합니다.

 

9. 예외 전파

public class ExceptionPropagationExample {
    public static void main(String[] args) {
        try {
            someMethod();
        } catch (Exception e) {
            System.out.println("Caught exception: " + e.getMessage());
        } finally {
            System.out.println("Finally block executed");
        }
    }

    public static void someMethod() throws Exception {
        anotherMethod();
    }

    public static void anotherMethod() throws Exception {
        throw new Exception("Exception thrown from anotherMethod");
    }
}

위의 예제에서는 예외를 다른 메서드로 전파하는 예제입니다. anotherMethod()에서 발생한 예외가 someMethod()로 전파되고, 이어서 main() 메서드에서 처리됩니다.

 

10. printStackTrace() 메서드 예제

public class PrintStackTraceExample {
    public static void main(String[] args) {
        try {
            int result = divide(10, 0);
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Caught ArithmeticException: " + e.getMessage());
            e.printStackTrace();
        } finally {
            System.out.println("Finally block executed");
        }
    }

    public static int divide(int dividend, int divisor) {
        return dividend / divisor;
    }
}

위의 예제에서는 ArithmeticException이 발생할 때 catch 블록에서 해당 예외를 처리하고, e.getMessage()를 통해 예외 메시지를 출력합니다. 그리고 e.printStackTrace()를 호출하여 예외의 스택 트레이스를 콘솔에 출력합니다.

11. getMessage() 메서드 예제

public class GetMessageExample {
    public static void main(String[] args) {
        try {
            String str = null;
            int length = str.length();
            System.out.println("String length: " + length);
        } catch (NullPointerException e) {
            System.out.println("Caught NullPointerException: " + e.getMessage());
        } finally {
            System.out.println("Finally block executed");
        }
    }
}

위의 예제에서는 NullPointerException이 발생할 때 catch 블록에서 해당 예외를 처리하고, e.getMessage()를 통해 예외 메시지를 출력합니다.

이러한 예제들은 printStackTrace() 메서드를 사용하여 예외의 스택 트레이스를 출력하고, getMessage() 메서드를 사용하여 예외의 메시지를 출력하는 방법을 보여줍니다. 이를 통해 예외에 대한 정보를 얻거나 디버깅에 도움을 줄 수 있습니다.

 

이러한 예제들은 try-catch-finally 구문을 활용하여 예외 상황에 대한 명확한 처리와 정리 작업을 수행하는 방법을 보여줍니다. 예외가 발생한 후 `catch` 블록이 실행되고, 이어서 finally 블록이 실행된 것을 확인할 수 있습니다.

이처럼 try-catch-finally 구문은 예외 처리와 관련된 코드를 작성할 때 유용하게 사용됩니다. catch 블록은 예외를 처리하고, finally 블록은 항상 실행되어야 하는 정리 작업 등을 수행할 때 사용됩니다.

 

throw

throw는 자바에서 예외를 명시적으로 발생시키는 키워드입니다. 예외가 발생한 상황을 나타내고, 해당 예외를 호출자에게 전달하는 역할을 합니다. Throw를 사용하여 예외를 발생시키면 프로그램의 흐름이 중단되고 예외 처리 메커니즘이 시작됩니다.

throw의 기본 구문은 다음과 같습니다:

throw <예외 객체>;
  • <예외 객체>: 발생시킬 예외를 나타내는 객체입니다. 주로 Exception 클래스 또는 그 하위 클래스의 객체를 사용합니다. 개발자가 직접 예외 객체를 생성하여 사용할 수도 있습니다.

throw 키워드를 사용하여 예외를 발생시키면, 예외 객체는 호출 스택을 따라 위로 전파됩니다. 예외를 처리할 수 있는 catch 블록이 없는 경우, 예외는 호출 스택을 거슬러 올라가며 처리를 찾습니다. 이러한 과정에서 예외가 처리되지 않으면 프로그램은 비정상적으로 종료됩니다.

throw는 주로 다음과 같은 상황에서 사용됩니다:

  1. 예외 조건이 발생했을 때 해당 예외를 명시적으로 발생시키고자 할 때.
  2. 특정 상황에서 프로그램을 중지시키고 예외 처리 메커니즘을 트리거할 때.
  3. 사용자 정의 예외를 생성하고 발생시킬 때.

throw를 사용하여 예외를 발생시킴으로써 예외 상황을 알리고, 이에 대한 적절한 처리를 수행할 수 있습니다. 예외 처리는 프로그램의 안정성과 신뢰성을 높이는 데 중요한 역할을 합니다.

 

다음은 throw 구문을 사용한 예제 코드입니다

public class ThrowExample {
    public static void main(String[] args) {
        try {
            throwException();
        } catch (Exception e) {
            System.out.println("Caught exception: " + e.getMessage());
        }
    }

    public static void throwException() throws Exception {
        throw new Exception("Custom exception message");
    }
}

 

위의 코드에서는 throwException() 메서드에서 Exception 객체를 직접 생성하여 throw 키워드를 사용하여 예외를 발생시킵니다. throwException() 메서드에서는 throws Exception을 선언하여 해당 메서드에서는 예외를 처리하지 않고, 호출한 곳으로 예외를 전파합니다.

 

다양한 Throw 예제들

1. 파일을 읽는 메서드에서 IOException을 전파하는 예시:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FileProcessor {
    public void readFile(String fileName) throws IOException {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(fileName));
            String line;
            while ((line = reader.readLine()) != null) {
                // 파일 데이터 처리
            }
        } finally {
            if (reader != null) {
                reader.close();
            }
        }
    }
}


2. 사용자 정의 예외를 선언하고 전파하는 예시:

public class CustomExceptionExample {
    public void performOperation() throws CustomException {
        // 예외가 발생할 수 있는 작업
        if (/* 예외 조건 */) {
            throw new CustomException("Custom exception occurred");
        }
    }
}

class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}


3. 여러 예외를 선언하고 전파하는 예시:

public class MultipleExceptionsExample {
    public void performOperation() throws IOException, InterruptedException {
        // 예외가 발생할 수 있는 작업
        if (/* IOException 발생 조건 */) {
            throw new IOException("IO exception occurred");
        }
        if (/* InterruptedException 발생 조건 */) {
            throw new InterruptedException("Interrupted exception occurred");
        }
    }
}


4. 메서드 체인에서 예외를 전파하는 예시:

public class MethodChainExample {
    public void performOperation() throws IOException {
        // 예외가 발생할 수 있는 작업
        validateData();
    }

    public void validateData() throws IOException {
        // 예외가 발생할 수 있는 작업
        processData();
    }

    public void processData() throws IOException {
        // 예외가 발생할 수 있는 작업
        throw new IOException("IO exception occurred");
    }
}


5. 메서드 호출 시 예외를 처리하거나 전파하는 예시:

public class ExceptionHandlingExample {
    public void performOperation() {
        try {
            // 예외가 발생할 수 있는 작업
            divide(10, 0);
        } catch (ArithmeticException e) {
            // 예외 처리
        }
    }

    public void divide(int num1, int num2) throws ArithmeticException {
        if (num2 == 0) {
            throw new ArithmeticException("Cannot divide by zero");
        }
        // 나누기 연산 수행
    }
}

 

6. Checked 예외와 Unchecked 예외를 구분하는 예제

public class ExceptionTypesExample {
    public void processFile(String fileName) throws IOException {
        // 파일 처리 작업
        if (/* IOException 발생 조건 */) {
            throw new IOException("IO exception occurred");
        }

        if (/* Unchecked 예외 발생 조건 */) {
            throw new IllegalArgumentException("Invalid argument");
        }
    }
}

 

7. Checked 예외를 try-with-resources 구문으로 처리

import java.io.FileReader;
import java.io.IOException;
import java.io.BufferedReader;

public class TryWithResourcesExample {
    public void readFile(String fileName) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            while ((line = reader.readLine()) != null) {
                // 파일 데이터 처리
            }
        }
    }
}

이러한 예시들은 throws를 사용하여 예외를 선언하고 전파하는 다양한 상황을 보여줍니다. 각 예시는 특정한 상황에 맞춰 예외 처리를 적절하게 구현하는 방법을 보여줍니다.

 

 

throw 을 사용할 때 주의해야 할 몇 가지 사항이 있습니다

1. 예외의 적절한 선택: 예외는 예상 가능하고 복구 가능한 상황에서 발생해야 합니다. 따라서 예외를 발생시킬 때에는 예외의 의미와 상황에 적합한 예외 클래스를 선택해야 합니다. 자바에서는 다양한 예외 클래스를 제공하므로 적절한 예외 클래스를 선택하는 것이 중요합니다.

2. 예외 처리 메커니즘 활용: 예외를 발생시킨다면, 그 예외를 적절하게 처리할 수 있는 예외 처리 메커니즘을 구현해야 합니다. try-catch 블록을 사용하여 예외를 처리하거나, 호출자에게 예외를 전파하여 처리하도록 할 수 있습니다. 예외 발생 후에는 프로그램의 정상적인 흐름을 유지하기 위해 적절한 예외 처리를 해주어야 합니다.

3. 과용하지 않기: 예외는 예외적인 상황에서 발생해야 하므로, 예외를 너무 많이 사용하거나 모든 상황에 대해 예외를 발생시키는 것은 좋지 않습니다. 일반적인 흐름 제어나 오류 처리는 예외보다 다른 방식으로 처리하는 것이 바람직합니다. 예외는 예외 상황에만 사용되어야 합니다.

4. 성능에 주의: 예외 처리는 추가적인 비용이 발생할 수 있으므로, 성능에 영향을 줄 수 있습니다. 예외를 발생시키는 작업은 상대적으로 비용이 크므로, 성능에 민감한 부분에서 너무 자주 예외를 발생시키는 것은 피해야 합니다.

5. 예외의 적절한 문서화: 예외를 발생시킬 때 해당 예외에 대한 문서화를 제공해야 합니다. 예외의 발생 조건, 예외 메시지, 호출자가 예외를 처리해야 하는 방법 등을 문서화하여 개발자에게 명확하게 전달해야 합니다.

예외 처리는 프로그램의 신뢰성과 안정성을 높이는 데 중요한 역할을 합니다. 따라서 예외를 발생시킬 때는 신중하게 고려하고 적절한 예외 처리를 수행해야 합니다.

 

각각의 주의사항에 해당하는 예제 코드

1. 예외의 적절한 선택:

public class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}

public class ExceptionSelectionExample {
    public static void main(String[] args) {
        try {
            throw new CustomException("Custom exception occurred");
        } catch (CustomException e) {
            System.out.println("Caught CustomException: " + e.getMessage());
        }
    }
}

위의 코드에서는 CustomException 클래스를 사용하여 예외를 발생시킵니다. 이 예제에서는 예외의 의미와 상황에 맞는 예외 클래스를 선택하는 예시입니다.

2. 예외 처리 메커니즘 활용:

public class ExceptionHandlingExample {
    public static void main(String[] args) {
        try {
            int result = divide(10, 0);
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Caught ArithmeticException: " + e.getMessage());
        }
    }

    public static int divide(int dividend, int divisor) {
        try {
            return dividend / divisor;
        } catch (ArithmeticException e) {
            throw e;
        }
    }
}


위의 코드에서는 divide() 메서드에서 ArithmeticException이 발생하면 예외를 다시 `throw`하여 호출자에게 전파합니다. 이 예제에서는 예외 처리 메커니즘을 활용하여 예외를 전파하는 방식을 보여줍니다.

3. 과용하지 않기:

public class AvoidExceptionOveruseExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
        for (int i = 0; i <= numbers.length; i++) {
            try {
                System.out.println(numbers[i]);
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("Caught ArrayIndexOutOfBoundsException: " + e.getMessage());
            }
        }
    }
}

위의 코드에서는 배열의 범위를 벗어나는 인덱스에 접근하는 예외를 처리합니다. 이 예제에서는 예외를 피하기 위해 배열의 범위를 체크하여 예외 발생을 방지하는 방식을 보여줍니다.

4. 성능에 주의:

public class PerformanceConsiderationExample {
    public static void main(String[] args) {
        try {
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                process(i);
            }
        } catch (OutOfMemoryError e) {
            System.out.println("Caught OutOfMemoryError: " + e.getMessage());
        }
    }

    public static void process(int value) {
        // 예외적인 상황이 아니라면 예외를 발생시키지 않음
        if (value % 1000 == 0) {
            throw new OutOfMemoryError("Out of memory");
        }
    }
}


위의 코드에서는 예외를 피하기 위해 process() 메서드에서 예외를 발생시키지 않는 경우를 처리합니다. 이 예제에서는 성능에 영향을 줄 수 있는 예외 발생을 피하는 방식을 보여줍니다.

5. 예외의 적절한 문서화:

public class ExceptionDocumentationExample {
    public static void main(String[] args) {
        try {
            process(null);
        } catch (NullPointerException e) {
            System.out.println("Caught NullPointerException: " + e.getMessage());
        }
    }

    public static void process(String str) {
        if (str == null) {
            throw new NullPointerException("String cannot be null");
        }
    }
    
}

 

throws

throws는 메서드 선언부에서 해당 메서드에서 발생할 수 있는 예외를 명시하는 키워드입니다. throws를 사용하여 예외를 선언하면, 메서드 호출자에게 해당 예외를 처리하도록 요구할 수 있습니다. 이를 통해 예외 처리의 책임을 메서드 호출자에게 위임할 수 있습니다.

throws의 기본 구문은 다음과 같습니다:

[접근 제어자] 반환타입 메서드명([매개변수]) throws 예외클래스1, 예외클래스2, ... {
    // 메서드 내용
}
  • 예외클래스1, 예외클래스2, ...: 해당 메서드에서 발생할 수 있는 예외 클래스들을 나열합니다. 각 예외 클래스는 쉼표로 구분됩니다.

throws 를 사용하여 예외를 선언하면, 메서드에서 발생하는 예외를 호출자에게 알리고, 호출자가 해당 예외를 처리하도록 할 수 있습니다. 호출자는 선언된 예외를 `try-catch` 블록으로 처리하거나, 자신을 호출한 메서드로 예외를 전파할 수 있습니다.

 

throws 는 주로 다음과 같은 상황에서 사용됩니다:

1. 예외를 처리할 책임이 호출자에게 있을 때: 메서드가 예외를 처리할 수 있는 능력이 부족하거나, 메서드 외부에서 예외를 처리할 필요가 있는 경우 throws 를 사용하여 예외 처리를 호출자에게 위임할 수 있습니다. 이렇게 함으로써 예외 처리의 책임을 분산시킬 수 있습니다.

2. 예외가 발생할 수 있는 경우를 명시할 때: 메서드가 예외를 발생시킬 수 있는 경우, throws를 사용하여 호출자에게 해당 예외의 발생 가능성을 알려줄 수 있습니다. 이는 명시적인 예외 처리를 유도하고, 코드의 가독성과 안정성을 높이는 데 도움이 됩니다.

3. 체크된 예외를 처리할 필요가 있는 경우: 체크된 예외는 컴파일러가 예외 처리를 강제하는 예외로, throws 를 사용하여 호출자에게 예외 처리를 요구할 수 있습니다. 이는 컴파일러에 의한 예외 처리 검사를 통해 예외를 처리하는 코드를 보다 안전하게 구현할 수 있도록 합니다.

4. 예외 처리의 표준화를 위해: 여러 메서드에서 동일한 예외를 처리해야 할 경우, 예외를 throws 로 선언하여 예외 처리를 통일할 수 있습니다. 이는 코드의 일관성과 유지 보수성을 향상시키는 데 도움이 됩니다.

throws 는 예외 처리를 유연하게 구현할 수 있는 도구이지만, 과용될 경우 코드의 가독성이 저하되거나 예외 처리의 복잡성이 증가할 수 있습니다. 따라서 throws 를 사용할 때는 신중하게 예외 처리의 적절성을 판단하고, 코드의 일관성과 가독성을 유지하는 데 주의해야 합니다.



다음은 `throws`를 사용하는 예제 코드입니다:

public class ThrowsExample {
    public static void main(String[] args) {
        try {
            process();
        } catch (CustomException e) {
            System.out.println("Caught CustomException: " + e.getMessage());
        }
    }

    public static void process() throws CustomException {
        // 예외 발생 조건
        if (/* 예외 조건 */) {
            throw new CustomException("Custom exception occurred");
        }

        // 예외가 발생하지 않은 경우 정상적으로 처리
    }
}

class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}

위의 코드에서 process() 메서드는 throws CustomException을 선언하여 CustomException 예외를 처리하지 않고 호출자로 전파합니다. `main()` 메서드에서는 process() 메서드를 호출하고, catch 블록에서 CustomException을 처리합니다.

다양한 throws 예제들

1. 여러 예외를 동시에 처리하기 위한 throws 구문

import java.io.*;

public class MultipleExceptionsExample {
    public static void main(String[] args) {
        try {
            process();
        } catch (FileNotFoundException | IOException e) {
            System.out.println("Caught Exception: " + e.getMessage());
        }
    }

    public static void process() throws FileNotFoundException, IOException {
        FileInputStream file = new FileInputStream("file.txt");
        // 파일에서 데이터 읽어오는 작업
        file.close();
    }
}

위의 코드에서 process() 메서드에서 throws FileNotFoundException, IOException을 선언하여 파일 처리 중 발생할 수 있는 두 가지 예외를 호출자로 전파합니다. main() 메서드에서는 이를 동시에 처리하기 위해 catch 블록에 FileNotFoundException | IOException을 사용합니다.

2. 상속 관계에 있는 예외를 처리하기 위한 throws 구문

import java.rmi.RemoteException;
import java.rmi.NotBoundException;

public class RemoteExceptionExample {
    public static void main(String[] args) {
        try {
            connectToServer();
        } catch (RemoteException e) {
            System.out.println("Caught RemoteException: " + e.getMessage());
        } catch (NotBoundException e) {
            System.out.println("Caught NotBoundException: " + e.getMessage());
        }
    }

    public static void connectToServer() throws RemoteException, NotBoundException {
        // 서버에 연결하는 작업
    }
}

위의 코드에서 connectToServer() 메서드에서 throws RemoteException, NotBoundException을 선언하여 RMI(Remote Method Invocation) 관련 예외를 호출자로 전파합니다. main() 메서드에서는 각각의 예외를 처리하기 위해 별도의 catch 블록을 사용합니다.

3. 사용자 정의 예외를 throws로 전파하기

class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}

public class CustomExceptionExample {
    public static void main(String[] args) {
        try {
            throwCustomException();
        } catch (CustomException e) {
            System.out.println("Caught CustomException: " + e.getMessage());
        }
    }

    public static void throwCustomException() throws CustomException {
        throw new CustomException("Custom exception occurred");
    }
}

위의 코드에서 throwCustomException() 메서드에서 throws CustomException을 선언하여 사용자 정의 예외인 CustomException을 호출자로 전파합니다. main() 메서드에서는 이 예외를 처리하기 위해 catch 블록을 사용합니다.

 

4.  `throws`를 사용하여 예외를 다시 던지기 (Exception Re-Throwing)

import java.io.IOException;

public class RethrowExceptionExample {
    public static void main(String[] args) {
        try {
            processFile("file.txt");
        } catch (IOException e) {
            System.out.println("Caught IOException: " + e.getMessage());
        }
    }

    public static void processFile(String fileName) throws IOException {
        try {
            // 파일을 열고 데이터 처리 작업
        } catch (IOException e) {
            // 예외를 처리하지 않고 다시 던짐
            throw e;
        }
    }
}


위의 코드에서 processFile() 메서드에서는 파일을 처리하다가 IOException이 발생하면 예외를 다시 던집니다. main() 메서드에서는 이 예외를 처리하기 위해 catch 블록을 사용합니다.

5. 여러 예외를 선언하고 전파하는 예제

public class MultipleExceptionsExample {
    public void performOperation() throws IOException, InterruptedException {
        // 예외가 발생할 수 있는 작업
        if (/* IOException 발생 조건 */) {
            throw new IOException("IO exception occurred");
        }
        if (/* InterruptedException 발생 조건 */) {
            throw new InterruptedException("Interrupted exception occurred");
        }
    }
}

 

6. 메서드 체인에서 예외를 전파하는 예제

public class MethodChainExample {
    public void performOperation() throws IOException {
        // 예외가 발생할 수 있는 작업
        validateData();
    }

    public void validateData() throws IOException {
        // 예외가 발생할 수 있는 작업
        processData();
    }

    public void processData() throws IOException {
        // 예외가 발생할 수 있는 작업
        throw new IOException("IO exception occurred");
    }
}

각 예제는 throws를 사용하여 예외를 선언하고, 호출자에게 예외 처리를 요구하는 방식을 보여줍니다. 이를 통해 예외 처리를 더욱 유연하게 구현할 수 있습니다.

 

 

throws 키워드를 사용할 때 주의해야 할 사항들은 다음과 같습니다:

1. checked 예외와 unchecked 예외를 구분해야 합니다: checked 예외는 RuntimeException 클래스를 상속하지 않는 예외들을 의미합니다. checked 예외를 던지는 경우, 해당 예외를 처리하거나 throws 선언을 통해 호출자에게 전파해야 합니다. 반면, unchecked 예외는 RuntimeException 클래스를 상속하는 예외들을 의미하며, 이러한 예외는 반드시 처리하지 않아도 됩니다.

public void readData() throws IOException {
    // IOException을 던질 수 있는 작업
}

위의 코드에서 IOException은 checked 예외입니다. 따라서 readData 메서드에서는 IOException을 처리하거나 throws 선언으로 호출자에게 전파해야 합니다. checked 예외는 반드시 예외 처리나 전파가 필요하므로 이에 주의해야 합니다.

2. 예외 전파는 필요한 경우에만 사용해야 합니다: throws를 사용하여 예외를 전파하는 것은 호출자에게 예외 처리를 위임하는 것을 의미합니다. 하지만 모든 상황에서 예외를 전파하는 것은 바람직하지 않을 수 있습니다. 예외를 처리하고 복구할 수 있는지를 고려해야 합니다.

public void processFile(String fileName) throws IOException {
    // 파일을 처리하는 작업
    readData(fileName); // IOException 발생 가능

    // 나머지 작업
}

위의 코드에서 processFile 메서드에서 readData 메서드를 호출하면서 IOException이 발생할 수 있습니다. 따라서 processFile 메서드에서는 IOException을 처리하지 않고 호출자에게 예외를 전파합니다. 이 경우에는 processFile 메서드의 호출자에서 예외 처리를 해주어야 합니다.

3. 예외 전파에 대한 문서화가 필요합니다: throws 문을 사용하여 예외를 전파할 때는 해당 메서드의 API 문서에 명확하게 어떤 예외가 발생할 수 있는지 기술하는 것이 좋습니다. 이는 사용자가 메서드를 올바르게 호출하고 예외를 처리할 수 있도록 도와줍니다.

/**
 * 파일을 읽어오는 메서드입니다.
 * @param fileName 읽을 파일의 이름
 * @throws IOException 파일을 읽는 도중 IO 오류가 발생한 경우
 */
public void readData(String fileName) throws IOException {
    // 파일을 읽는 작업
}

위의 코드에서는 Javadoc 주석을 사용하여 readData 메서드가 IOException을 발생시킬 수 있다는 것을 문서화했습니다. 이를 통해 사용자는 해당 메서드가 어떤 예외를 던질 수 있는지 알 수 있습니다.

4. 예외 처리의 적절성을 고려해야 합니다: throws를 사용하여 예외를 전파하는 것은 항상 최선의 선택은 아닙니다. 예외를 전파하기 전에 예외를 적절하게 처리하고 복구하는 것이 더 나은 방법일 수 있습니다. 예외 처리의 적절성을 고려하고, 예외를 처리하거나 전파해야 할 때만 throws를 사용해야 합니다.

public void readData(String fileName) {
    try {
        // 파일을 읽는 작업
    } catch (IOException e) {
        // 예외 처리 및 복구 작업
    }
}

위의 코드에서는 readData 메서드 내부에서 예외를 처리하고 복구하는 작업을 수행합니다. 예외를 적절하게 처리하고 복구할 수 있는 경우에는 예외를 전파하지 않고 메서드 내부에서 처리하는 것이 좋습니다.

5. 예외 전파 시 코드 가독성을 유지해야 합니다: throws를 사용하여 예외를 전파할 때, 메서드 시그니처가 복잡해지고 코드의 가독성이 저하될 수 있습니다. 적절한 예외 처리와 전파를 위해 코드의 가독성을 유지하는 노력이 필요합니다.

public void processData(String fileName) throws IOException {
    // 코드 작성
    readData(fileName); // IOException 발생 가능
    // 코드 작성
}

예외를 전파하는 경우에도 코드의 가독성을 유지해야 합니다. processData 메서드에서 readData 메서드를 호출하면서 IOException이 발생할 수 있다는 것을 명시적으로 표현하고 있습니다.

6. 호출자가 예외를 처리하지 않는 경우 컴파일 경고가 발생합니다: throws를 사용하여 예외를 전파할 때, 호출자가 해당 예외를 처리하지 않는다면 컴파일러는 경고를 표시하거나 컴파일 에러를 발생시킬 수 있습니다. 호출자가 예외를 처리할 수 있도록 적절한 예외 처리를 유도해야 합니다.

public void processData(String fileName) throws IOException {
    // 코드 작성
    readData(fileName); // IOException 발생 가능
    // 코드 작성
}

위의 코드에서 processData 메서드에서 readData 메서드를 호출하고 IOException을 throws 선언으로 전파합니다. 그러나 호출자가 IOException을 처리하지 않는다면 컴파일 경고가 발생할 수 있습니다. 이러한 경고를 주의하여 처리하는 것이 중요합니다.

7. throws는 메서드 시그니처에만 사용해야 합니다: throws는 메서드 선언부에서 사용되며, 메서드에서 발생할 수 있는 예외를 선언합니다. throws는 변수, 생성자 또는 일반 코드 블록에서 사용할 수 없습니다.

 

 

Chained Exception

자바에서 Chained Exception은 예외 처리 시 발생한 예외를 다른 예외에 첨부하는 기능을 제공합니다. 이 기능은 디버깅과 예외 처리 과정에서 유용하며, 예외의 원인과 관련된 정보를 추적하는 데 도움을 줍니다.

Chained Exception을 사용하기 위해선 `Throwable` 클래스의 생성자 중 하나를 호출하면서 원인 예외를 전달해야 합니다. 대표적인 생성자는 다음과 같습니다.

public Throwable(Throwable cause)
public Throwable(String message, Throwable cause)

위의 생성자를 사용하여 체인 예외를 생성할 수 있습니다. 체인 예외를 발생시키는 코드는 아래와 같습니다.

try {
    // 예외가 발생할 수 있는 코드
} catch (Exception e) {
    // 원인 예외를 포함한 체인 예외 생성
    throw new Exception("Additional information", e);
}


Chained Exception 예제

1. 단순한 Chained Exception 예제

public class ChainedExceptionExample {
    public static void main(String[] args) {
        try {
            divideByZero();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void divideByZero() throws Exception {
        try {
            int result = 5 / 0;
        } catch (ArithmeticException e) {
            throw new Exception("Divide by zero", e);
        }
    }
}

위의 코드에서는 divideByZero() 메서드에서 0으로 나누는 산술 예외가 발생하고, 이 예외를 포함한 체인 예외를 생성합니다. 예외가 발생한 후 printStackTrace() 메서드를 호출하여 예외의 스택 트레이스를 출력합니다.

2. 중첩된 Chained Exception 예제:

public class ChainedExceptionExample {
    public static void main(String[] args) {
        try {
            readConfig();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void readConfig() throws Exception {
        try {
            readFile();
        } catch (IOException e) {
            throw new Exception("Error reading configuration", e);
        }
    }

    public static void readFile() throws IOException {
        throw new IOException("File not found");
    }
}

위의 코드에서는 readConfig() 메서드에서 readFile() 메서드를 호출하고, readFile() 메서드에서 IOException을 발생시킵니다. 이 예제에서는 중첩된 Chained Exception을 보여주고 있습니다. readConfig()에서 발생한 IOException을 포함한 체인 예외를 생성하고 출력합니다.

Chained Exception은 예외 처리 과정에서 문제의 원인을 추적하기 위해 유용합니다. 원인 예외와 상세한 정보를 함께 제공하여 디버깅 및 예외 처리를 용이하게 합니다.

'Java' 카테고리의 다른 글

String Class  (0) 2024.04.08
Call by Value / Call by Reference  (0) 2024.04.08
Anonymous Class  (0) 2024.04.08
Nested Classes(중첩 클래스)  (0) 2024.04.08
Method Class  (0) 2024.04.08