7Java가 지원하는 4가지 OOP (Object-Oriented Programming) 원칙은 무엇인가요?
Java는 캡슐화, 추상화, 상속, 다형성을 지원합니다. 캡슐화는 공개 API 뒤로 내부 상태를 숨깁니다. 추상화는 핵심적인 동작에 집중합니다. 상속은 기존 타입을 기반으로 새로운 타입을 생성합니다. 다형성은 코드가 공유된 규약(contract)을 통해 다양한 구현체와 동작할 수 있도록 합니다. 실무에서는 결합도를 낮추기 위해 상속보다 컴포지션(합성)을 사용하는 것이 권장되는 경우가 많습니다.
interface PaymentService {
void pay(BigDecimal amount);
}
class CardPaymentService implements PaymentService {
@Override
public void pay(BigDecimal amount) {
// payment implementation
}
}
AI 코치와 함께 이 질문에 답해 보세요
8메서드 오버로딩과 오버라이딩의 차이점은 무엇인가요?
오버로딩은 메서드 이름은 같지만 매개변수가 다른 여러 메서드를 정의하는 것을 의미하며, 컴파일러가 정적 인자 타입을 기준으로 호출할 메서드를 선택합니다. 오버라이딩은 하위 클래스가 상위 클래스의 인스턴스 메서드 구현을 재정의하는 것을 의미하며, 런타임 디스패치가 실제 객체 타입을 기반으로 실행할 메서드를 선택합니다. static 메서드는 오버라이드되는 것이 아니라 숨겨지는(shadowing/hiding) 것이며, private 메서드는 접근 가능한 멤버로 상속되지 않으므로 오버라이드할 수 없습니다.
void send(String message) {}
void send(String message, int priority) {}
class Animal {
void speak() { System.out.println("Animal"); }
}
class Dog extends Animal {
@Override
void speak() { System.out.println("Dog"); }
}
AI 코치와 함께 이 질문에 답해 보세요
9인터페이스와 추상 클래스의 차이점은 무엇인가요?
인터페이스는 계약을 정의하며 추상 메서드, 디폴트 메서드, 정적 메서드, 상수, 그리고 private 헬퍼 메서드를 포함할 수 있습니다. 반면 추상 클래스는 이에 더해 인스턴스 상태, 생성자, protected 필드, 공유 구현, 추상 또는 구체 메서드를 가질 수 있습니다. 클래스는 하나의 클래스만 상속할 수 있지만 인터페이스는 여러 개를 구현할 수 있습니다. 독립적인 계약에는 인터페이스를 사용하고, 구현과 상태를 공유하는 밀접하게 관련된 타입들에는 추상 클래스를 사용합니다.
interface Repository<T> {
Optional<T> findById(long id);
}
AI 코치와 함께 이 질문에 답해 보세요
10final 키워드는 어떤 의미를 갖나요?
final 변수는 값을 한 번만 할당할 수 있지만, 참조가 final이라고 해서 참조 대상 객체 자체가 불변(immutable)이 되는 것은 아닙니다. final 메서드는 오버라이드할 수 없으며, final 클래스는 상속(확장)할 수 없습니다. final은 코드의 의도를 명확히 전달하고 참조 변경이나 상속을 제한하지만, 그 자체만으로 완전한 불변 모델이나 스레드 안전성(thread safety)을 제공하지는 않습니다.
final int maxAttempts = 3;
final List<String> names = new ArrayList<>();
names.add("Alice"); // allowed
public final void validate() {}
public final class Token {}
AI 코치와 함께 이 질문에 답해 보세요
11Java에서 레코드(record)란 무엇인가요?
레코드(record)는 불변 데이터 객체를 간결하게 정의하기 위한 문법입니다. 컴파일러가 private final 필드, 생성자, 접근자(accessor), equals, hashCode, toString을 자동으로 생성합니다. 레코드는 DTO(Data Transfer Object), 값 객체(value object), 메서드 반환 결과 등에 유용합니다. 하지만 모든 도메인 엔티티를 대체할 수는 없습니다. 콤팩트 생성자(compact constructor)에서 유효성 검증을 추가할 수는 있지만, 복잡한 가변 동작, 지연 로딩(lazy loading), 상속 기반 모델에는 레코드가 잘 맞지 않습니다.
public record User(long id, String name) {}
public record Order(List<String> items) {
public Order {
items = List.copyOf(items);
}
}
AI 코치와 함께 이 질문에 답해 보세요
12Java에서 불변 객체(immutable object)는 어떻게 만드나요?
클래스를 final로 선언하거나 상속을 엄격히 제한하고, 필드를 private final로 선언하며, setter 메서드를 제공하지 않고 생성자에서 유효성 검사를 수행합니다. 변경 가능한(mutable) 입력값에 대해서는 방어적 복사를 수행하고, 조회 시에도 복사본이나 수정 불가능한 뷰(unmodifiable view)를 반환해야 합니다. 깊은 불변성(deep immutability)을 보장하려면 모든 중첩된 상태 역시 불변이어야 합니다. final 필드는 안전한 초기화 및 공개(safe publication)를 돕지만, 변경 가능한 컬렉션이나 중첩 객체를 외부로 노출하면 해당 객체는 논리적으로 여전히 변경 가능할 수 있습니다.
public final class User {
private final String name;
private final List<String> roles;
public User(String name, List<String> roles) {
this.name = name;
this.roles = List.copyOf(roles);
}
public List<String> getRoles() {
return roles;
}
}
AI 코치와 함께 이 질문에 답해 보세요