196 lines
6.9 KiB
Markdown
196 lines
6.9 KiB
Markdown
# **자바 리플렉션(Reflection) 쉽게 배우기**
|
|
|
|
## **1. 리플렉션이란?**
|
|
리플렉션(Reflection)이란 **실행 중에 클래스의 정보를 조회하고, 필드·메서드·생성자를 동적으로 조작하는 기능**이다.
|
|
즉, **컴파일 시점이 아니라 런타임(실행 중)에 클래스 내부를 들여다보고 조작할 수 있다.**
|
|
|
|
---
|
|
|
|
## **2. 주요 클래스 및 메서드 정리**
|
|
|
|
### **(1) `Class` 클래스 (클래스 정보 조회)**
|
|
| 메서드 | 설명 |
|
|
|-----|---|
|
|
| `Class.forName("클래스명")` | 클래스 객체 가져오기 (정적 로드) |
|
|
| `Object.getClass()` | 인스턴스로부터 클래스 객체 얻기 |
|
|
| `getName()` | 클래스 전체 이름 (`패키지.클래스명`) 반환 |
|
|
| `getSimpleName()` | 클래스 단순 이름 반환 |
|
|
| `getDeclaredFields()` | 선언된 모든 필드(`Field[]`) 조회 |
|
|
| `getDeclaredMethods()` | 선언된 모든 메서드(`Method[]`) 조회 |
|
|
| `getDeclaredConstructors()` | 선언된 모든 생성자(`Constructor[]`) 조회 |
|
|
|
|
**예제 코드 (클래스 정보 출력)**
|
|
```java
|
|
Class<?> clazz = Class.forName("java.util.ArrayList");
|
|
|
|
System.out.println("클래스 이름: " + clazz.getName());
|
|
System.out.println("간단한 이름: " + clazz.getSimpleName());
|
|
System.out.println("패키지: " + clazz.getPackage().getName());
|
|
```
|
|
✅ **클래스의 이름과 패키지 정보를 확인할 수 있다!**
|
|
|
|
---
|
|
|
|
### **(2) `Field` 클래스 (필드 정보 조회 및 수정)**
|
|
| 메서드 | 설명 |
|
|
|-----|---|
|
|
| `getName()` | 필드 이름 가져오기 |
|
|
| `getType()` | 필드 타입 가져오기 |
|
|
| `getModifiers()` | 접근 제어자 가져오기 |
|
|
| `setAccessible(true)` | private 필드 접근 가능하게 설정 |
|
|
| `get(Object obj)` | 특정 객체의 필드 값 가져오기 |
|
|
| `set(Object obj, Object value)` | 특정 객체의 필드 값 변경 |
|
|
|
|
**예제 코드 (필드 조회 및 값 변경)**
|
|
```java
|
|
import java.lang.reflect.*;
|
|
|
|
class Person {
|
|
private String name = "John";
|
|
}
|
|
|
|
public class ReflectionExample {
|
|
public static void main(String[] args) throws Exception {
|
|
Person person = new Person();
|
|
Class<?> clazz = person.getClass();
|
|
|
|
Field field = clazz.getDeclaredField("name");
|
|
field.setAccessible(true); // private 접근 허용
|
|
|
|
System.out.println("기존 값: " + field.get(person));
|
|
field.set(person, "Alice"); // 값 변경
|
|
System.out.println("변경된 값: " + field.get(person));
|
|
}
|
|
}
|
|
```
|
|
✅ **private 필드도 강제로 조작할 수 있다!**
|
|
|
|
---
|
|
|
|
### **(3) `Method` 클래스 (메서드 정보 조회 및 호출)**
|
|
| 메서드 | 설명 |
|
|
|-----|---|
|
|
| `getName()` | 메서드 이름 가져오기 |
|
|
| `getParameterTypes()` | 메서드 매개변수 타입 가져오기 |
|
|
| `getReturnType()` | 반환 타입 가져오기 |
|
|
| `invoke(Object obj, Object... args)` | 메서드 실행 |
|
|
|
|
**예제 코드 (메서드 실행)**
|
|
```java
|
|
import java.lang.reflect.*;
|
|
|
|
class Calculator {
|
|
private int add(int a, int b) {
|
|
return a + b;
|
|
}
|
|
}
|
|
|
|
public class ReflectionExample {
|
|
public static void main(String[] args) throws Exception {
|
|
Calculator calc = new Calculator();
|
|
Class<?> clazz = calc.getClass();
|
|
|
|
Method method = clazz.getDeclaredMethod("add", int.class, int.class);
|
|
method.setAccessible(true); // private 접근 허용
|
|
|
|
int result = (int) method.invoke(calc, 5, 10); // 메서드 실행
|
|
System.out.println("결과: " + result);
|
|
}
|
|
}
|
|
```
|
|
✅ **private 메서드도 실행할 수 있다!**
|
|
|
|
---
|
|
|
|
### **(4) `Constructor` 클래스 (생성자 정보 조회 및 인스턴스 생성)**
|
|
| 메서드 | 설명 |
|
|
|-----|---|
|
|
| `getParameterTypes()` | 생성자 매개변수 타입 가져오기 |
|
|
| `newInstance(Object... initargs)` | 새로운 인스턴스 생성 |
|
|
|
|
**예제 코드 (객체 동적 생성)**
|
|
```java
|
|
import java.lang.reflect.*;
|
|
|
|
class Person {
|
|
private String name;
|
|
|
|
public Person(String name) {
|
|
this.name = name;
|
|
}
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
}
|
|
|
|
public class ReflectionExample {
|
|
public static void main(String[] args) throws Exception {
|
|
Class<?> clazz = Person.class;
|
|
|
|
Constructor<?> constructor = clazz.getConstructor(String.class);
|
|
Object person = constructor.newInstance("Charlie"); // 동적 생성
|
|
|
|
Method method = clazz.getMethod("getName");
|
|
System.out.println("이름: " + method.invoke(person)); // Charlie 출력
|
|
}
|
|
}
|
|
```
|
|
✅ **리플렉션을 이용해 생성자를 실행하고 객체를 만들 수 있다!**
|
|
|
|
---
|
|
|
|
### **(5) `Modifier` 클래스 (접근 제어자 확인)**
|
|
| 메서드 | 설명 |
|
|
|-----|---|
|
|
| `isPublic(int mod)` | public 여부 확인 |
|
|
| `isPrivate(int mod)` | private 여부 확인 |
|
|
| `isStatic(int mod)` | static 여부 확인 |
|
|
|
|
**예제 코드 (필드 접근 제어자 확인)**
|
|
```java
|
|
import java.lang.reflect.*;
|
|
|
|
class Sample {
|
|
private int num;
|
|
public static String text;
|
|
}
|
|
|
|
public class ReflectionExample {
|
|
public static void main(String[] args) throws Exception {
|
|
Field field = Sample.class.getDeclaredField("text");
|
|
int modifiers = field.getModifiers();
|
|
|
|
System.out.println("static인가? " + Modifier.isStatic(modifiers)); // true
|
|
System.out.println("public인가? " + Modifier.isPublic(modifiers)); // true
|
|
}
|
|
}
|
|
```
|
|
✅ **필드나 메서드가 `public`, `private`, `static`인지 확인할 수 있다!**
|
|
|
|
---
|
|
|
|
## **3. 리플렉션의 활용 예시**
|
|
✔ **DI(의존성 주입) 프레임워크(Spring)** → 리플렉션으로 객체를 생성하고 자동 주입
|
|
✔ **JUnit 테스트 프레임워크** → `@Test` 붙은 메서드 자동 실행
|
|
✔ **JSON 라이브러리(Jackson, Gson)** → 객체를 JSON으로 변환할 때 필드 조회
|
|
✔ **프록시 패턴(AOP, 동적 프록시)** → 런타임에 동적 메서드 실행
|
|
|
|
---
|
|
|
|
## **4. 리플렉션의 단점과 주의점**
|
|
❌ **성능 저하** → 리플렉션은 일반 메서드 호출보다 느리다. 자주 사용하면 성능 문제가 생길 수 있다.
|
|
❌ **보안 문제** → `setAccessible(true)`로 private 필드/메서드에 접근할 수 있어 보안 위험이 있다.
|
|
❌ **컴파일 타임 체크 불가능** → 오타나 잘못된 메서드 호출은 실행 시점에서야 오류가 발생한다.
|
|
|
|
✅ **따라서, 꼭 필요한 경우에만 사용하고 남용하지 않는 것이 좋다!**
|
|
|
|
---
|
|
|
|
## **5. 정리**
|
|
✅ **리플렉션은 실행 중 클래스 정보를 조회하고 조작할 수 있는 기능이다!**
|
|
✅ **`Class`, `Field`, `Method`, `Constructor` 클래스를 활용하면 필드 값 변경, 메서드 실행, 객체 생성 등이 가능하다!**
|
|
✅ **Spring, JSON 파싱, 테스트 프레임워크 등에서 널리 사용된다!**
|
|
✅ **하지만 성능 저하와 보안 문제를 고려하여 신중히 사용해야 한다!**
|
|
|
|
✔ **리플렉션을 잘 활용하면, 자바 프로그램을 더 유연하게 만들 수 있다!** |