[Java] 자바 중급(4) - 래퍼, Class 클래스

wony·2024년 3월 30일

Java

목록 보기
11/30

0.개요

주제 : 김영한님의 자바 중급 1편 총 정리
내용 : 래퍼, Class 클래스에 대해 공부

초간단 정리 :

  • 래퍼 클래스는 기본 데이터 타입( int, char 등)을 객체로 다룰 수 있게 해주는 클래스입니다.
  • 주요 래퍼 클래스는 Integer, Character, Boolean 등이 있습니다. 기본 데이터 타입을 객체로 감싸는( wrapper ) 역할을 합니다.
  • 쉽게 이야기해서 래퍼 클래스는 기본형의 객체 버전입니다.
    • valueOf() : 래퍼 타입을 반환. 숫자, 문자열을 모두 지원
    • parseInt() : 문자열을 기본형으로 변환

1.래퍼 클래스 - 기본형의 한계1

기본형의 한계

자바는 객체 지향 언어이다. 그런데 int, double같은 기본형은 객체가 아니다.
기본형은 객체가 아니기 때문에 다음과 같은 한계가 있다.

  • 객체가 아님 : 기본형 데이터는 객체가 아니기 때문에, 객체 지향 프로그램의 장점을 살릴 수 없다. 기본형은 객체가 아니므로 메서드를 제공할 수 없는 것이 대표적이다.
    • 추가로 객체 참조가 필요한 컬렉션 프레임워크를 사용할 수 없다. 그리고 제너릭도 사용할 수 없다.
  • null 값을 가질 수 없음 : 기본형 데이터는 null값을 가질 수 없다.
    때로는 데이터가 없음 이라는 상태를 나타내야 할 필요가 있는데, 기본형은 항상 값을 가지기 때문에 이런 표현을 할 수 없다.

직접 만든 래퍼 클래스

int를 클래스로 만들어보자. int는 클래스가 아니지만, int값을 가지고 클래스를 만들면 된다. 다음 코드는 마치 int를 클래스로 감싸서 만드는 것처럼 보인다. 이렇게 특정 기본형을 감싸서 만드는 클래스를 래퍼 클래스라 한다.

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); // 숫자를 문자로 변경
    }
}
  • MyIntegerint value 라는 단순한 기본형 변수를 하나 가지고 있다.
  • 그리고 이 기본형 변수를 편리하게 사용하도록 다양한 메서드를 제공한다.
  • 앞에서 본 compareTo() 메서드를 클래스 내부로 캡슐화 했다.
  • 이 클래스는 불변으로 설계했다.

MyInteger 클래스는 단순히 데이터 조각인 int를 내부에 품고, 메서드를 통해 다양한 기능을 추가했다. 덕분에 데이터 조각에 불과한 intMyInteger를 통해 객체로 다룰 수 있게 되었다.

public class MyIntegerMethodMain1 {
    public static void main(String[] args) {
        MyInteger myInteger = new MyInteger(10);
        int i1 = myInteger.compareTo(5);
        int i2 = myInteger.compareTo(10);
        int i3 = myInteger.compareTo(20);

        System.out.println("i1 = " + i1);
        System.out.println("i2 = " + i2);
        System.out.println("i3 = " + i3);
    }
}
  • MyInteger 는 객체이므로 자신이 가진 메서드를 편리하게 호출할 수 있다.
  • 참고로 int 는 기본형이기 때문에 스스로 메서드를 가질 수 없다.

2.래퍼 클래스 - 기본형의 한계2

기본형과 null

  • 기본형은 항상 값을 가져야 한다.
    하지만, 때로는 데이터가 "없음"이라는 상태가 필요할 수 있다.
  • 객체의 경우 데이터가 없다는 null을 통해 보여준다.
  • 물론 null 값을 반환하는 경우 잘못하면 NullPointerException 이 발생할 수 있기 때문에 주의해서 사용해야 한다.

3.래퍼 클래스 - 자바 래퍼 클래스

  • 지금까지 설명한 래퍼 클래스는 기본형을 객체로 감싸서 더 편리하게 사용하도록 도와주기 때문에 상당히 유용하다.
  • 쉽게 이야기해서 래퍼 클래스는 기본형의 객체 버전이다.

자바는 기본형에 대응하는 래퍼 클래스를 기본으로 제공한다.

  • byte -> Byte
  • short -> Short
  • int -> Integer
  • long -> Long
  • float -> Float
  • double -> Double
  • char -> Character
  • boolean -> Boolean

자바가 제공하는 기본 래퍼 클래스는 다음과 같은 특징을 가지고 있다.

  • 불변이다.
  • equals로 비교해야 한다.

래퍼 클래스 예시

public class WrapperClassMain {

