- Java의 자료형
자바의 자료형에는 원시 타입(Primitive type), 참조 타입(Reference type) 두가지 타입이 있다.
1. 원시타입 (Primitive type)
Primitive type은
stack 메모리 공간에 값 자체가 저장되며
boolean, int, long, char 등이 있다.
2. 참조타입 (Reference type)
Reference type은
heap 메모리 공간에 저장되며 해당 객체들의 주소를 stack 메모리 공간에 저장한다.
기본형 이외 대부분의 타입들을 말하며, 배열, 클래스 인터페이스 등이 있다.
int a = 1; // stack 에 저장
Integer b = new Integer(); // heap에 저장하고 주소값을 참조
3. 래퍼 클래스 (Wrapper class)
그럼 Wrapper class는 무엇일까
ArrayList<T>, HashMap<T>, HashSet<T> 등에서 사용하는 generic 클래스는 primitive type을 지원하지 않는다
때문에 그에 대응하는 Wrapper class를 사용하여 값 대신 객체를 주고받을 수 있도록 한다.
Primitive type | Wrapper class |
byte | Byte |
boolean | Boolean |
char | Character |
double | Double |
float | Float |
int | Integer |
long | Long |
short | Short |
wrapper class의 특징으로는 == 연산을 할 수 없다는 점이 있다.
primitive type에서는 값을 비교하지만
wrapper class는 참조된 주소를 비교하여 같은 객체인지를 확인한다.
Integer i1 = new Integer(77);
Integer i2 = new Integer(77);
System.out.println(i1 == i2); // false;
// 주소값 출력
System.out.println(System.identityHashCode(i1)); // 724542711
System.out.println(System.identityHashCode(i2)); // 498931366
// 값 자체를 비교하면 true를 얻을 수 있다.
System.out.println(Integer.valueOf(i1) == Integer.valueOf(i2)); // true;
System.out.println(i1.equals(i2)); // true
또한 새로운 객체를 새로 생성하는 것이기 때문에 wrapper class는 불변(Immutable) 객체이다.
4. String
그렇다면 궁금한게 String은 바로 값을 대입하니 primitive type일까?
답은 아니오 다.
String은 java에서 리터럴 표현식(literal expression)을 사용할 수 있도록 특별대우 해주는 자료형이다.
Java Heap memory 에는 String pool 이라는 영역을 가지고 있다.
리터럴 표현식으로 String을 선언하게 되면
String 내부에서 intern() 메소드를 호출하게 되고
intern() 메소드는 heap 내에 있는 String pool에서 해당 값이 있는지 먼저 확인하게 되고,
있다면 해당 주소값을 리턴해주고, 없다면 pool 안에 새로 생성하여 주소값을 리턴해준다.
Wrapper class를 설명 할 때 == 연산을 할 수 없다고 했었다.
하지만 String이 리터럴 표현식으로 생성된다면
String pool 덕분에 wrapper class 임에도 불구하고 == 연산을 수행할 수 있는 것이다.
String이 new 연산자로 생성된다면
마찬가지로 == 연산자가 아닌 equals처럼 값을 비교하여야 한다.
5. Primitive type vs Wrapper class
그래서 왜 두가지 중 무엇을 쓸것인가
primitive type은 Stack Memory에 저장되며 접근이 매우 빠르지만,
Wrapper class는 Heap Memory에 저장되기 때문에 오버헤드로 인해 상대적으로 접근 속도가 느리다.
byte | 8 bits | Byte | 128 bits |
boolean | 1 bit (8 bits) | Boolean | 128 bits |
char | 16 bits | Character | 128 bits |
double | 64 bits | Double | 192 bits |
float | 32 bits | Float | 128 bits |
int | 32 bits | Integer | 128 bits |
long | 64 bits | Long | 192 bits |
short | 16 bits | Short | 128 bits |
이렇게만 보면 primitive type만을 사용해야 할 것 같지만
wrapper class를 설명할 때 언급했던것 처럼
1. 코드를 객체 중심적으로 작성하는데 유리하다.
ArrayList<T>, HashMap<T>, HashSet<T> 등에서 사용하는 generic 클래스는 primitive type을 지원하지 않는다
때문에 그에 대응하는 Wrapper class를 사용하여 값 대신 객체를 주고받을 수 있도록 한다.
2. 또한 wrapper class 는 null 값을 받을 수 있다.
primitive type에는 null 값이 허용되지 않는 반면 wrapper class는 가능하다.
null 값을 한번 더 체크 해줘야 하는 번거로움이 있을 수 있지만
어쩔수 없이 null값을 다뤄야 할때 유용하다.
고로 어느 하나가 낫다 라기보단 둘을 잘 혼용해서 쓰면 될 것 같다.
어쩌다가 여기까지 오게 된건가
comehear 프로젝트 당시 무분별하게 primitive type과 wrapper class를 사용하다가
한번 정리가 필요하다고 생각했었는데
이제서야 정리하게 되었다.
** 참조
https://en.wikipedia.org/wiki/Primitive_wrapper_class_in_Java#cite_note-1
https://www.upgrad.com/blog/wrapper-classes-in-java/
https://harshitjain.home.blog/2019/05/20/java-string-intern/
'개발자의 삶 > Java' 카테고리의 다른 글
[Java] 메소드 참조(Method References)에 대하여 ( 이중콜론 :: ) (0) | 2023.06.05 |
---|---|
[Java] 람다식(Lambda Expression)과 스트림(Stream)에 대하여 - (2) (1) | 2023.06.01 |
[Java] 람다식(Lambda Expression)과 스트림(Stream)에 대하여 - (1) (0) | 2023.05.31 |
[Java] 오토박싱(Autoboxing), 언박싱(unboxing) (1) | 2023.05.27 |
[JAVA] 람다식 (map, filter, reduce, collect) (0) | 2021.05.04 |