JAVA의 자료형에는 흔히 기본형 또는 원시타입 이라고 부르는Primitive type
과 참조형 또는 래퍼클래스 라고 부르는 Reference Type
두 종류가 있습니다. 이 포스트에서는 Primitive Type에 대해서 알아보겠습니다.
Primitive Type의 테이터는 변수에 할당 될 때 아래표에 나와있는것과 같이 고정된 크기로 저장되고, 해당 변수가 데이터 값을 보관합니다.
# primitive type
타입 | 크기 | 범위 | 최소값 | 최대값 |
---|---|---|---|---|
byte | 8 bits | -2^7 ~ 2^7-1 | -128 | 127 |
boolean | 1 bit | true, false | - | - |
short | 16 bits | -2^15 ~ 2^15-1 | -32,768 | +32,767 |
int | 32 bits | -2^31 ~ 2^31-1 | -2,147,483,648 | +2,147,483,647 |
long | 64 bits | -2^63 ~ 2^63-1 | -9,223,372,036,854,775,808 | +9,223,372,036,854,775,807 to |
float | 32 bits | 2^-149 ~ (2-2^-23)*2^127 | 1.4 E-45 | 3.402,823,5 E+38 |
double | 64 bits | 2^-1074 ~ (2-2^-52)*2^1023 | 4.9 E-324 | 1.797,693,134,862,315,7 E+308 |
char | 16 bits | 0 ~ 2^16-1 (모든 Unicode 캐릭터) | 0 | 65,535 |
자바에서는 위에 나와있는 8가지 + void
를 포함하여 총 9가지를 primitive type으로 정의 합니다.
char
타입을 제외하고는 컴파일러에서 해당 값을 상위타입으로 자동으로 변환 (Auto casting) 할 수 있습니다. 예를 들어 위의 표에 나와있는 int
형 변수를 하나 만들고 가질수 있는 최대값인 2,147,483,647을 할당해 봅시다.
1 | int intValue = 2147483647; //Integer.MAX_VALUE 와 동일 |
이후에 intValue 에다가 +1을 해버리면 어떻게 될까요? int
형이 가질수 있는 최대범위를 넘어가기 때문에 에러가 날것 같은데 한번 테스트 해 보겠습니다.
1 |
|
intValue++; 하는 순간에 오류가 날 것으로 예상했지만 오류는 나지 않았습니다.
에러가 나지 않았으니
2147483647에 1을 더한 값인 2147483648과 intValue++ 한 값이 같을것이라고 assertion을 줘 봤는데요 다음과 같은 컴파일 에러가 납니다.
“The literal 2147483648 of type int is out of range”
컴파일러는 2147483648이 int의 범위 밖이라는것을 알고 있는데 어째서 intValue++는 허용을 해 줬을까요? 어떤값이 나오는지 출력해서 확인 해 봤습니다.
1 | //assertEquals(2147483648, intValue); |
1 | -2147483648 |
음수값이 나온것으로 확인 됐습니다. 1씩 더 더하면 어떻게 되는지 확인해 보겠습니다.
1 | intValue++; |
1 | -2147483648 |
이런 결과를 얻게 된 이유는 short, int, long 타입의 2진수 값 저장소는 메모리에서 2의 보수 값을 사용하는데서 비롯 됩니다.
학교에서 다 배워서 알고있는 10진수의 2진수 변환을 해보면
- 0은 00000000
- 1은 00000001
- 2는 00000010
… 이런식으로 변환되고
- -1은 11111111
- -2는 11111110
- -128은 10000000
이런식으로 변환이 됩니다.
그럼 우리가 궁금해 하는 Integer.MAX_VALUE 의 2진수 값은 무엇인지 확인해 보겠습니다.
1 |
|
1 | 1111111111111111111111111111111 |
※ 참고로 Integer.toBinaryString()
함수는 int형의 10진수 숫자를 2진수로 변환한 값을 스트링으로 리턴해 주는 함수 입니다.
그 결과가 양수인 경우 부호비트를 제외한 2^31 만큼만 보여주고 음수인경우 2^32 를 다 보여줍니다.
그래서 int형의 최대값은 1이 31개 있는것을 확인 할 수 있습니다.
여기에 1씩 더한값을 또 이진수로 변환해보면 아래와같이 되겠죠
1 | 1111111111111111111111111111111 |
2의 보수로 저장하고 있기 때문에 1씩 더해서 실제로 2^0 자리가 1로 바뀌었지만 0은 1로 1은 0으로 바꾼 이후 +1을 해서 원래값을 만들어야 하므로 +2씩 한것같은 효과를 봤던 것 입니다.
잠깐 딴길로 샜는데 다시 본론으로 돌아와서 AutoCasting에 대해 살펴보겠습니다.
1 |
|
int
형인 intValue를 Integer 범위의 최대값으로 잡고, long
형인 longValue에 intValue+1을 한 값을 저장 했습니다.int
형을 그냥 +1 했을때는 음수값이 나오면서 위의 테스트가 실패했었는데 지금은 성공합니다.
이와 같이 상위 개념의 타입으로는 값의 정확도를 보장하면서 자동으로 변환을 시켜 주는데 이를 AutoCasting
이라고 합니다.
# 퀴즈
int value의 절대값을 구하는 함수를 만드시오.
1 | public class PrimitiveType { |
Math.abs()
라는 훌륭한 함수가 있기 때문에 그냥 return 해주면 간단히 해결 됩니다.
But!
맨처음 표를 보실 때 int의 범위가 -2^31 ~ 2^31-1 였다는걸 기억하신다면 음수가 양수보다 표현할수 있는 숫자가 한개 더 많다는 사실을 눈치 채셨는지 모르겠습니다.
1 |
|
1 | -2147483648 |
int형으로 표현할 수 있는 최소값은 절대값을 씌웠는데도 그대로 음수 부호를 달고 있습니다?!!
2의 보수표현을 할때 0은 양수로 취급하기 때문에 음수를 표현할 수 있는 값이 양수 보다 한개 더 많은 것 입니다.
1 | //1. 아예 long으로 변환해서 값을 리턴해 주는 경우 |
위와같이 두가지 경우로 생각해 볼 수 있습니다.
아예 그냥 long으로 변환해서 던져주거나 (물론 이경우에는 호출하는쪽에서 long으로 받아야 하므로 사실상 int의 최소값 이하를 사용하는 경우가 종종 있는 경우에 해당될 것 입니다.)
혹시라도 int의 최소값이 들어오는 경우에는 아예 익셉션을 리턴해 버려서 문제가 있음을 인지시켜 줄 수도 있겠습니다.
익셉션이 안나오고 그냥 절대값을 마이너스로 던져버린 경우 실제 저 코드가 프로그램의 일부로 돌아간다고 했을 때 에러로그도 안찍히고 문제없이 돌아가는것 처럼 보이지만 틀린값이 나오는 끔찍한 프로그램이 될 수 있습니다.