[기술면접] JAVA - 2/4 (주로 예외처리Exception)

2023. 3. 3. 13:38기술 면접 준비

[기술면접] JAVA - 2/4의 목차

  • Inner Class(내부 클래스)의 장점
  • 리플렉션(Reflection)이란 
  • Error와 Exception의 차이
  • CheckedException과 UnCheckedException의 차이와  RuntimeException에 대해 설명해주세요.
  • Optional API

 

 


 

 

😎 Inner Class(내부 클래스)의 장점에 대해 설명해주세요.

1. 내부 클래스에서 외부 클래스의 멤버에 손쉽게 접근할 수 있다.
2. 서로 관련 있는 클래스를 논리적으로 묶어서 표현함으로써, 코드의 캡슐화를 증가시킨다.
3. 외부에서는 내부 클래스에 접근할 수 없으므로, 코드의 복잡성을 줄일 수 있다.

 

 

 

😎 리플렉션(Reflection)이란 무엇인지 설명해주세요.

리플렉션이란

  • 구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API 입니다.
  • 힙 영역에 로드된 Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API이다.

 

▶꼬리질문 - 리플렉션의 장단점에 대해 말씀해주세요.

  • 장점
    • 런타임 시점에서 클래스의 인스턴스를 생성하고, 접근 제어자와 관계 없이 필드와 메소드에 접근하여 필요한 작업을 수행할 수 있는 유연성을 가지고 있다.
  • 단점
    • 캡슐화를 저해한다.
    • 런타임 시점에서 인스턴스를 생성하므로 컴파일 시점에서 해당 타입을 체크할 수 없다.
    • 런타임 시점에서 인스턴스를 생성하므로 구체적인 동작 흐름을 파악하기 어렵다.
    • 단순히 필드 및 메소드를 접근할 때보다 리플렉션을 사용하여 접근할 때 성능이 느리다. (모든 상황에서 성능이 느리지는 않음.)

 

 

▶꼬리질문 - 리플렉션은 어떤 경우에 사용되는지 설명해주세요.

코드를 작성할 시점에는 어떤 타입의 클래스를 사용할지 모르지만, 런타임 시점에 지금 실행되고 있는 클래스를 가져와서 실행해야 하는 경우에 사용됩니다.

 

프레임워크나 IDE에서 이런 동적인 바인딩을 이용한 기능을 제공하는데요.

intelliJ의 자동완성 기능, 스프링의 어노테이션이 리플렉션을 이용한 기능이라 할 수 있습니다.

 

 

그리고

큰 규모의 개발 단계에서는 수많은 객체와 의존 관계를 파악하기 어렵다. 이때 리플렉션을 사용하면 동적으로 클래스를 만들어서 의존 관계를 맺어줄 수 있다.

가령, Spring의 Bean Factory를 보면, @Controller, @Service, @Repository 등의 어노테이션만 붙이면 Bean Factory에서 알아서 해당 어노테이션이 붙은 클래스를 생성하고 관리해 주는 것을 알 수 있다.

개발자는 Bean Factory에 해당 클래스를 알려준 적이 없는데, 이것이 가능한 이유는 바로 리플렉션 덕분이다.

런타임에 해당 어노테이션이 붙은 클래스를 탐색하고 발견한다면, 리플렉션을 통해 해당 클래스의 인스턴스를 생성하고 필요한 필드를 주입하여 Bean Factory에 저장하는 식으로 사용이 된다.

 

 

 

 

😎 Error와 Exception의 차이를 설명해주세요.

  • Error는 실행 중 일어날 수 있는 치명적 오류를 말합니다.
    • 컴파일 시점에 체크할 수 없고, 오류가 발생하면 프로그램은 비정상 종료되며 예측 불가능한 UncheckedException에 속합니다. 
    • 런타임에서 실행 시 발생되며 전부 예측 불가능한 Unchecked Error에 속한다.
  • 반면, Exception은 Error보다 비교적 경미한 오류이며, try-catch를 이용해 프로그램의 비정상 종료를 막을 수 있습니다.

 

