🤗 모든 질문은 '아는대로 설명해주세요'가 베이스~!
🔔 Java의 특징
"Java의 핵심 특징은 무엇인가요?"
Java는 대표적인 객체지향 프로그래밍 언어입니다. JVM 위에서 동작하기 때문에 플랫폼에 독립적이며, 가비지 컬렉터(GC)를 통한 자동 메모리 관리, 멀티스레드 API 제공, 풍부한 라이브러리 생태계로 유지보수와 확장성이 뛰어납니다.
(꼬리 질문)
"Java의 플랫폼 독립성이 실제 배포나 운영 환경에서 어떤 이점을 줄 수 있을까요?"
JVM만 설치되어 있다면 동일한 .class 파일을 어떤 환경에서도 실행 가능해 운영체제에 따라 코드를 수정하거나 다시 빌드할 필요가 없습니다.
🔔 JVM의 역할
- JVM은 스택 기반으로 동작하며, Java Byte Code를 OS에 맞게 해석 해주는 역할을 하고
가비지컬렉션을 통해 자동적인 메모리 관리를 해줍니다.
🔔 Java의 컴파일 과정
"Java의 컴파일 과정에 대해 설명해주세요."
- .java 소스 파일 작성 → javac 컴파일러로 .class 바이트코드 생성
- JVM의 클래스 로더가 .class 파일을 메모리에 로딩
- 실행 엔진(Execution Engine)이 인터프리터 또는 JIT 컴파일러로 기계어로 변환하여 실행
(보충 질문)
"Interpreter와 JIT Compiler는 언제 사용되나요?"
처음엔 인터프리터 방식으로 실행되며, 반복 호출되는 메서드는 JIT 컴파일러가 감지해 성능을 위해 기계어로 변환하여 실행 속도를 높입니다.
(꼬리 질문) "JIT 컴파일러가 항상 더 좋은 성능을 보이나요?"
짧게 한 번만 실행되는 코드는 오히려 JIT의 초기 비용 때문에 비효율적일 수 있습니다. JVM은 통계적으로 분석하여 선택적으로 적용합니다.
🔔 원시 타입 (Primitive Types)
"Java에서 제공하는 원시 타입과 바이트 크기는 어떻게 되나요?"
- 정수형: byte(1바이트), short(2), int(4), long(8)
- 실수형: float(4), double(8)
- 문자형: char(2) - 유니코드 지원
- 논리형: boolean(1) - true / false
(꼬리 질문) "boolean의 크기가 1바이트인가요?"
▶ 명확한 크기를 보장하진 않지만, JVM의 구현에 따라 1바이트 이상이 될 수 있습니다. 단지 논리 값 하나만 표현합니다.
🔔 오버라이딩과 오버로딩
"오버라이딩과 오버로딩의 차이를 설명해주세요."
- Overriding: 상위 클래스의 메서드를 하위 클래스에서 재정의하여 사용하는 것
- Overloading: 같은 이름의 메서드를 파라미터의 개수나 타입을 다르게 하여 여러 개 정의하는 것
(꼬리 질문) "Overriding 시 주의할 점은 무엇인가요?"
접근 제어자는 더 좁게 만들 수 없으며, 예외도 부모보다 더 많은 checked 예외를 던질 수 없습니다. @Override 어노테이션 사용이 권장됩니다.
🔔 객체지향 프로그래밍(OOP)란
"객체지향 프로그래밍이란 무엇인가요?"
프로그래밍에서 필요한 데이터를 객체로 추상화하여, 상태와 행위를 가진 단위로 구성하고 이들 간 상호작용으로 로직을 만드는 방식입니다. OOP의 4대 특징: 캡슐화, 상속, 다형성, 추상화
(꼬리 질문) "절차지향 프로그래밍과 차이는 무엇인가요?"
절차지향은 순차적인 로직 중심으로 구성되고, OOP는 데이터와 그 데이터를 처리하는 행위를 묶은 단위 중심입니다. OOP는 유지보수와 확장에 유리합니다.
🔔 try-with-resources
"try-with-resources 문법에 대해 설명해주세요."
Java 7부터 지원된 문법으로, AutoCloseable 인터페이스를 구현한 자원을 try() 안에 선언하면, try 블록이 끝날 때 자동으로 자원이 반환됩니다.
- 문제점:
- 자원 반납에 의해 코드가 복잡해짐
- 에러로 자원을 반납하지 못하는 경우 발생
- 에러 스택 트레이스가 누락되어 디버깅이 어려움
(꼬리 질문) "기존 try-catch-finally와의 차이는 무엇인가요?"
자원 반납을 명시적으로 하지 않아도 되어 코드가 간결하고, 예외 누락 방지 및 디버깅에 유리합니다.
🔔 Closeable과 AutoCloseable의 관계
"Closeable과 AutoCloseable의 관계는 무엇인가요?"
Closeable은 AutoCloseable을 상속한 하위 인터페이스입니다. Closeable은 IOException을 명시적으로 던지지만, AutoCloseable은 더 넓은 범위의 예외를 지원합니다.
(꼬리 질문) "둘의 차이를 고려해 어떤 상황에서 사용하나요?"
파일, 스트림처럼 IOException이 중요한 경우 Closeable, 그 외 단순한 자원 관리는 AutoCloseable로 충분합니다.
🔔 불변 객체란, 대표적인 Java의 예시
"Java에서 불변 객체란 무엇이고, 어떤 예시가 있나요?"
불변 객체는 생성 후 내부 상태가 절대 변경되지 않는 객체입니다. 대표적으로 String, Integer, LocalDate 등이 있습니다.
(꼬리 질문) "String을 변경할 수 있는 것처럼 보이는 이유는 무엇인가요?"
String str = "a"; str = "b";는 기존 문자열을 바꾸는 게 아니라 새로운 문자열 객체를 생성해 str이 그걸 참조하는 것입니다. 원래 객체는 변하지 않습니다.
🔔 참조 타입에서 불변 객체 만드는 방법
"참조 타입은 어떻게 불변성을 보장하나요?"
- 내부 필드가 객체, 배열, 컬렉션인 경우 깊은 복사(deep copy)를 사용합니다.
- getter에서는 원본을 반환하지 않고 clone() 또는 Collections.unmodifiableXXX()로 방어적 복사(defensive copy)를 제공합니다.
(꼬리 질문) "defensive copy와 shallow copy의 차이는 무엇인가요?"
shallow copy는 참조만 복사해 원본 변경 시 영향이 있으며, defensive copy는 새로운 객체를 만들어 안전하게 복사합니다.
🔔 불변 객체와 final 키워드의 필요성
"불변 객체나 final 키워드를 굳이 사용하는 이유는 무엇인가요?"
- Thread-safe하여 동기화를 고려하지 않아도 됩니다.
- 상태 변경이 없기 때문에 예외 발생 시 rollback이 필요 없는 실패 원자성 제공
- 사이드 이펙트(부수효과)가 없고 예측 가능한 코드 작성 가능
- 캐싱, 맵, 셋의 키 등으로 활용 시 안정적
(꼬리 질문) "GC 성능 향상과의 연관성은 무엇인가요?"
▶ 불변 객체는 재사용되고 참조 변경이 적기 때문에 GC가 참조 분석을 빠르게 수행할 수 있고, 결과적으로 스캔 시간이 짧아집니다.
🔔 Thread-Safe란?
"Thread-Safe란 무엇인가요?"
여러 스레드가 동시에 어떤 객체나 메서드에 접근해도 문제가 발생하지 않는 것을 의미합니다. 상태가 공유되더라도 데이터의 일관성이 유지됩니다.
(꼬리 질문) "Thread-safe를 보장하는 방법에는 무엇이 있을까요?"
불변 객체 사용, synchronized 키워드, ConcurrentHashMap 등 동시성 컬렉션 사용, 원자성 클래스(AtomicInteger 등)를 활용하는 방법이 있습니다.
🔔 추상 클래스와 인터페이스의 개념과 차이
"추상 클래스와 인터페이스의 차이를 설명해주세요."
- 추상 클래스는 abstract 키워드를 사용하며, 일반 메서드와 추상 메서드를 함께 가질 수 있습니다.
- 인터페이스는 기본적으로 모든 메서드가 추상 메서드이며, Java 8 이후부터는 default, static 메서드도 정의할 수 있습니다.
공통점
- 인스턴스 생성 불가능하며, 하위 클래스에서 구현 또는 확장 필요
차이점
- 인터페이스는 다중 구현 가능, 추상 클래스는 단일 상속만 가능
- 인터페이스는 모든 구현 클래스에 특정 메서드가 존재하도록 강제함
- 추상 클래스는 공통된 상태(필드)를 함께 가질 수 있어 기능 공유에 유리
(꼬리 질문) "추상 클래스와 인터페이스 중 언제 어떤 걸 써야 하나요?"
공통 로직을 일부 구현해 제공하고 싶다면 추상 클래스, 다양한 구현체에 특정 행위를 강제하고 싶다면 인터페이스가 적합합니다.
🔔 싱글톤 패턴
"싱글톤 패턴이란 무엇인가요?"
싱글톤은 애플리케이션에서 단 하나의 인스턴스만 존재하게 하는 디자인 패턴입니다.
주로 공유 설정 객체, 캐시, 로깅 등 전역 상태가 필요한 곳에 사용됩니다.
(꼬리 질문) "싱글톤을 구현할 때 주의할 점은?"
멀티스레드 환경에서 인스턴스가 중복 생성되지 않도록 synchronized, double-checked locking, 또는 enum 싱글톤을 사용합니다.
🔔 싱글톤 예시 - Spring Bean
"Spring에서 싱글톤 패턴의 예시는 무엇인가요?"
Spring 프레임워크의 기본 Bean Scope는 Singleton입니다.
모든 요청에 대해 동일한 인스턴스를 반환하여 메모리 낭비를 줄이고, DI(의존성 주입)를 통해 객체를 효과적으로 관리합니다.
(꼬리 질문)
"Spring에서 매번 다른 객체를 원할 경우 어떻게 하나요?"
@Scope("prototype") 어노테이션을 사용하면 매번 새로운 인스턴스를 생성할 수 있습니다.
🔔 가비지 컬렉션(Garbage Collection)에 대해 설명해주세요.
- 가비지 컬렉션은 JVM(자바 가상 머신)의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리 객체(garbage)를 모아 주기적으로 제거하는 프로세스를 말합니다.
- Stop-The-World:
메모리가 언제 해제되는지 정확하게 알 수 없어 제어하기 힘들며,
가비지 컬렉션(GC)이 동작하는 동안에는 다른 동작을 멈추기 때문에 오버헤드가 발생되는 문제점이 있다.
🔔 가비지 컬렉션(GC) 개요
- GC의 작업을 수행하기 위해 JVM이 어플리케이션의 실행을 잠시 멈추고,
GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업을 중단 후 (Stop The World 과정)
사용하지 않는 메모리를 제거(Mark and Sweep 과정)하고 작업이 재개됩니다.
- Reachable : 객체가 참조되고 있는 상태
Unreachable : 객체가 참조되고 있지 않은 상태 (GC의 대상이 됨)
- Mark And Sweep
- Mark 과정 : 먼저 Root Space로부터 그래프 순회를 통해 연결된 객체들을 찾아내어 각각 어떤 객체를 참조하고 있는지 찾아서 마킹한다.
- Sweep 과정 : 참조하고 있지 않은 객체 즉 Unreachable 객체들을 Heap에서 제거한다.
- Compact 과정 : Sweep 후에 분산된 객체들을 Heap의 시작 주소로 모아 메모리가 할당된 부분과 그렇지 않은 부분으로 압축한다. (가비지 컬렉터 종류에 따라 하지 않는 경우도 있음)
- heap 메모리의 구조
- JVM의 힙(heap) 영역은 동적으로 레퍼런스 데이터가 저장되는 공간으로서, 가비지 컬렉션에 대상이 되는 공간이다.
- Heap영역은 처음 설계될 때 다음의 2가지를 전제 (Weak Generational Hypothesis)로 설계되었다.
- 대부분의 객체는 금방 접근 불가능한 상태(Unreachable)가 된다.
- 오래된 객체에서 새로운 객체로의 참조는 아주 적게 존재한다.
- Young 영역
- 새롭게 생성된 객체가 할당(Allocation)되는 영역
- 대부분의 객체가 금방 Unreachable 상태가 되기 때문에, 많은 객체가 Young 영역에 생성되었다가 사라진다.
- Young 영역에 대한 가비지 컬렉션(Garbage Collection)을 Minor GC라고 부른다.
- Eden
- new를 통해 새로 생성된 객체가 위치.
- 정기적인 쓰레기 수집 후 살아남은 객체들은 Survivor 영역으로 보냄
- Survivor 0 / Survivor 1
- 최소 1번의 GC 이상 살아남은 객체가 존재하는 영역
- Survivor 영역에는 특별한 규칙이 있는데, Survivor 0 또는 Survivor 1 둘 중 하나에는 꼭 비어 있어야 하는 것이다.
- Old 영역
- Young영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역
- Young 영역보다 크게 할당되며, 영역의 크기가 큰 만큼 가비지는 적게 발생한다.
- Old 영역에 대한 가비지 컬렉션(Garbage Collection)을 Major GC 또는 Full GC라고 부른다.
- Minor GC 과정
- Young Generation 영역은 짧게 살아남는 메모리들이 존재하는 공간이다.
- 처음 생성된 객체는 Young Generation 영역의 일부인 Eden 영역에 위치
- 객체가 계속 생성되어 Eden 영역이 꽉차게 되고 Minor GC가 실행
- Mark 동작을 통해 reachable 객체를 탐색
- Eden 영역에서 살아남은 객체는 1개의 Survivor 영역으로 이동
- Eden 영역에서 사용되지 않는 객체(unreachable)의 메모리를 해제(sweep)
- 살아남은 모든 객체들은 age값이 1씩 증가
- 또다시 Eden 영역에 신규 객체들로 가득 차게 되면 다시한번 minor GC 발생하고 mark 한다
- marking 한 객체들을 비어있는 Survival 1으로 이동하고 sweep
- 다시 살아남은 모든 객체들은 age가 1씩 증가
- 이러한 과정을 반복
- Young Generation 영역은 짧게 살아남는 메모리들이 존재하는 공간이다.
- Major GC 과정
- Old Generation은 길게 살아남는 메모리들이 존재하는 공간이다.
- 객체의 age가 임계값(여기선 8로 설정)에 도달하게 되면,
- 이 객체들은 Old Generation 으로 이동된다. 이를 promotion 이라 부른다.
- 위의 과정이 반복되어 Old Generation 영역의 공간(메모리)가 부족하게 되면 Major GC가 발생되게 된다.
- Major GC가 일어나면 Thread가 멈추고 Mark and Sweep 작업을 해야 해서 CPU에 부하를 주기 때문에 멈추거나 버벅이는 현상이 일어난다.
- Old Generation은 길게 살아남는 메모리들이 존재하는 공간이다.
🔔 객체지향의 설계원칙에 대해 설명해주세요.
- SRP - 단일 책임 원칙 : 한 클래스는 하나의 책임만 가져야 한다.
- OCP - 개방 폐쇄 원칙 : 확장에는 열려있고, 수정에는 닫혀있어야 한다.
- LSP - 리스코프 치환 원칙 : 하위 타입은 항상 상위 타입을 대체 할 수 있어야 한다.
- ISP - 인터페이스 분리 원칙 : 한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다.
- DIP - 의존관계 역전 원칙 : 추상을 매개로 메시지를 주고받음으로써 관계를 최대한 느슨하게 만든다.
🔔 Java 메모리 영역과 할당 시점
"Java 메모리는 어떻게 나뉘며, 언제 할당되나요?"
- Method Area: 클래스, static 변수 저장. JVM 시작 시 할당
- Heap: 객체 인스턴스 저장. 런타임 중 동적으로 할당됨
- Stack: 지역 변수 및 메서드 콜 스택 저장. 메서드 호출 시 할당
(꼬리 질문) "static 변수는 어디에 저장되나요?"
Method Area에 저장되며, 클래스 로딩 시 초기화됩니다.
🔔 클래스와 객체에 대해 설명해주세요.
- 클래스는 객체를 만들어내기 위한 설계도 혹은 틀 이라고 할 수 있고, 객체를 생성하는데 사용합니다.
- 객체는 설계도(클래스)를 기반으로 생성되며, 자신의 고유 이름과 상태, 행동을 갖습니다.
- 여기서 상태는 필드(fields), 행동은 메소드(Method)라고 표현합니다.
- 객체에 메모리가 할당되어 실제로 활용되는 실체는 '인스턴스'라고 부릅니다.
🔔 생성자(Constructor)에 대해 설명해주세요.
"생성자의 역할과 특징은 무엇인가요?"
- 객체 생성 시 자동 호출되어 초기화를 수행하는 특수 메서드입니다.
- 명시적으로 선언하지 않으면 기본 생성자가 자동 추가됩니다.
- 오버로딩이 가능하여 다양한 방식으로 객체를 초기화할 수 있습니다.
(꼬리 질문) "생성자에서 다른 생성자를 호출할 수 있나요?"
▶ this() 키워드를 통해 같은 클래스의 다른 생성자를 호출할 수 있습니다. 반드시 첫 줄에서 호출해야 합니다.
🔔 Wrapper Class / Boxing & UnBoxing
"Wrapper Class란 무엇이고, Boxing/UnBoxing은 무엇인가요?"
- Wrapper Class는 기본형을 객체로 다루기 위해 존재합니다. (Integer, Double, Boolean 등)
- Boxing: 기본형 → Wrapper 객체로 변환
- UnBoxing: Wrapper 객체 → 기본형으로 변환
(꼬리 질문) "AutoBoxing의 단점은 무엇인가요?"
반복문 안에서 Autoboxing이 자주 일어나면 불필요한 객체 생성으로 성능 저하를 유발할 수 있습니다. 명시적 형변환이 더 효율적인 경우도 있습니다.
🔔 Synchronized에 대해 아는 대로 말해주세요.
"Synchronized 키워드는 어떤 역할을 하나요?"
멀티스레드 환경에서 여러 스레드가 동시에 같은 자원에 접근할 때, 데이터의 정합성을 보장하기 위해 사용되는 키워드입니다. 한 스레드가 자원을 점유하고 있는 동안 다른 스레드는 해당 자원에 접근하지 못하도록 막아줍니다.
- 메서드나 블록에 synchronized를 붙여 사용
- 객체 또는 클래스 단위로 락을 설정 가능
(꼬리 질문) "Synchronized 키워드를 남발하면 어떤 문제가 생기나요?"
락 경합으로 인해 스레드 대기 시간이 길어지고, 전체 성능 저하가 발생할 수 있습니다. 꼭 필요한 곳에만 사용하는 것이 좋습니다.
🔔 new String()과 리터럴("")의 차이에 대해 설명해주세요.
"new String()과 문자열 리터럴 ""의 차이는 무엇인가요?"
- "hello" 같은 리터럴은 String Constant Pool에 저장되어 같은 값을 재사용합니다.
- new String("hello")는 Heap에 새로운 객체를 생성하여 같은 값을 가진 객체라도 참조 주소가 다릅니다.
(꼬리 질문) "같은 문자열을 두 번 선언했을 때 리터럴 방식은 메모리를 아끼는 이유는?"
String Pool을 통해 동일 문자열 객체를 재사용하기 때문에 중복 객체 생성을 방지할 수 있어 메모리 효율이 높습니다.
🔔 equals와 '=='의 차이
"equals와 ==의 차이를 설명해주세요."
- ==: 두 객체의 참조(주소값)를 비교
- equals(): 두 객체의 실제 값(내용)을 비교. String 클래스에서는 오버라이딩 되어 값 비교를 합니다.
(꼬리 질문) "사용자 정의 클래스에서 equals()가 주소 비교만 하는 이유는?"
Object 클래스의 기본 equals()는 ==과 동일하게 참조 비교만 하기 때문이며, 값 비교를 원할 경우 equals()를 오버라이딩해야 합니다.
🔔 String, StringBuffer, StringBuilder의 차이를 설명해주세요.
"이들 클래스의 차이를 설명해주세요."
- String: 불변 객체로 문자열 변경 시 새로운 객체 생성
- StringBuffer: 가변 객체로 동기화 지원, 멀티스레드 환경에서 사용
- StringBuilder: 가변 객체로 동기화 미지원, 싱글스레드 환경에 적합
(꼬리 질문) "StringBuilder가 더 빠른데도 String을 쓰는 이유는?"
불변 객체이기 때문에 안정성이 높고, 비교적 적은 문자열 조작에서는 가독성과 성능 차이가 크지 않아 String 사용이 유리할 수 있습니다.
🔔 String이 불변인 이유
"String 클래스가 불변(Immutable)인 이유는 무엇인가요?"
- 성능 최적화: String Pool을 통한 캐싱으로 메모리 절약 및 속도 향상
- 스레드 안정성: 동기화 없이도 안전하게 공유 가능
- 보안 강화: 외부에서 값을 바꿀 수 없어 민감 정보의 보호에 유리
(꼬리 질문) "불변 객체라면 문자열을 변경할 수 없는데, String에서 변경처럼 보이는 이유는?"
실제로는 기존 문자열이 변경되는 게 아니라 새로운 문자열 객체를 생성해 참조가 변경되는 것입니다.
🔔 접근 제한자(Access Modifier)에 대해 설명해주세요.
"Java의 접근 제한자에는 어떤 것들이 있나요?"
- public: 어디서나 접근 가능
- protected: 같은 패키지 또는 상속 관계에서 접근 가능
- (default): 같은 패키지 내에서만 접근 가능
- private: 같은 클래스 내부에서만 접근 가능
(꼬리 질문) "protected는 언제 사용하나요?"
같은 패키지나 상속받은 클래스에서 내부 동작을 재정의하거나 확장할 때 사용합니다.
🔔 클래스 멤버 변수 초기화 순서
"클래스 변수는 어떤 순서로 초기화되나요?"
- static 변수: 클래스 로딩 시 초기화
- 인스턴스 변수: 객체 생성 시 초기화 블록보다 먼저 실행
- 생성자: 마지막으로 실행되어 객체를 초기화
(꼬리 질문) "생성자보다 초기화 블록이 먼저 실행되는 이유는?"
객체가 생성되기 전에 공통 초기 로직을 설정하기 위해 초기화 블록이 먼저 실행됩니다.
🔔 static 키워드
"static 키워드의 역할은 무엇인가요?"
- 클래스 로딩 시 메모리에 올라가며, 객체 생성 없이 사용 가능
- static 멤버는 모든 인스턴스에서 공유됨
- static 변수는 프로그램 종료 시까지 유지됨
(꼬리 질문) "static을 남용하면 안 되는 이유는?"
상태가 전역으로 공유되어 의도치 않은 변경 가능성이 있으며, 테스트나 유지보수가 어려워질 수 있습니다.
🔔 static을 사용하는 이유
언제 static을 사용하는 것이 효과적인가요?"
- 공통된 자원 관리 (ex. 상수, 설정값)
- 유틸성 메서드 (ex. Collections.sort()처럼 상태를 가지지 않는 기능성 메서드)
(꼬리 질문) "static 메서드는 어떤 한계가 있나요?"
인스턴스 변수나 메서드에 접근할 수 없기 때문에, 객체 고유 상태가 필요한 경우엔 사용할 수 없습니다.
🔔 내부 클래스 (Inner Class)
"내부 클래스의 장점은 무엇인가요?"
- 외부 클래스의 private 멤버에 직접 접근 가능
- 논리적으로 관련된 클래스를 함께 묶어 관리 가능
- 캡슐화 증가 및 외부 접근 제한으로 보안성 향상
(꼬리 질문) "정적 내부 클래스와 인스턴스 내부 클래스의 차이는?"
정적 내부 클래스는 외부 클래스의 인스턴스 없이 사용 가능하며, 정적 멤버만 접근할 수 있습니다. 반면, 인스턴스 내부 클래스는 외부 클래스 인스턴스가 있어야 생성 가능합니다.
🔔 리플렉션(Reflection)
"리플렉션이란 무엇인가요?"
리플렉션은 실행 중에 클래스, 필드, 메서드 등을 동적으로 조회하고 조작할 수 있는 기능입니다.
(꼬리 질문) "어떤 상황에서 리플렉션이 사용되나요?"
프레임워크(Spring, Hibernate 등)나 IDE가 객체를 동적으로 조작해야 할 때 사용합니다. 예: 어노테이션 처리, 의존성 주입, 테스트 자동화
🔔 Error와 Exception의 차이를 설명해주세요.
"Error와 Exception의 차이는 무엇인가요?"
- Error: 시스템 레벨의 치명적인 문제 (예: OutOfMemoryError) → 복구 불가
- Exception: 개발자가 처리 가능한 예외 상황 (예: FileNotFoundException)
(꼬리 질문) "Error도 catch로 잡을 수 있나요?"
가능은 하지만 권장하지 않습니다. Error는 대부분 복구 불가능한 상황이라 시스템 종료를 유도하는 것이 바람직합니다.
🔔 CheckedException과 UnCheckedException의 차이를 설명해주세요.
"CheckedException과 UncheckedException의 차이는?"
- Checked: 컴파일 타임에 체크되어 반드시 예외 처리 필요 (예: IOException)
- Unchecked: 런타임에 발생하며 예외 처리 선택 가능 (예: NullPointerException)
(꼬리 질문) "RuntimeException을 직접 정의할 수 있나요?"
가능합니다. 예외 상황이 예상되며, 선택적으로 처리하고자 할 때 사용자 정의 Unchecked 예외를 만들어 사용할 수 있습니다.
🔔 Optional API에 대해 설명해주세요.
"Optional은 어떤 목적에서 사용되나요?"
Optional은 null 값을 안전하게 처리하기 위한 컨테이너 클래스입니다. NPE를 방지하고, 메서드 체이닝을 통해 코드 가독성을 향상시킵니다.
(꼬리 질문) "Optional을 남용하면 문제가 될 수 있을까요?"
필드나 파라미터에 Optional을 사용하는 건 비효율적일 수 있으며, 반환값에 국한해 사용하는 것이 좋습니다.
🔔 컬렉션 프레임워크란?

