1️⃣ Primitive Type (기본형 타입)
Primitive Type은 Java에서 가장 기본적인 데이터 타입으로, 값 자체를 저장하는 타입이다.
Primitive Type은 스택(Stack) 메모리에 저장되며, 객체가 아니기 때문에 메서드를 가질 수 없고, null을 가질 수도 없다.
Java의 Primitive Type 8가지
타입 | 크기 | 기본값 | 설명 |
byte | 1 byte | 0 | 정수형 (범위: -128 ~ 127) |
short | 2 byte | 0 | 정수형 (범위: -32,768 ~ 32,767) |
int | 4 byte | 0 | 정수형 (범위: -2^31 ~ 2^31-1) |
long | 8 byte | 0L | 정수형 (범위: -2^63 ~ 2^63-1) |
float | 4 byte | 0.0f | 실수형 (단정도 부동소수점) |
double | 8 byte | 0.0 | 실수형 (배정도 부동소수점) |
boolean | 1 byte (이론상) | false | 논리형 (true/false) |
char | 2 byte | ‘\u0000’ | 문자형 (유니코드 문자 저장) |
Primitive Type 특징
- 연산 속도가 빠르다
- null을 저장할 수 없다.
- 객체가 아니므로 메서드를 호출할 수 없다.
- Wrapper Class를 이용하면 Reference Type으로 변환 가능하다. (Boxing)
2️⃣ Reference Type (참조형 타입)
Reference Type은 객체를 가리키는 주소를 저장하는 타입이다.
Reference Type은 힙(Heap) 메모리에 객체가 생성되며, 참조 변수는 스택(Stack)에 저장된 주소값을 가진다.
Reference Type의 주요 예시
- Boxed Primitive Type (Wrapper Class): Integer, Double, Boolean 등
- String: String
- Collection Framework: List, Set, Map, Queue, Stack 등
- 배열(Array): int[], String[] 등
- 사용자 정의 클래스: Person, Car 등
- 열거형(enum): enum Color { RED, GREEN, BLUE }
Reference Type 특징
- 객체를 생성하면 힙(Heap) 메모리에 저장되고, 변수가 그 객체의 참조(주소)를 저장한다.
- null을 저장할 수 있다.
- 메서드를 가질 수 있다.
- Primitive Type보다 연산 속도가 느리다.
3️⃣ Boxed Primitive Type (박싱된 기본형 타입)
Boxed Primitive Type은 Primitive Type을 객체로 감싸는 타입으로, Reference Type에 속한다.
즉, Primitive Type을 Wrapper Class로 감싸면 Boxed Primitive Type이 된다.
Wrapper Class
Wrapper Class는 Primitive Type을 감싸는 클래스이며, Boxed Primitive Type을 위한 클래스이다.
즉, Boxed Primitive Type = Wrapper Class의 객체이다. (모든 Boxed Primitive Type은 Wrapper Class의 인스턴스이지만,
Wrapper Class 자체는 객체를 생성하지 않은 상태일 수도 있다.)
Java의 주요 Wrapper Class
Primitive Type | Wrapper Class |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
Wrapper Class의 주요 메서드
- Integer.parseInt("123") → 문자열을 정수로 변환
- Double.valueOf(3.14) → 기본형을 객체로 변환
- Boolean.TRUE.equals(true) → 논리값 비교
- Integer.valueOf(10).compareTo(20) → 값 비교 (크면 1, 같으면 0, 작으면 -1)
- Integer.sum(), Integer.min(), Integer.max() → 간단한 산술 연산 기능
int intValue = Integer.parseInt("10"); // 문자열을 정수로 변환
Integer i1 = Integer.valueOf(10); // 기본형을 객체로 변환
Integer i2 = Integer.valueOf("10"); // 문자열을 객체로 변환
System.out.println(i1.equals(10)); // true
System.out.println(i1.compareTo(10)); // 0
System.out.println("sum: " + Integer.sum(10, 20)); // 30
System.out.println("min: " + Integer.min(10, 20)); // 10
System.out.println("max: " + Integer.max(10, 20)); // 20
Boxed Primitive Type 특징
- 기본형 타입을 객체로 다뤄야 할 때 사용 (ex. 컬렉션 요소로 저장)
- null 을 저장할 수 있다.
- Primitive Type을 Wrapper Class로 변경하는 것을 박싱(Boxing), Wrapper Class를 Primitive Type으로 변경하는 것을 언박싱(Unboxing)이라고 한다.
// 박싱
Integer newInteger = new Integer(10); // 생성자 방식은 버전 9부터 deprecated 상태다. valueOf() 사용을 권장한다.
Integer integerObj = Integer.valueOf(10);
// 언박싱
int intValue = integerObj.intValue();
- Java는 컴파일러가 자동으로 박싱, 언박싱을 해주는 오토 박싱(Auto-Boxing), 오토 언박싱(Auto-Unboxing)을 지원한다.
- Auto-Boxing: Primitive Type → Wrapper Class로 자동 변환
Integer num = 10; // int → Integer (Auto-Boxing)
- Auto-Unboxing: Wrapper Class → Primitive Type으로 자동 변환
int value = num; // Integer → int (Auto-Unboxing)
+) Boxed Primitive Type이 필요한 이유
1. 컬렉션(Collection)과 제네릭(Generic) 사용
Java의 제네릭(Generic) 타입과 컬렉션(Collection Framework)은 Primitive Type을 직접 저장할 수 없다.
- Java의 컬렉션(List, Map, Set 등)은 Reference Type(객체)만 저장 가능하다.
- 따라서, int 같은 Primitive Type을 Integer로 감싸야 한다.
📌 예제 - ArrayList에 int 값을 저장하려면 Integer 사용 필요
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// int를 직접 저장할 수 없음 → Integer 사용
ArrayList<Integer> list = new ArrayList<>();
list.add(10); // Auto-Boxing (int → Integer)
list.add(20);
System.out.println(list); // [10, 20]
}
}
💡 왜 Primitive Type을 직접 사용할 수 없을까?
제네릭(List<T>)은 객체(Reference Type)만을 처리하도록 설계되었기 때문!
- List<int> ❌ (불가능)
- List<Integer> ✅ (가능)
2. Object 타입과의 호환성
Primitive Type은 Object 를 상속받지 않기 때문에, Object 타입이 필요한 곳에서는 반드시 Boxed Primitive Type을 사용해야 한다.
📌 예제 - Object 타입으로 int 값을 저장해야 하는 경우
public class Main {
public static void main(String[] args) {
Object obj1 = 10; // Auto-Boxing (int → Integer)
Object obj2 = 3.14; // Auto-Boxing (double → Double)
System.out.println(obj1); // 10
System.out.println(obj2); // 3.14
}
}
💡 왜 필요할까?
- Object는 모든 Reference Type의 부모 클래스이지만, Primitive Type은 객체가 아니라서 포함되지 않는다.
- 따라서 int, double 같은 Primitive type을 Object 타입에 저장하려면 Boxing이 필요하다.
3. 기본형 타입에 메서드를 제공하기 위해
Primitive Type은 메서드를 가질 수 없지만, Boxed Primitive Type(Wrapper Class)은 다양한 유틸리티 메서드를 제공한다.
📌 예제 - Integer.parseInt()를 이용한 문자열 변환
public class Main {
public static void main(String[] args) {
String numStr = "123";
// 문자열을 int로 변환 (Primitive Type에서는 직접 지원 X)
int num = Integer.parseInt(numStr);
System.out.println(num * 2); // 246
}
}
💡 왜 Primitive Type에는 메서드가 없을까?
Primitive Type은 값 자체를 저장하는 단순한 데이터 타입이므로 객체가 아니라 메서드를 가질 수 없음 → Wrapper Class를 사용하면 다양한 메서드를 활용 가능!
4. 멀티스레딩 환경에서 동기화(Synchronization) 필요
Primitive Type은 Thread-Safe하지 않다. 즉, 여러 스레드에서 접근할 때 동기화가 필요하면 Boxed Primitive Type을 사용해야 한다.
📌 예제 - AtomicInteger를 활용한 동기화
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static void main(String[] args) {
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // 원자적 증가
System.out.println(count.get()); // 1
}
}
💡 왜 AtomicInteger를 사용할까?
- int는 멀티스레딩 환경에서 동기화 처리가 어려움.
- AtomicInteger(Boxed Primitive Type 기반)는 스레드 안전하게 동작하도록 설계됨.
5. Reflection 및 다양한 API와의 호환
Java의 일부 API는 Boxed Primitive Type을 사용해야만 동작한다. 예를 들어, Reflection API는 Class<T> 타입을 필요로 한다.
- int.class → Primitive Type
- Integer.class → Boxed Primitive Type (Wrapper Class)
📌 예제 - Reflection API에서 Integer.class 사용
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> intClass = int.class; // Primitive Type
Class<?> integerClass = Integer.class; // Boxed Primitive Type (Wrapper Class)
System.out.println(intClass.getName()); // int
System.out.println(integerClass.getName()); // java.lang.Integer
}
}
💡 왜 Primitive Type을 바로 사용할 수 없을까?
Reflection API는 객체 기반으로 설계되었기 때문!