String literal과 new String(””)의 차이
String literal
- String Constant Pool에 저장
- 동일한 문자열 리터럴이 여러 번 사용되더라도, Java는 메모리 절약을 위해 동일한 참조를 재사용합니다.
new String("")
- Heap에 저장
- Heap에 새로운 객체를 만들고, 기존 String Constant Pool에 동일한 문자열이 존재하더라도 무시하고 별도의 객체를 만듭니다.
- GC의 메모리 회수 대상
String Contant Pool?
Java의 String Constant Pool은 JVM이 관리하는 특별한 메모리 영역입니다. 이곳에 저장된 문자열 리터럴은 JVM 종료 시까지 유지되거나, 클래스가 언로드될 때만 제거됩니다. 즉, String Pool에 저장된 객체는 일반적으로 GC의 대상이 되지 않습니다.
꼬리 질문?
- 문자열이 String Constant Pool에 저장되고, 이를 JVM이 어떻게 식별하고 참조하는지?
- GC의 동작 원리? 메모리 회수 타이밍?
- GC와 Old Generation, Young Generation
String vs String Builder vs String Buffer
String은 불변 객체이다. 객체를 수정할 때마다 새로운 객체를 생성한다. 불변 객체이기 때문에 스레드 안전을 보장한다. 하지만 메모리 할당과 객체 생성 오버헤드가 발생하기 때문에 수정 작업이 상대적으로 느리다.
String Builder
- 가변 객체
- 객체를 수정할 때 새로 생성하지 않고 내부 버퍼에서 직접 수정한다.
- 멀티 스레드 환경에서는 동기화 문제가 발생할 수 있다. 따라서 싱글 스레드 환경에 적합하다.
String Buffer
String Builder 처럼 가변 객체이다. 히자만 String Builder와 다르게 동기화를 보장한다. 내부적으로 동기화(synchronized)가 적용되어 멀티스레드 환경에서도 안전하게 사용할 수 있다.
특징 | String | StringBuilder | StringBuffer |
---|---|---|---|
가변성 | 불변 (Immutable) | 가변 (Mutable) | 가변 (Mutable) |
스레드 안전성 | 안전 | 안전하지 않음 | 안전 |
성능 | 수정 작업이 느림 | 단일 스레드에서 빠름 | 멀티스레드 환경에서 안정적 |
사용 목적 | 고정 문자열, 키 값 처리 | 단일 스레드에서 자주 수정 | 멀티스레드에서 자주 수정 |
참고
String literal과 new String("") 방식의 차이일 뿐 모두 String이다. 따라서 모두 가변 객체이다.
꼬리 질문
- 불변성 유지와 스레드 안전성의 관계는?
여러 스레드가 동시에 객체를 사용하더라도 불변성을 유지하면 데이터의의 일관성과 안전성을 유지할 수 있다.
즉, 데이터의 경쟁 조건이나 동기화 문제가 발생하지 않는다.
- String Builder와 String Buffer의 공통점 또는 차이점은?
- String Builder와 String Buffer는 어디에 저장되는가?
new String("")와 동일하게 Heap에 저장된다. 가변 객체는 기본적으로 Heap에 저장된다.
어노테이션
Java에서 메타데이터를 코드에 추가하기 위한 문법이다. 주석과 비슷해 보이지만, 코드에 의미를 부여하거나 특정 동작을 지시할 수 있다.
주로 컴파일러나 런타임에서 특정 작업을 수행하도록 정보를 제공하는 역할을 한다.
- Java와 같은 언어에서 메타데이터를 코드에 추가하기 위한 도구
- 가독성, 생산성, 유지보수성을 높인다. 개발자의 반복 작업과 설정을 줄이는데 사용한다.
- Retention 정책을 통해 어노테이션이 유지되는 시점(컴파일러, 클래스 파일, 런타임)을 설정할 수 있다.
Retention 정책 | 유지 시점 | 저장 위치 | 적용 예시 |
---|---|---|---|
SOURCE | 소스 코드에서만 유지 | 없음 | @Override , @SuppressWarnings |
CLASS | 클래스 파일에 저장, 런타임에는 제거 | .class 파일 | 내부 프레임워크 어노테이션 |
RUNTIME | 런타임까지 유지 | .class 파일 | @Autowired , @Transactional,@Test |
@Override 동작
1. 부모 클래스의 메서드를 확인
자식 클래스의 메서드가 부모 클래스에 정의된 메서드와 시그니처(메서드 이름, 매개변수, 반환 타입)가 정확히 일치하는지를 컴파일러가 확인한다. 만약 일치하지 않으면 컴파일 오류가 발생한다.
2. 정상적인 오바라이드
확인 후, 부모 클래스의 메서드는 유지되고, 자식 클래스의 메서드가 부모 메서드를 재정의(override) 합니다. 런타임 시 호출되는 메서드는 객체의 실제 타입(즉, 자식 클래스)에 따라 결정됩니다.
@Override의 Retention 정책은 SOURCE이다. 소스 코드에서만 유지된다. `@Override`의 주요 목적은 오버라이드 실수를 방지하는 데 있습니다. 즉, `@Override`는 부모 클래스의 메서드를 정확히 오버라이드했는지 검증하는 도구이다.
추가 예시
@Test
생산성 문제: 테스트 어노테이션이 없었으면, 직접 테스트 메서드를 호출해야 한다.
Hibernate ORM 매핑
어노테이션이 없으면 XML 파일 설정을 통해 매핑해야 한다.
가독성 문제: 클래스와 매핑 정보가 분리되어 읽기 어렵다.
꼬리 질문
- Rention 정책이란? 종류는? 각 종류에 대해 설명
- Override의 사용 목적은? 용이한 점은?
어노테이션과 리플렉션
어노테이션은 리플렉션으로 동작한다? 아니다!
RUNTIME 타입일 때만 리플렉션으로 동작한다. Retention 정책이 SOURCE 또는 CLASS에서는 리플렉션 불가능하다.
리플렉션이란?
리플렉션는 반사라는 의미를 가지고 있다. 모든 클래스는 JVM의 클래스 로더를 통해 읽혀지고, 해당 정보(클래스 구조, 필드, 메서드 등 메타데이터)는 메타스페이스라는 메모리 영역에 저장된다. 읽어들인 클래스 정보가 곧 거울에 비친 자신의 모습과 같다고 해서 리플렉션이라는 용어를 사용한다고 한다. 리플렉션은 이렇게 저장된 클래스 정보를 참조하여 클래스를 조사하거나 조작하는 기술이다. 스프링 프레임워크에서의 대표적인 리플렉션 활용으로 꼽자면 @Autowired를 꼽을 수 있다.
스프링 프레임워크에서는 리플렉션 과정을 자동으로 수행한다. 스프링이 제공하는 AutowiredAnnotationBeanPostProcessor 클래스가 이 역할을 담당한다.
- 빈 생성 후 리플렉션으로 @Autowired 어노테이션 붙은 필드, 메서드, 생성자를 탐색
- 해당 빈의 타입에 맞는 의존성을 스프링 컨테이너에서 검색
- 리플렉션을 사용해 의존성 주입
클래스 조작
- 메모리에 올라간 클래스 정보를 동적으로 탐색하고 조작할 수 있는 기능을 제공
- 메서드 호출, 필드 값 읽기/쓰기, 객체 생성 등이 리플렉션으로 가능
어노테이션은 리플렉션을 통해 어떻게 활용되는가? 앞서 @Autowired는 리플렉션을 통해 의존성 주입을 한다고 설명했다. 어노테이션은 리플렉션을 통해 런타임 시점에 클래스, 메서드, 필드, 파라미터 등에 부여된 추가 정보를 읽고 처리하는 데 사용된다. 어노테이션 자체는 단순한 메타데이터(정적 데이터)지만, 리플렉션을 통해 어노테이션의 존재를 확인하고 그에 따라 동적으로 동작할 수 있다.
- 어노테이션은 클래스, 메서드, 필드 등에 붙어 정보를 제공하는 역할
- 하지만 어노테이션은 정적 데이터일 뿐, 리플렉션이 없으면 실행 시 이를 읽거나 처리할 수 없다.
실무에서 @ConditionalOnProperty을 사용한 적 있는데, @ConditionalOnProperty는 특정 프로퍼티가 설정된 경우에만 빈을 등록하도록 조건을 걸 수 있는 어노테이션이다. 리플렉션을 통해 어노테이션의 속성값을 읽고, 조건에 따라 동적으로 객체를 생성해서 빈으로 등록된다.
@ConditionalOnProperty(prefix = "firebase", name = "api", havingValue = "v1")
@Bean
public FcmSender fcmMulticaster(@Value("${firebase.fcm.ttl}") String ttl) {
FirebaseMessaging firebaseMessaging = firebaseMessagingConfig.firebaseMessaging();
return new FcmMulticaster(firebaseMessaging, fcmTokenQuery, ttl);
}
'외부활동 > JSCODE 자바' 카테고리의 다른 글
자바 4주차: 동시성 프로그래밍 기초(작성 중) (0) | 2025.01.19 |
---|---|
자바 3주차: 컬렉션 기초(작성 중) (0) | 2025.01.17 |
자바 1주차: 자바 객체 지향 (0) | 2025.01.03 |
자바 1주차: 자바 기본 (0) | 2025.01.03 |