"자바의 컬렉션 프레임워크에 대해 설명해주세요."
컬렉션 프레임워크는 다수의 데이터를 구조적으로 저장하고, 효율적으로 처리할 수 있도록 Java에서 제공하는 자료구조 API들의 집합입니다.
- 핵심 인터페이스: List, Set, Map, Queue
- 각각의 인터페이스를 구현하는 다양한 클래스가 존재하며, 상황에 맞게 선택적으로 사용할 수 있습니다.
(꼬리 질문) "컬렉션 프레임워크를 사용함으로써 얻는 이점은 무엇인가요?"
▶ 자료 구조를 직접 구현하지 않고도 효율적인 데이터 관리를 할 수 있으며, 유지보수성과 생산성이 향상됩니다.
🔔 List, Set, Map, Stack, Queue의 특징에 대해 설명해주세요.
- List는 순서가 있는 데이터의 집합이며, 데이터의 중복을 허용합니다.
대표적인 구현체로는 ArrayList가 있고, 이는 Vector를 개선한 것입니다.
이외에도 LinkedList 등의 구현체가 있습니다.- Vector, ArrayList, LinkedList, Stack, Queue
- Set은 순서가 없는 데이터의 집합이며, 데이터의 중복을 허용하지 않습니다.
대표적인 구현체로는 HashSet이 있고, 순서를 보장하기 위해서는 LinkedHashSet을 사용합니다.
(Map의 key-value 구조에서 key 대신 value가 들어가 value를 key로 하는 자료구조)- HashSet, LinkedHashSet, TreeSet
- Map은 키와 값이 한 쌍으로 이뤄져 있고, 키를 기준으로 중복을 허용하지 않으며, 순서가 없습니다.
key의 순서를 보장하기 위해서는 LinkedHashMap을 사용합니다.- HashMap, TreeMap, HashTable, Properties
- Stack 객체는 직접 new 키워드로 사용할 수 있으며, 마지막에 저장한 데이터를 가장 먼저 꺼내는 구조(LIFO : Last In First Out)
- Queue 인터페이스는 LinkedList에 new 키워드를 적용해 사용할 수 있으며, 처음 저장한 데이터를 가장 먼저 꺼내게 되는 구조(FIFO : First In First Out)
🔔 Set과 Map에서 객체 중복 체크 방식
"Set과 Map에서 Object 타입의 중복을 어떻게 판단하나요?"
- hashCode()로 해시값 비교 → 다르면 다른 객체
- 해시값이 같을 경우 equals()로 값 비교
- 두 메서드 모두 만족해야 동일 객체로 판단됨
(꼬리 질문) "equals()와 hashCode()를 함께 오버라이딩 해야 하는 이유는?"
hashCode가 같아도 equals가 다르면 다른 객체로 판단되며, 둘 다 일관성 있게 오버라이딩하지 않으면 자료구조가 오작동할 수 있습니다.
🔔 Vector와 List의 차이를 설명해주세요.
- 벡터는 데이터 삽입시 원소를 밀어내지만 리스트는 노드를 연결만 하기 때문에, 삽입 삭제 부분에서 리스트가 시간복잡도의 우위를 가집니다.
- 벡터는 랜덤부분접근이 가능하지만 리스트는 더블링크드리스트(노드가 양쪽으로 연결)로 되어있기 때문에 랜덤 접근이 되지 않습니다. 검색적인 측면에서는 벡터가 우위에 있습니다.
- 벡터는 리스트와 달리 항상 동기화되는 장점이자 단점을 가지고 있습니다. 멀티 쓰레드 환경에서 안전하게 객체를 추가하고 삭제할 수 있지만, 단일쓰레드 환경 일때도 동기화를 하기 때문에 List보다 성능이 떨어집니다.
🔔 제네릭에 대해 설명해주시고, 왜 쓰는지 알려주세요.
"제네릭은 왜 사용하나요?"
- 컴파일 시 타입 검사를 통해 타입 안정성 보장
- 캐스팅 없이 원하는 타입으로 바로 사용할 수 있음
- 코드 재사용성과 가독성 향상
(꼬리 질문) "와일드카드를 사용하는 이유는 무엇인가요?"
? extends T, ? super T를 통해 제네릭 타입의 유연성을 확보하고, 메서드 인자에서 타입의 범위를 제한하거나 확장할 수 있습니다.
🔔 final / finally / finalize
"final, finally, finalize의 차이를 설명해주세요."
- final: 변수 → 변경 불가, 메서드 → 오버라이딩 금지, 클래스 → 상속 금지
- finally: try-catch 문에서 예외 발생 여부와 관계없이 실행되는 블록
- finalize: GC가 객체를 수거하기 직전에 호출하는 메서드 (현재는 거의 사용 안 함)
(꼬리 질문) "finalize()를 사용하지 말라고 하는 이유는?"
호출 시점이 보장되지 않고, 성능 저하와 GC 지연 문제를 유발할 수 있어, try-with-resources나 Cleaner를 사용하는 것이 권장됩니다.
🔔 직렬화(Serialize)에 대해 설명해주세요.
"직렬화란 무엇이며, 왜 사용하나요?"
객체를 바이트 형태로 변환하여 파일, 네트워크 등 외부로 전송하거나 저장하기 위한 기술입니다. 역직렬화는 다시 객체로 복원하는 과정입니다.
(꼬리 질문) "직렬화 시 모든 객체가 가능한가요?"
Serializable 인터페이스를 구현한 객체만 직렬화할 수 있으며, transient 키워드로 특정 필드를 직렬화 대상에서 제외할 수 있습니다.
🔔 SerialVersionUID를 선언해야 하는 이유에 대해 설명해주세요.
"SerialVersionUID는 왜 선언하나요?"
직렬화와 역직렬화 시 클래스 버전이 일치하는지 확인하기 위한 고유 ID입니다. 선언하지 않으면 JVM이 자동 생성하며, 클래스 구조가 바뀌면 역직렬화 오류 발생 가능성이 높습니다.
(꼬리 질문) "SerialVersionUID를 명시적으로 선언하는 이유는?"
클래스 변경이 있어도 같은 ID를 유지하면 역직렬화를 정상적으로 수행할 수 있어 하위 호환성을 유지할 수 있습니다.
🔔 옵저버 패턴을 어떻게 구현하나요
"옵저버 패턴은 어떻게 동작하나요?"
객체의 상태 변화를 관찰하는 객체(옵저버)를 등록해 두고, 주체(Subject)의 상태가 변하면 옵저버에게 알림을 보내는 구조입니다.
(꼬리 질문) "Java에서 옵저버 패턴은 어떻게 구현하나요?"
Observer와 Observable 클래스를 직접 활용하거나, 이벤트 리스너 패턴이나 Callback, RxJava, LiveData 등을 활용해 구현할 수 있습니다.
🔔 MVC 패턴을 설명하고 MVVM 패턴과의 차이는 무엇인지 설명해보세요
"MVC와 MVVM 패턴의 차이를 설명해주세요."
- MVC: Controller가 View와 Model 사이를 연결하는 전통적 구조
- MVVM: ViewModel이 중간에서 데이터를 바인딩하여 양방향 연동을 가능하게 함
MVC
- View ↔ Controller ↔ Model 구조
- View와 Model 간 의존이 강해질 수 있음
MVVM
- View ↔ ViewModel ↔ Model 구조
- DataBinding과 LiveData 등으로 View와 로직이 깔끔히 분리됨
(꼬리 질문) "안드로이드에서 MVVM이 선호되는 이유는?"
ViewModel이 생명주기를 관리하며, UI와 비즈니스 로직을 깔끔히 분리할 수 있어 테스트가 용이하고 유지보수가 쉬워집니다.
'취준 > 면접' 카테고리의 다른 글
[면접 준비] - 벡엔드 (0) | 2024.05.20 |
---|---|
[면접 준비] - 운영체제 (0) | 2024.05.20 |
[면접 준비] - 네트워크 (0) | 2024.05.20 |
[면접 준비] - 자료구조 (0) | 2024.05.20 |
[면접 준비] - 데이터베이스 (0) | 2024.05.20 |