    public static void main(String[] args) {
        // Integer newInteger = new Integer(10); //미래에 삭제 예정, 대신에 valueOf() 사용
        Integer integerObj = Integer.valueOf(10); //-128 ~ 127 자주 사용하는 숫자 값 재사용, 불변

        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

래퍼 클래스 생성 - 박싱(Boxing)

  • 기본형을 래퍼 클래스로 변경하는 것을 마치 박스에 물건을 넣은 것 같다고 해서 박싱(Boxing)이라 한다.
  • new Integer(10) 은 직접 사용하면 안된다. 작동은 하지만, 향후 자바에서 제거될 예정이다.대신에 Integer.valueOf(10) 를 사용하면 된다.
  • 추가로 Integer.valueOf() 에는 성능 최적화 기능이 있다.
    개발자들이 일반적으로 자주 사용하는 -128 ~127 범위의 Integer 클래스를 미리 생성해준다.
  • 해당 범위의 값을 조회하면 미리 생성된 Integer 객체를 반환한다.
    해당 범위의 값이 없으면 new Integer() 를 호출한다.
    • 마치 문자열 풀과 비슷하게 자주 사용하는 숫자를 미리 생성해두고 재사용한다.
    • 참고로 이런 최적화 방식은 미래에 더 나은 방식으로 변경될 수 있다.

intValue() - 언박싱(Unboxing)

  • 래퍼 클래스에 들어있는 기본형 값을 다시 꺼내는 메서드이다.
  • 박스에 들어있는 물건을 꺼내는 것 같다고 해서 언박싱(Unboxing)이라 한다.
    • 언박싱: xxxValue() (예: intValue() , doubleValue() )

비교는 equals() 사용

  • 래퍼 클래스는 객체이기 때문에 == 비교를 하면 인스턴스의 참조값을 비교한다.
  • 래퍼 클래스는 내부의 값을 비교하도록 equals() 를 재정의 해두었다. 따라서 값을 비교하려면 equals() 를 사용해야 한다.

참고로 래퍼 클래스는 객체를 그대로 출력해도 내부에 있는 값을 문자로 출력하도록 toString() 을 재정의했다.

래퍼 클래스 - 오토 박싱

오토 박싱 - Autoboxing

  • 자바에서 intInteger로 변환하거나, Integerint로 변환하는 부분을 정리해보자.
  • 다음과 같이 valueof(), intValue() 메서드를 사용하면 된다.

1) 박싱, 언박싱 예시

public class AutoboxingMain1 {
	public static void main(String[] args) {
		// Primitive -> Wrapper
		int value = 7;
		Integer boxedValue = Integer.valueOf(value);

		// Wrapper -> Primitive
		int unboxedValue = boxedValue.intValue();

		System.out.println("boxedValue = " + boxedValue);
		System.out.println("unboxedValue = " + unboxedValue);
	}
}

실행 결과
boxedValue = 7
unboxedValue = 7

  • 박싱: valueOf()
  • 언박싱: xxxValue() (예: intValue() , doubleValue() )
  • 개발자들이 오랜기간 개발을 하다 보니 기본형을 래퍼 클래스로 변환하거나 또는 래퍼 클래스를 기본형으로 변환하는 일이 자주 발생했다.
  • 자바는 이런 문제를 해결하기 위해 자바 1.5부터 오토 박싱( Auto-boxing ), 오토 언박싱( Auto-Unboxing )을 지원한다.

2) 오토 박싱, 오토 언박싱

public class AutoboxingMain2 {
    public static void main(String[] args) {
        // Primitive -> Wrapper
        int value = 7;
        Integer boxedValue = value; // 오토 박싱

        // Wrapper -> Primitive
        int unboxedValue = boxedValue;  // 오토 언박싱

        System.out.println("boxedValue = " + boxedValue);
        System.out.println("unboxedValue = " + unboxedValue);
    }
}

실행 결과

boxedValue = 7
unboxedValue = 7
  • 오토 박싱과 오토 언박싱은 컴파일러가 개발자 대신 valueOf, xxxValue() 등의 코드를 추가해주는 기능이다.
  • 덕분에 기본형과 래퍼형을 서로 편리하게 변환할 수 있다.
  • 따라서, AutoboxingMain1AutoboxingMain2는 동일하게 작동한다.

4. 래퍼 클래스 - 주요 메서드와 성능

래퍼 클래스가 제공하는 주요 메서드를 알아보자.( valueOf() , parseInt() 중요!)

public class WrapperUtilsMain {
    public static void main(String[] args) {
        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));
        System.out.println("min: " + Integer.min(10, 20));
        System.out.println("max: " + Integer.max(10, 20));
    }
}

실행 결과
compareResult = -1
sum: 30
min: 10
max: 20

  • valueOf() : 래퍼 타입을 반환한다. 숫자, 문자열을 모두 지원한다.
  • parseInt() : 문자열을 기본형으로 변환한다.
  • compareTo() : 내 값과 인수로 넘어온 값을 비교한다. 내 값이 크면 1 , 같으면 0 , 내 값이 작으면 -1 을 반환한다.
  • Integer.sum() , Integer.min() , Integer.max() : static 메서드이다. 간단한 덧셈, 작은 값, 큰값 연산을 수행한다.

parseInt( ) vs valueOf( )
원하는 타입에 맞는 메서드를 사용하면 된다.

  • valueOf("10") 는 래퍼 타입을 반환한다.
  • parseInt("10") 는 기본형을 반환한다.
    • Long.parseLong() 처럼 각 타입에 parseXxx() 가 존재한다.

