Post

[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: 318ms
  • Long: 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의 유틸 클래스부터 찾아보는 습관을 들이자.


출처: 김영한의 실전 자바 - 중급 1편

This post is licensed under CC BY 4.0 by the author.

Trending Tags