백준 문제 풀이 도중 primitive 타입 이외의 Reference 타입 정렬 부분을 구현해야 했다.
(Arrays.sort()의 인자로는 primitive 타입의 변수만 가능하다는 것을 알게 되어 글을 요약정리하게 되었다.)
primitive 타입이란 무엇인가?
자바에서는 데이터 타입이 크게 두 가지로 원시 타입(Primitive Type)과 참조타입(Reference Type)이 있다.
원시 타입은 우리가 너무나도 많이 사용하는 정수, 실수, 문자, 논리 리터럴등의 실제 데이터 값을 저장하는 타입이고,
참조 타입은 객체(Object)의 번지를 참조(주소를 저장)하는 타입으로 메모리 번지 값을 통해 객체를 참조하는 타입이다.
원시 타입(Primitive Type)
종류 | 데이터형 | 크기(byte / bit) | 표현 범위 |
논리형 | boolean | 1 / 8 | true 또는 false |
문자형 | char | 2 / 16 | '\u0000' ~ 'uFFFF' (16비트 유니코드 문자 데이터) |
정수형 | byte | 1 / 8 | -128 ~ 127 |
정수형 | short | 2 / 16 | -32768 ~ 32767 |
정수형 | int | 4 / 32 | -2147483648 ~ 2147483647( -21억 ~ + 21억) |
정수형 | long | 8 / 64 | -9223372036854775808 ~ 9223372036854775807(-100경 ~ + 100경) |
실수형 | float | 4 / 32 | 1.4E-45 ~ 3.4028235E38 |
실수형 | double | 8 / 64 | 4.9E-324 ~ 1.7976931348623157E308 |
참조 타입(Reference Type)
참조형은 기본 자료형을 기초로 하여 만들어진 자료형이다. 대표적으로 자바에서 제공하는 String, Array, Map, Set 등과 같은 클래스(Class)와 인터페이스(Interface), 열거형(Enum)이 여기에 해당한다. 추가적으로 필요에 따라 사용자가 참조형 타입을 정의할 수도 있다.
Java에서 실제 객체는 힙 영역에 저장되며 참조 타입 변수는 스택 영역에 실제 객체들의 주소를 저장하여, 객체를 사용할때 마다 참조 변수에 저장된 객체의 주소를 불러와 사용하는 방식이다.
스택 영역 | 힙 영역 |
int age = 24 char c = 'A' String address = 101번지 |
101번지 : "tistory" |
기본 타입 변수인 age와 c는 값을 직접 저장하지만,
참조 타입 변수(String 클래스)인 address는 힙 영역의 String 객체의 주소 값을 가진다.
- 정적 메모리 스택(Stack) 영역
스택 영역에는 기본타입 변수가 할당되고 변수의 실제 값들이 저장된다.
참조 타입의 변수들은 이 스택 영역에서 힙 영역에 생성된 객체들의 주소 값을 저장한다.
객체 안의 메소드의 작업이 종료되면 할당되었던 메모리 공간은 반환되어 비워진다.
- 동적 메모리 힙(Heap) 영역
힙 영역에는 객체와 배열이 생성된다.
그리고 참조타입(배열, 클래스, 인터페이스 등)들이 이 객체들의 주소를 스택 영역에 저장한다.
기본타입 변수들과는 다르게 크기가 정해져 있지 않다.
프로그램 실행시 메모리에 동적으로 할당된다.
참조하는 변수가 없으면 자바의 가비지 컬렉터가 제거한다.
가비지 컬렉터(Garbage collector) : 메모리의 힙 영역에 할당된 더 이상 사용되지 않는 객체를 제거 하는 역할
이렇게 객체를 제거하며 메모리가 관리된다.
참조 타입은 계속해서 객체를 강조하고 있다.
Object
모든 Class와 Enum은 Object 클래스를 상속한다. 다시말해 Object는 모든 Class와 Enum의 일반화된 타입이라는 것이다. 여기서 주의할 점은 Interface는 Object를 상속하지 않는다는 사실이다. 이와 관련된 내용은 자바 api 문서 Tree에 잘 드러나 있다.
Wrapper 클래스
Wrapper Class는 기본 자료형을 감싼 클래스이다. 대표적으로 Byte, Short, Integer, Long, Float, Double, Character, Boolean이 있다. 이것을 사용하는 이유는 앞서 설명한 String을 사용하는 이유와도 동일하다. 기본 자료형을 클래스로 랩핑하면 얻을 수 있는 이점은 유용한 메서드를 제공할 수 있다는 점이다.
하지만 이보다 더 중요한 이유는 제네릭(Generic)에 있다. 제네릭에 사용되는 매개변수 T는 Object 자료형만 받을 수 있다. 이것은 다시 말해 클래스로 정의된 객체만을 전달받는다는 것이다. 다만 코드를 짜다보면 제네릭을 기본 자료형에 적용해야 하는 경우가 있다. 이러한 경우에는 Wrapper Class을 이용하면 문제가 해소된다.
원시 타입과 참조 타입 차이
기능적으로 원시 타입과 참조 타입은 크게 2가지 차이가 있다.
1. Null 포함 가능 여부
원시타입은 null을 담을 수 없지만, 참조 타입은 가능하다.
// 불가능
int i = null;
// 가능
Integer integer = null;
2. 제너릭 타입에서 사용 가능 여부
원시타입은 제너릭 타입에서 사용할 수 없지만, 참조 타입은 가능하다.
// 불가능
List<i> list;
// 가능
List<Integer> list;
원시타입은 '스택' 메모리에 값이 존재한다.
반면에 참조타입은 하나의 인스턴스이기 때문에 '스택' 메모리에는 참조값만 있고, 실제 값은 힙 메모리에 존재한다.
그리고 값을 필요로 할 때마다 언박싱 과정을 거쳐야 하니 원시타입과 비교해서 접근 속도가 느려지게 된다.
예외적으로 엄청 큰 숫자를 복사해야 한다면, 참조값만 넘길 수 있는 참조타입이 좋을 수도 있다.
원시타입이 사용하는 메모리 | 참조타입이 사용하는 메모리 |
boolean - 1bit | Boolean – 128 bits |
byte - 8bits | Byte - 128bits |
short, cagr - 16bits | Short, Charater - 128bits |
int, float - 32bits | Integer, Float - 128bits |
long, double - 64bits | Long, Double - 196bits |
원시 타입보다 참조 타입이 사용하는 메모리양이 압도적으로 높다.
따라서 메모리 사용적으로도 원시 타입이 참조 타입보다 효율적으로 사용할 수 있다.
'JAVA' 카테고리의 다른 글
[JAVA] BigInteger 클래스 (0) | 2022.03.24 |
---|---|
[JAVA] Comparable과 Comparator (0) | 2022.03.13 |
[JAVA] StringBuffer, StringBuilder 클래스 (0) | 2022.01.24 |
[JAVA] StringTokenizer 클래스 (0) | 2022.01.20 |
[JAVA] Scanner와 BufferedReader/BufferedWriter (0) | 2022.01.19 |
댓글