▶꼬리질문 - 예외처리(Exception) 자세히 알아보기

  • 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
  • 자바가 제공하는 예외 계층 구조
  • RuntimeException과 RuntimeException가 아닌 것의 차이는?
  • 커스텀한 예외 만드는 방법

 

자바에서 예외 처리 방법 (try, catch, throw, throws, finally)

자바에서 예외 처리란, 프로그램 실행 중 발생할 수 있는 예기치 못한 예외 발생에 대비한 코드를 작성하는 것이며,

예외처리의 목적은 예외의 발생으로 인한 실행중인 프로그램의 갑작스런 비정상 종료를 막고 실행상태를 유지할 수 있도록 함

 

예외처리(Exception handling)
정의 - 프로그램 실행 시 발생할 수 있는 예외 발생에 대비한 코드를 작성하는 것
목적 - 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것

 

자바의 예외 처리는

  • 예외가 발생한 메소드 내에서 직접 처리하는 방법
  • 예외가 발생한 메소드를 호출한 곳으로 예외 객체를 넘겨주는 방법
  • 사용자 정의 예외를 생성하여 처리하는 방법이 있다.

 

1. 예외가 발생한 메소드 내에서 직접 처리 ( try - catch - finally)

try {
 
	예외 발생 가능성이 있는 문장들;
 
}catch(예외 타입1 매개변수명) {
	
    예외타입1의 예외가 발생할 경우 처리 문장들;
 
}catch(예외 타입2 매개변수명) {
	
    예외타입2의 예외가 발생할 경우 처리 문장들;
 
}finally {
	
    항상 수행할 필요가 있는 문장들;
}

try  블록은  예외가  발생할  가능성이  있는  범위를  지정하는  블록이다.  try  블록은  최소한  하나의  catch  블록이  있어야  하며, catch 블록은  try  블록  다음에  위치한다.

 

catch  블록의  매개변수는  예외  객체가  발생했을  때  참조하는  변수명으로  반드시  java.lang.Throwable  클래스의

하위  클래스  타입으로  선언되어야  한다.

 

지정된  타입의  예외  객체가  발생하면  try  블록의  나머지  문장들은  수행되지  않고,  자바  가상  머신은  발생한  예외  객체를 발생시키며  발생한  예외  객체  타입이  동일한  catch  블록을  수행한다.

 

finally  블록은  필수  블록은  아니다.

finally  블록이  사용되면  finally  블록의  내용은  예외  발생  유무나  예외  catch  유무와  상관  없이  무조건  수행된다.

따라서,  데이터베이스나  파일을  사용한  후  닫는  기능과  같이  항상  수행해야  할  필요가  있는  경우에  사용한다.

 

package study;
 
public class ExceptionTest {
 
    public static void main(String[] args) {
        
        String[] name = new String[2];
        
        try {
            name[0] = "아이유";
            System.out.println("이름 : " + name[0]);
 
            name[1] = "트와이스";
            System.out.println("이름 : " + name[1]);
 
            name[2] = "헤이즈";
            System.out.println("이름 : " + name[2]);
            
        }catch (ArrayIndexOutOfBoundsException e) {
        
            System.out.println("배열 참조 에러 발생");
            
        }catch (Exception e) {
        
            System.out.println(e.getMessage());
            
        }finally {
            System.out.println("시스템 종료");
        }
    }
}

// 출력
이름 : 아이유
이름 : 트와이스
배열 참조 에러 발생
시스템 종료

- String[2]  배열을  선언하고,  3번째  배열에  값을  대입할 때  예외가  발생한다.

- ArrayIndexOutOfBoundsException  이  발생하여  catch에  잡히고  "배열 참조 에러 발생"  이라는  메시지가  출력된다.

- 최종적으로  finally가  실행되며  "시스템 종료"  메시지가 출력된다.

 

 

 

2. 예외가 발생한 메소드를 호출 한 곳으로 예외 객체를 넘기는 방법 (throws)

