[JAVA] 래퍼, Class 클래스
[JAVA] 래퍼, Class 클래스
기본형의 한계와 래퍼 클래스
기본형의 한계
자바는 객체 지향 언어이지만 int
, double
같은 기본형(Primitive Type) 은 객체가 아니라 객체 지향의 장점을 온전히 사용할 수 없다.
기본형의 한계를 이해하기 위해, 두 값을 비교해서 다음과 같은 결과를 출력하는 간단한 코드를 작성해보자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) {
int value = 10;
int i1 = compareTo(value, 5);
int i2 = compareTo(value, 10);
int i3 = compareTo(value, 20);
System.out.println("i1 = " + i1);
System.out.println("i2 = " + i2);
System.out.println("i3 = " + i3);
}
public static int compareTo(int value, int target) {
if (value < target) {
return -1;
} else if (value > target) {
return 1;
} else {
return 0;
}
}
//실행결과
i1 = 1
i2 = 0
i3 = -1
주요 한계점
- 메서드 없음: 기본형은 메서드를 가질 수 없음 (객체와 달리
value.compareTo()
같은 메서드 호출 불가) - 컬렉션 제약: 기본형은 제네릭, List 등에서 직접 사용할 수 없음
- null 사용 불가: 기본형은 항상 값을 가져야 하므로 “값 없음” 상태 표현 불가
직접 만든 래퍼 클래스
기본형 int
를 감싸는 MyInteger
클래스 정의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyInteger {
private final int value;
public MyInteger(int value) { this.value = value; }
public int getValue() { return value; }
public int compareTo(int target) {
if (value < target) return -1;
else if (value > target) return 1;
else return 0;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
- 메서드를 객체에 캡슐화하여 직접 호출 가능
- 비교, 출력 등 기능을 메서드로 제공
- 불변 객체 설계 가능
1
2
MyInteger myInteger = new MyInteger(10);
System.out.println(myInteger.compareTo(5)); // 1
기본형은 null을 사용 불가
1
2
3
4
5
6
7
// 기본형의 문제점: -1은 실제 값인지, 없는 값인지 모호함
private static int findValue(int[] intArr, int target) {
for (int value : intArr) {
if (value == target) return value;
}
return -1;
}
→ -1
은 값이기도 하고 “없음”이기도 함 → 명확하지 않음
객체는 null을 사용 가능!
1
2
3
4
5
6
7
8
9
10
11
12
13
// 래퍼 클래스를 이용해 '값 없음'을 null로 명확히 구분 가능
private static MyInteger findValue(MyInteger[] intArr, int target) {
for (MyInteger myInteger : intArr) {
if (myInteger.getValue() == target) return myInteger;
}
return null;
}
//실행결과
-1
0
1
null
-
기본형은 객체가 아니기 때문에 다음과 같은 제약이 있음
→ 메서드 사용 불가, null 표현 불가, 컬렉션/제네릭 사용 불가
- 직접 만든 래퍼 클래스를 사용하면 객체처럼 다룰 수 있음
- 자바는
Integer
,Double
같은 표준 래퍼 클래스를 제공함
자바 래퍼 클래스
래퍼 클래스란?
- 기본형을 객체로 감싸서 더 풍부한 기능을 제공하는 클래스
- 자바는 기본형에 대응하는 표준 래퍼 클래스를 기본 제공함
기본형 | 래퍼 클래스 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Integer newInteger = new Integer(10); // 미래에 삭제 예정, 대신에 valueOf() 사
Integer integerObj = Integer.valueOf(10); // 추천 방식
Long longObj = Long.valueOf(100);
Double doubleObj = Double.valueOf(10.5);
System.out.println("newInteger = " + newInteger);
System.out.println("integerObj = " + integerObj);
System.out.println("longObj = " + longObj);
System.out.println("doubleObj = " + doubleObj);
System.out.println("내부 값 읽기");
int intValue = integerObj.intValue();
System.out.println("intValue = " + intValue);
long longValue = longObj.longValue();
System.out.println("longObj = " + longValue);
System.out.println("비교");
System.out.println("==: " + (newInteger == integerObj));
System.out.println("equals: " + newInteger.equals(integerObj));
//실행 결과
newInteger = 10
integerObj = 10
longObj = 100
doubleObj = 10.5
내부 값 읽기
intValue = 10
longObj = 100
비교
==: false
equals: true
==
는 주소 비교 → 항상 equals()로 비교toString()
은 내부 값을 문자열로 출력하도록 이미 재정의되어 있음
박싱(Boxing) & 언박싱(Unboxing)
-
박싱: 기본형 → 래퍼 클래스
예)
Integer.valueOf(10)
-
언박싱: 래퍼 클래스 → 기본형
예)
integerObj.intValue()
오토 박싱 / 오토 언박싱
1
2
3
int value = 7;
Integer boxed = value; // 오토 박싱
int unboxed = boxed; // 오토 언박싱
→ 컴파일러가 Integer.valueOf()
, intValue()
호출 코드를 자동으로 추가함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Integer i1 = Integer.valueOf(10);//숫자, 래퍼 객체 반환
Integer i2 = Integer.valueOf("10");//문자열, 래퍼 객체 반환
int intValue = Integer.parseInt("10");//문자열 전용, 기본형 반환
//비교
int compareResult = i1.compareTo(20);
System.out.println("compareResult = " + compareResult);
//산술 연산
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
//실행결과
compareResult = -1
sum: 30
min: 10
max: 20
valueOf()
→ 래퍼 타입 반환 (숫자/문자열 모두)parseInt()
→ 문자열 → 기본형compareTo()
→ 비교 결과: -1 / 0 / 1
성능 비교
1
2
3
4
5
6
7
8
9
10
11
// 기본형 long
long sumPrimitive = 0;
for (int i = 0; i < 1_000_000_000; i++) {
sumPrimitive += i;
}
// 래퍼 클래스 Long (오토 박싱 발생)
Long sumWrapper = 0L;
for (int i = 0; i < 1_000_000_000; i++) {
sumWrapper += i;
}
실행 시간 예시 (M2 맥 기준)
long
: 318msLong
: 1454ms → 약 5배 차이
왜 차이가 날까?
- 기본형: 4~8byte만 사용
- 래퍼 클래스: 메타데이터 등 포함 → 추가 메모리 소모 (8~16byte 이상)
- 오토 박싱 반복 → 객체 생성 비용 증가
언제 어떤 걸 써야 하나?
상황 | 추천 방식 |
---|---|
성능 민감한 반복 연산 | 기본형 (int, long 등) |
유지보수가 중요한 일반 로직 | 래퍼 클래스 |
컬렉션, 제네릭 사용 필요 시 | 래퍼 클래스 |
값이 “없음(null)”을 표현 | 래퍼 클래스 |
최근에는 컴퓨터 성능이 워낙 좋아졌기 때문에, 성능보다는 유지보수성과 가독성 을 우선으로 코드를 작성하는 경우가 많다.
최적화는 실제로 성능 문제가 발생했을 때, 그 핵심 병목 구간만 타겟팅해서 수행하는 것이 더 효과적이다.
자바 유틸리티 클래스 정리
Class 클래스
클래스의 정보(메타데이터)를 다루는데 사용한다. Class
클래스를 통해 개발자는 실행중인 자바 어플리케이션 내부의 필요한 클래스의 속성과 메서드에 대한 정보를 조회하고 조작할 수 있다.
주요 기능
- 클래스 이름, 필드, 메서드, 부모 클래스, 인터페이스 조회
- 런타임에 객체 생성, 메서드 호출 가능 (리플렉션)
- 애노테이션 조회 및 처리
Class 객체 얻는 방법 3가지
1
2
3
4
5
Class clazz = String.class; //클래스에서 조회
Class clazz = new String().getClass(); //인스턴스에서 조회
Class clazz = Class.forName("java.lang.String"); //문자열로 조회
주요 메서드
1
2
3
4
Field[] fields = clazz.getDeclaredFields(); //클래스의 모든 필드 조회
Method[] methods = clazz.getDeclaredMethods(); //클래스의 모든 메서드 조회
Class<?> superclass = clazz.getSuperclass(); //클래스의 부모 클래스 조회
Class<?>[] interfaces = clazz.getInterfaces(); //클래스의 인터페이스 조회
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//Class 조회
Class clazz = String.class; // 1.클래스에서 조회
//Class clazz = new String().getClass();// 2.인스턴스에서 조회
//Class clazz = Class.forName("java.lang.String"); // 3.문자열로 조회
// 모든 필드 출력
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("Field: " + field.getType() + " " +
field.getName());
}
// 모든 메서드 출력
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("Method: " + method);
}
// 상위 클래스 정보 출력
System.out.println("Superclass: " + clazz.getSuperclass().getName());
// 인터페이스 정보 출력
Class[] interfaces = clazz.getInterfaces();
for (Class i : interfaces) {
System.out.println("Interface: " + i.getName());
}
-
출력 결과
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
Field: class [C value Field: int hash Field: long serialVersionUID Field: class [Ljava.io.ObjectStreamField; serialPersistentFields Field: interface java.util.Comparator CASE_INSENSITIVE_ORDER Method: public boolean java.lang.String.equals(java.lang.Object) Method: public java.lang.String java.lang.String.toString() Method: public int java.lang.String.hashCode() Method: public int java.lang.String.compareTo(java.lang.Object) Method: public int java.lang.String.compareTo(java.lang.String) Method: public int java.lang.String.indexOf(java.lang.String,int) Method: static int java.lang.String.indexOf(char[],int,int,java.lang.String,int) Method: static int java.lang.String.indexOf(char[],int,int,char[],int,int,int) Method: public int java.lang.String.indexOf(int) Method: public int java.lang.String.indexOf(java.lang.String) Method: public int java.lang.String.indexOf(int,int) Method: public static java.lang.String java.lang.String.valueOf(char) Method: public static java.lang.String java.lang.String.valueOf(java.lang.Object) Method: public static java.lang.String java.lang.String.valueOf(boolean) Method: public static java.lang.String java.lang.String.valueOf(char[],int,int) Method: public static java.lang.String java.lang.String.valueOf(char[]) Method: public static java.lang.String java.lang.String.valueOf(double) Method: public static java.lang.String java.lang.String.valueOf(float) Method: public static java.lang.String java.lang.String.valueOf(long) Method: public static java.lang.String java.lang.String.valueOf(int) Method: private static void java.lang.String.checkBounds(byte[],int,int) Method: public int java.lang.String.length() Method: public boolean java.lang.String.isEmpty() Method: public char java.lang.String.charAt(int) Method: public int java.lang.String.codePointAt(int) Method: public int java.lang.String.codePointBefore(int) Method: public int java.lang.String.codePointCount(int,int) Method: public int java.lang.String.offsetByCodePoints(int,int) Method: public void java.lang.String.getChars(int,int,char[],int) Method: void java.lang.String.getChars(char[],int) Method: public byte[] java.lang.String.getBytes() Method: public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException Method: public void java.lang.String.getBytes(int,int,byte[],int) Method: public byte[] java.lang.String.getBytes(java.nio.charset.Charset) Method: public boolean java.lang.String.contentEquals(java.lang.StringBuffer) Method: public boolean java.lang.String.contentEquals(java.lang.CharSequence) Method: private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder) Method: public boolean java.lang.String.equalsIgnoreCase(java.lang.String) Method: public int java.lang.String.compareToIgnoreCase(java.lang.String) Method: public boolean java.lang.String.regionMatches(int,java.lang.String,int,int) Method: public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int) Method: public boolean java.lang.String.startsWith(java.lang.String) Method: public boolean java.lang.String.startsWith(java.lang.String,int) Method: public boolean java.lang.String.endsWith(java.lang.String) Method: private int java.lang.String.indexOfSupplementary(int,int) Method: public int java.lang.String.lastIndexOf(int,int) Method: static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int) Method: static int java.lang.String.lastIndexOf(char[],int,int,java.lang.String,int) Method: public int java.lang.String.lastIndexOf(java.lang.String,int) Method: public int java.lang.String.lastIndexOf(int) Method: public int java.lang.String.lastIndexOf(java.lang.String) Method: private int java.lang.String.lastIndexOfSupplementary(int,int) Method: public java.lang.String java.lang.String.substring(int) Method: public java.lang.String java.lang.String.substring(int,int) Method: public java.lang.CharSequence java.lang.String.subSequence(int,int) Method: public java.lang.String java.lang.String.concat(java.lang.String) Method: public java.lang.String java.lang.String.replace(char,char) Method: public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence) Method: public boolean java.lang.String.matches(java.lang.String) Method: public boolean java.lang.String.contains(java.lang.CharSequence) Method: public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String) Method: public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String) Method: public java.lang.String[] java.lang.String.split(java.lang.String,int) Method: public java.lang.String[] java.lang.String.split(java.lang.String) Method: public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]) Method: public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable) Method: public java.lang.String java.lang.String.toLowerCase(java.util.Locale) Method: public java.lang.String java.lang.String.toLowerCase() Method: public java.lang.String java.lang.String.toUpperCase() Method: public java.lang.String java.lang.String.toUpperCase(java.util.Locale) Method: public java.lang.String java.lang.String.trim() Method: public char[] java.lang.String.toCharArray() Method: public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]) Method: public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]) Method: public static java.lang.String java.lang.String.copyValueOf(char[],int,int) Method: public static java.lang.String java.lang.String.copyValueOf(char[]) Method: public native java.lang.String java.lang.String.intern() Superclass: java.lang.Object Interface: java.io.Serializable Interface: java.lang.Comparable Interface: java.lang.CharSequence
System 클래스 - 시스템 제어 및 정보 조회
주요 기능
- 시간 조회:
System.currentTimeMillis()
,System.nanoTime()
- 환경 변수:
System.getenv()
- 시스템 속성:
System.getProperties()
,System.getProperty(key)
- 배열 고속 복사:
System.arraycopy(src, srcPos, dest, destPos, length)
- 프로그램 종료:
System.exit(status)
1
2
System.out.println(System.currentTimeMillis());
System.arraycopy(original, 0, copied, 0, original.length);
Math 클래스
자주 쓰는 메서드
Math.max(a, b)
,Math.min(a, b)
,Math.abs(x)
Math.ceil(x)
,Math.floor(x)
,Math.round(x)
Math.sqrt(x)
,Math.pow(a, b)
,Math.random()
1
2
System.out.println(Math.round(2.5)); // 3
System.out.println(Math.random()); // 0.0 ~ 1.0
Random 클래스
1
2
3
4
5
6
7
8
Random random = new Random(); // 매번 다른 결과
Random fixed = new Random(1); // 항상 같은 결과 (seed 고정)
int i = random.nextInt(); // 전체 범위
int i2 = random.nextInt(10); // 0 ~ 9
int i3 = random.nextInt(10) + 1; // 1 ~ 10
double d = random.nextDouble(); // 0.0 ~ 1.0
boolean b = random.nextBoolean(); // true/false
- Seed 고정 시 항상 같은 결과 → 테스트에 유용
- Math.random()도 내부적으로 Random 클래스 사용
자바의 유틸리티 클래스들은 복잡한 시스템 작업을 간단한 코드로 가능하게 해준다. 필요한 기능이 있다면, 먼저 JDK의 유틸 클래스부터 찾아보는 습관을 들이자.
This post is licensed under
CC BY 4.0
by the author.