래퍼 클래스와 성능
래퍼 클래스는 객체이기 때문에 기본형보다 다양한 기능을 제공한다. 그럼, 기본형, 래퍼 클래스 어떤 것을 사용해야 할까?

유지보수 vs 최적화
유지보수 vs 최적화를 고려해야 하는 상황이라면 유지보수하기 좋은 코드를 먼저 고민해야 한다. 특히 최신 컴퓨터는 매우 빠르기 때문에 메모리 상에서 발생하는 연산을 몇 번 줄인다고해도 실질적인 도움이 되지 않는 경우가 많다.

  • 특히 웹 애플리케이션의 경우 메모리 안에서 발생하는 연산 하나보다 네트워크 호출 한 번이 많게는 수십만배 더 오래 걸린다. 자바 메모리 내부에서 발생하는 연산을 수천번에서 한 번으로 줄이는 것 보다, 네트워크 호출 한 번을 더 줄이는 것이 더 효과적인 경우가 많다.
  • 권장하는 방법은 개발 이후에 성능 테스트를 해보고 정말 문제가 되는 부분을 찾아서 최적화 하는 것이다.

5. Class 클래스

자바에서 Class 클래스는 클래스의 정보(메타데이터)를 다루는데 사용된다. Class 클래스를 통해 개발자는 실행중인 자바 애플리케이션 내에서 필요한 클래스의 속성과 메서드에 대한 정보를 조회하고 조작할 수 있다.

Class 클래스의 주요 기능은 다음과 같다.

  • 타입 정보 얻기: 클래스의 이름, 슈퍼클래스, 인터페이스, 접근 제한자 등과 같은 정보를 조회할 수 있다.
  • 리플렉션: 클래스에 정의된 메서드, 필드, 생성자 등을 조회하고, 이들을 통해 객체 인스턴스를 생성하거나 메서드를 호출하는 등의 작업을 할 수 있다.
  • 동적 로딩과 생성: Class.forName() 메서드를 사용하여 클래스를 동적으로 로드하고, newInstance() 메서드를 통해 새로운 인스턴스를 생성할 수 있다.
  • 애노테이션 처리: 클래스에 적용된 애노테이션(annotation)을 조회하고 처리하는 기능을 제공한다.

예를 들어, String.classString 클래스에 대한 Class 객체를 나타내며, 이를 통해 String 클래스에 대한 메타데이터를 조회하거나 조작할 수 있다.

public class ClassMetaMain {
    public static void main(String[] args) throws Exception{
        //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());
        }
    }
}

class vs clazz - class는 자바의 예약어다. 따라서 패키지명, 변수명으로 사용할 수 없다.

  • 이런 이유로 자바 개발자들은 class 대신 clazz 라는 이름을 관행으로 사용한다.
  • clazzclass 와 유사하게 들리고, 이 단어가 class 를 의미한다는 것을 쉽게 알 수 있다.

실행 결과

Field: class [B value
...
Method: public boolean java.lang.String.equals(java.lang.Object)
Method: public int java.lang.String.length()
...
Superclass: java.lang.Object
Interface: java.io.Serializable
Interface: java.lang.Comparable
...

Class 클래스는 다음과 같이 3가지 방법으로 조회할 수 있다.

Class clazz = String.class; // 1.클래스에서 조회
Class clazz = new String().getClass();// 2.인스턴스에서 조회
Class clazz = Class.forName("java.lang.String");//3.문자열로 조회

Class 클래스의 주요 기능

  • getDeclaredFields(): 클래스의 모든 필드를 조회한다.
  • getDeclaredMethods(): 클래스의 모든 메서드를 조회한다.
  • getSuperclass(): 클래스의 부모 클래스를 조회한다.
  • getInterfaces(): 클래스의 인터페이스들을 조회한다.

클래스 생성하기

Class 클래스에는 클래스의 모든 정보가 들어있다. 이 정보를 기반으로 인스턴스를 생성하거나, 메서드를 호출하고, 필드의 값도 변경할 수 있다. 간단하게 인스턴스를 생성해보자.

public class Hello {
    public String hello() {
        return "hello!";
    }
}
public class ClassCreateMain {
    public static void main(String[] args) throws Exception {

        //Class helloClass = Hello.class;
        Class helloClass = Class.forName("lang.clazz.Hello");
        Hello hello = (Hello) helloClass.getDeclaredConstructor().newInstance();
        String result = hello.hello();
        System.out.println("result = " + result);
    }
}
result = hello!

getDeclaredConstructor().newInstance()

  • getDeclaredConstructor() : 생성자를 선택한다.
  • newInstance() : 선택된 생성자를 기반으로 인스턴스를 생성한다.
profile
안녕하세요. wony입니다.

3개의 댓글

comment-user-thumbnail
2024년 4월 26일

안녕하세요. 정리 감사히 잘 보고 있습니다!
혹시, Class 클래스와 System 클래스는 언제 정리해주실 예정이신가요??

1개의 답글