자바의 예외 처리 방법은 예외가 발생한 지점에서 try-catch 또는 try-catch-finally 블록을 이용하여

직접 처리하지 않아도 예외가 발생한 메소드를 호출한 지점으로 예외를 전달하여 처리하는 방법이 있다.

 

이때 사용하는 예약어가 throws 이다.

 

throws는 어떠한 메소드의 내부 소스코드에서 에러가 발생했을시 예외처리를 try-catch 로 하는 것이 아니라,

이 메소드를 사용하는 곳으로 책임을 전가하는 행위이다.

package study;
 
public class ExceptionTest {
 
    static void callDriver() throws ClassNotFoundException {
        Class.forName("oracle.jdbc.driver.OracleDriver");
        System.out.println("완료");
    }
    public static void main(String[] args) {
 
        try {
 
            callDriver();
 
        }catch (ClassNotFoundException e) {
 
            System.out.println("클래스를 찾을 수 없습니다.");
 
        }catch (Exception e) {
 
            System.out.println(e.getMessage());
            
        }finally {
            System.out.println("시스템 종료");
        }
    }
}

// 출력
클래스를 찾을 수 없습니다.
시스템 종료

- main에서 callDriver() 메소드를 실행시킨다.

- callDriver() 메소드에서 "oracle.jdbc.driver.OracleDriver" 클래스를 가져온다.

- 해당 클래스를 찾지 못하면 ClassNotFoundException이 발생하는데,

callDriver()에서는 throws ClassNotFoundException처리로 호출한 main에 예외를 넘긴다.

- main에서는 ClassNotFoundException을 받아 catch 문에서 잡아서 "클래스를 찾을 수 없습니다." 메시지를 출력한다.

- 마지막으로 finally가 실행되며 "시스템 종료"를 출력한다.

 

 

 

3. 사용자 정의 예외 생성 (throw)

기존의 예외 클래스로 예외를 처리할 수 없다면 사용자가 직접 예외 클래스를 작성해서 발생시킬 수 있다.

사용자가 예외 클래스를 직접 정의하려면 예외 클래스의 최상위 클래스인 java.lang.Exception 클래스를 상속받아 클래스를 정의한다.

class 예외클래스이름 extends Exception {

자바 가상 머신은 프로그램 수행중에 예외가 발생하면 자동으로 해당하는 예외 객체를 발생시킨 다음 catch 블록을 수행한다.

 

 

 

하지만 예외는 사용자가 강제적으로 발생시킬 수도 있다. 예외를 발생시키기 위해 throw 예약어를 사용한다.

throw new 예외클래스이름(매개변수);

 

throw를 이용한 예외 발생시에도 try-catch-finally 구문을 이용한 예외 처리(1번)를 하거나,

package study;
 
public class ExceptionTest {
 
    public static void main(String[] args) {
 
        try {
            throw new Exception();
        }catch (Exception e) {
            System.out.println(e);
        }finally {
            System.out.println("시스템 종료");
        }
    }
}

// 출력
java.lang.Exception
시스템 종료

 

throws를 이용하여 예외가 발생한 메소드를 호출한 다른 메소드로 넘기는 예외 처리 방법(2번)을 사용해야 한다.

package study;
 
public class MyException extends Exception {
    
    public MyException() {
        super("내가 만든 예외");
    }
}
package study;
 
public class ExceptionTest {
 
    static void callException() throws MyException {
 
        throw new MyException();
    }
 
