Reflection을 이해하지 못하는 사람들의 대부분은 사실 Class 객체를 제대로 이해하지 못한 상태이다.
Class 객체란?
"Java에서 모든 클래스는 JVM 안에서 Class라는 객체로 관리된다."
처음 보면 이상한 문장이다.
클래스가 객체라니?
이번 글에서는 이 문장이 왜 맞는 말인지 이해해보자.
우리가 알고 있는 클래스
예를 들어 다음과 같은 클래스가 있다.
public class User {
private String name;
public void login() {
System.out.println("login");
}
}
우리는 보통 이것을
User 클래스
라고 부른다.
그리고 객체를 생성한다.
User user = new User();
User 클래스
│
▼
new User()
│
▼
User 객체 생성
여기까지는 모두 알고 있다.
그런데 JVM은 다르게 생각한다.
JVM에게는
User.java
가 중요한 것이 아니다.
JVM은
User.java
↓
컴파일
↓
User.class
↓
ClassLoader가 읽음
↓
Class 객체 생성
↓
필요할 때 객체 생성 가능
이라는 순서로 동작한다.
여기서 핵심은
JVM은 User 클래스를 직접 다루는 것이 아니라 User를 표현하는 Class 객체를 다룬다.
실제 메모리에서는
Method Area
─────────────────────
User 클래스 정보
(String)
(int)
필드
메서드
생성자
Annotation
상속 정보
...
─────────────────────
Heap
Class<User> 객체 <───────────────┐
│
User user1 │
User user2 │
User user3 │
│
모두 같은 Class 객체를 참조────────┘
Class 객체는 하나만 존재한다.
객체가 천 개 만들어져도 Class 객체는 하나이다.
왜 Class 객체가 필요할까?
예를 들어
User user1 = new User();
User user2 = new User();
User user3 = new User();
객체마다
필드 목록
메서드 목록
생성자 목록
까지 모두 복사한다면 엄청난 메모리가 낭비된다.
그래서 JVM은
Class 객체
↓
필드 정보
메서드 정보
생성자 정보
Annotation 정보
를 하나만 가지고 있다.
객체들은 그것을 공유한다.
객체는 무엇을 가지고 있을까?
실제로 객체는
User user = new User();
라고 하면
Heap
User 객체
----------------
name = null
(Class<User>를 가리키는 내부 정보)
----------------
정도만 가진다.
메서드 코드가 객체 안에 들어있는 것이 아니다.
메서드는 Class 객체에 있다.
Class 객체는 언제 만들어질까?
User user = new User();
이 실행되는 순간 만들어질까?
아니다.
실제로는
User.class가 처음 사용되는 순간
↓
ClassLoader가 로딩
↓
Class 객체 생성
↓
Method Area 저장
↓
Heap에 Class 객체 생성
↓
이후 계속 재사용
한 번만 만들어진다.
확인해보자
User user1 = new User();
User user2 = new User();
System.out.println(user1.getClass());
System.out.println(user2.getClass());
결과
class User
class User
여기서
System.out.println(user1.getClass() == user2.getClass());
결과
true
이다.
왜냐하면
둘 다 같은 Class 객체를 참조하기 때문이다.
Class 객체를 얻는 방법
1. 가장 많이 사용하는 방법
Class<User> clazz = User.class;
컴파일 시 타입이 결정되어 있을 때 사용한다.
2. 객체에서 얻기
User user = new User();
Class<?> clazz = user.getClass();
이미 객체가 있을 때 사용한다.
3. 문자열로 얻기
Class<?> clazz =
Class.forName("com.example.User");
문자열만 가지고도 가져올 수 있다.
Spring, JDBC Driver, 프레임워크들이 많이 사용하는 방식이다.
Class 객체 안에는 무엇이 들어있을까?
Reflection을 이용하면 확인할 수 있다.
Class<User> clazz = User.class;
여기서
클래스 이름
clazz.getName();
com.example.User
필드 목록
clazz.getDeclaredFields();
name
age
email
...
메서드 목록
clazz.getDeclaredMethods();
login()
logout()
update()
...
생성자 목록
clazz.getDeclaredConstructors();
User()
User(String)
Annotation
clazz.getAnnotations();
@Service
@Transactional
...
상속 관계
clazz.getSuperclass();
Object
구현 인터페이스
clazz.getInterfaces();
Serializable
Cloneable
...
Spring에서는 어떻게 사용할까?
Spring Boot가 시작된다.
Application Start
↓
패키지 스캔
↓
Class 객체 발견
↓
Class 객체 조사
↓
@Service 확인
↓
객체 생성
↓
Bean 등록
Spring은 실제 클래스를 직접 다루는 것이 아니라
항상 Class 객체를 조사한다.
Class 객체와 new의 관계
많은 사람들이
new User()
↓
Class 객체 생성
이라고 생각한다.
실제로는
User.class 최초 사용
↓
ClassLoader
↓
Class 객체 생성
↓
new User()
↓
Class 객체를 이용하여 객체 생성
이다.
즉,
new는 Class 객체를 이용해서 Heap에 새로운 인스턴스를 만드는 과정이다.
Class 객체는 싱글톤처럼 동작한다.
같은 JVM 안에서는
User.class
에 대한 Class 객체는 하나만 존재한다.
User.class == User.class
결과
true
user1.getClass() == user2.getClass()
결과
true
이다.
면접 포인트
Class 객체란?
JVM이 클래스의 메타데이터(필드, 메서드, 생성자, Annotation, 상속 정보 등)를 관리하기 위해 생성하는 객체이다.
언제 생성되는가?
클래스가 처음 로딩될 때 ClassLoader에 의해 생성되며, JVM에서 하나만 존재하고 모든 인스턴스가 공유한다.
Reflection과의 관계는?
Reflection은 Class 객체를 기반으로 클래스 정보를 조회하고 객체 생성, 메서드 호출, 필드 접근 등을 수행하는 기능이다.
다음 글 추천
이 흐름에서 가장 자연스럽게 이어지는 주제는 "Annotation 동작 원리"이다.
Reflection과 Class 객체를 이해했다면,
@Service는 그냥 코드 위에 붙어있는 글자인데 Spring은 어떻게 그걸 읽고 Bean을 생성하는가?
를 이해할 수 있게 된다.
'language > java' 카테고리의 다른 글
| Meta Annotation (0) | 2026.06.16 |
|---|---|
| Annotation 동작 원리 (0) | 2026.06.16 |
| Reflection 원리 (0) | 2026.06.16 |
| 번외편 - Java Comparator vs JavaScript sort() (0) | 2026.06.04 |
| 번외편) groupingBy()와 toMap()은 내부 아이디어가 매우 비슷하다. (0) | 2026.06.01 |
댓글