    public static void main(String[] args) {
 
        try {
            callException();
        }catch (MyException e) {
            System.out.println(e.getMessage());
        }finally {
            System.out.println("시스템 종료");
        }
    }
}
// 출력
내가 만든 예외
시스템 종료

- MyException 이라는 Exception을 상속받는 예외를 만들었다. 그리고 MyException은 "내가 만든 예외"라는 메시지를 갖는다.

- ExceptionTest의 main이 실행되면 callException() 메소드를 호출한다.

- callException() 메소드는 MyException()을 new로 생성한 후 callException()를 호출한 main으로 던진다.

- MyException을 받은 main은 catch에서 해당 예외를 받아서 예외 메시지를 출력한다. "내가 만든 예외"

- 최종적으로 finally가 실행되어 "시스템 종료"가 출력된다. 

 

 

 

자바가 제공하는 예외(Exception) 계층 구조

Java에서 Exception은 checked unChecked 두 가지로 나눌 수 있다. 아래 사진은 Java Exception 클래스 계층 구조를 보여준다.

 

체크 예외(checked Exception)

실행하기 전에 예측 가능한 예외들을 말하는데, 체크 예외가 발생할 수 있는 메소드를

사용할 경우, 복구가 가능한 예외들이기 때문에 반드시 예외를 처리하는 코드를 작성해야 한다.

catch문으로 예외를 잡거나, throws로 예외를 자신을 호출한 클래스로 던지는 방법으로 해결해야 한다.

대표적인 Exception - IOException, SQLException

 

언체크 예외(unchecked Exception)

실행하고 난 후에 알 수 있는 예외들을 말하는데, 언체크 예외라고 불리는 이유는 명시적으로 예외처리를 강제하지 않기 때문이다.

언체크 예외는 따로 catch문으로 예외를 잡거나, throws로 선언하지 않아도 된다.

프로그램에 오류가 있을 때 발생하도록 의도된 것이다.

대표적인 Exception - NullPointerException, ArrayIndexOutOfBoundException

 

 

 

RuntimeException과 RuntimeException가 아닌 것의 차이?

위에서 설명했듯 RuntimeException은 unchecked Exception을 상속한 클래스이고,

RuntimeException이 아닌것은 checked Exception을 상속한 클래스이다.

 

RuntimeException을 상속한 예외들은 명시적인 예외처리를 강제하지 않기 때문에 따로 catch문으로 예외를 잡거나, throws로 선언하지 않아도 된다.

 

RuntimeException이 아닌 것은 checkedException이 발생할 수 있기 때문에 반드시 예외를 처리하는 코드를 함께 작성해야 한다

 

 

 

 

커스텀한 예외 만들기

기존에 정의된 예외 클래스 이외에 필요에 따라 새로운 예외 클래스를 정의하여 사용할 수 있다.

보통 Exception 클래스를 상속받는 클래스를 만들지만, 필요에 따라서 알맞은 예외 클래스를 상속받아서 만든다.

String 타입의 파라미터를 갖는 생성자는 상위 클래스의 생성자를 호출하여 예외 메시지를 넘겨준다.

 

 

다음은 잔고 부족 예외를 사용자 정의 클래스로 선언한 것이다.

package study;
 
public class InsufficientBalanceException extends Exception {
 
    public InsufficientBalanceException(String msg) {
        super(msg);
    }
}

 

InsufficientBalanceException은 Exception을 상속받기 때문에 컴파일러에 의해 체크되는 예외가 된다.

 

 

다음은 은행 계좌(account) 클래스를 작성한 것이다. 출금(withdraw)메소드에서 잔고(balance)와 출금액을 비교해서

잔고가 부족하면 InsufficientBalanceException을 발생시키도록 하였다.

package study;
 
public class Account {
    private long balance;
 
    public long getBalance() {
        return balance;
    }
    
    public void setBalance(long balance) {
        this.balance = balance;
    }
 
    public void deposit(int money) {
        balance += money;
    }
 
    public void withdraw(int money) throws InsufficientBalanceException {
        if(balance < money) {
            throw new InsufficientBalanceException("잔고 부족 : " + (money - balance) + "원이 모자랍니다.");
        }
        balance -= money;
    }
}

 

try 블록에서 예외가 발생하면 예외 객체는 catch 블록의 매개변수에서 참조하게 되므로 매개변수를 이용하면 예외객체의 정보를 알 수 있다. 모든 예외 객체는 Exception을 상속받기 때문에 메소드를 예외 객체에서 호출 할 수 있다.

그 중에서도 가장 많이 사용되는 getMessage()  printStackTrace()가 있다.

 

 

다음은 Account 클래스를 이용해서 예금과 출금을 한다. 출금할 때 withdraw() 메소드를 이용하므로 예외 처리를 해줘야한다.

InsufficientBalanceException 객체의 getMessage()와 printStackTrace()로 예외에 대한 정보를 얻어낸다.

package study;
 
public class AccountEx {
 
    public static void main(String[] args) {
        Account act = new Account();
 
        act.deposit(1_000_000);
        System.out.println("예금액 : " + act.getBalance());
 
        try {
            act.withdraw(1_500_000);
        }catch (InsufficientBalanceException e) {
            System.out.println(e.getMessage());
            System.out.println();
            e.printStackTrace();
        }
    }
}
// 출력
예금액 : 1000000
잔고 부족 : 500000원이 모자랍니다.
 
study.InsufficientBalanceException: 잔고 부족 : 500000원이 모자랍니다.
	at study.Account.withdraw(Account.java:16)
	at study.AccountEx.main(AccountEx.java:12)

 

▶ 꼬리질문 - 올바른 예외 처리 방식

예외 복구 전략이 명확하고 복구가 가능하다면 Checked Excetpion을 try-catch로 잡아서 예외를 복구하는 것이 좋다.

 

복구가 불가능한 Checked Exception이 발생하면 더 구체적인 UnChecked Exception을 발생시키고 예외에 대한 메시지를 명확하게 전달하는 것이 좋다.

 

무책임하게 상위 메서드에 throw로 예외를 던지는 것은 상위 메서드의 책임이 증가하기 때문에 좋지 않은 방법이다.

 

 

 

😎 CheckedException과 UnCheckedException의 차이와  RuntimeException에 대해 설명해주세요.

  • CheckedException은 실행하기 전에 예측 가능한 예외를 말하고, 반드시 예외 처리를 해야 합니다.
    • 대표적인 Exception - IOException, ClassNotFoundException 등
  • UncheckedException은 실행하고 난 후에 알 수 있는 예외를 말하고, 따로 예외처리를 하지 않아도 됩니다.
    • 대표적인 Exception - NullPointerException, ArrayIndexOutOfBoundException 등
  • RuntimeException은 UncheckedException을 상속한 클래스이고, RuntimeException이 아닌 것은 CheckedException을 상속한 클래스 입니다.

 

 

😎 Optional API에 대해 설명해주세요.

개발할때 가장 많이 발생하는 예외 중 하나가 NPE(NullPointerException)입니다.
NPE를 피하려면 null 여부 검사를 필연적으로 하게 되는데 만약 null 검사를 해야하는 변수가 많은 경우 코드가 복잡해지고 번거롭습니다.

 

하지만 Java8 부터 Optional<T>을 제공하여 null로 인한 예외가 발생하지 않도록 도와주고, Optional 클래스의 메소드를 통해 null을 컨트롤 할 수 있습니다.

 

 

 

 

 

 

 

 

 

 

관련 포스팅

[기술면접] JAVA - 1/4
[기술면접] JAVA - 2/4 (주로 예외처리Exception)
[기술면접] JAVA - 3/4 (주로 컬렉션 프레임워크)
[기술면접] JAVA - 4/4

 

 

 

 

 


출처: 

https://mangkyu.tistory.com/94
https://dev-coco.tistory.com/153
https://gyoogle.dev/blog/

 

'기술 면접 준비' 카테고리의 다른 글

[기술면접] JAVA - 4/4  (0) 2023.03.03
[기술면접] JAVA - 3/4 (주로 컬렉션 프레임워크)  (0) 2023.03.03
[기술면접] JAVA - 1/4  (0) 2023.03.02
[기술면접] Spring - 3/3  (0) 2023.03.02
[기술면접] Spring - 2/3  (0) 2023.03.02