JAVA의 자료형
서버와의 통신을 위해서 프로그램을 만들 일이 있어서 이것저것 기초작업을 시작해보다가 의문이 있어서 자료형에 대해 찾아보았다.
C++는 char, short, int, int64 등의 자료형과 여기에 대한 unsigned형들, C#에서는 byte, int16, int32, int64 등의 자료형들을 사용해왔다. 이클립스에서 코드를 작성하다 보니 unsigned형에 대한 코드 힌트가 나오지 않았다.
2026년 수정 안내 원래 이 글에는 “모든 자료형이 부호를 가지기 때문에 unsigned가 따로 없다”고 적었는데 틀렸다.
char가 바로 부호 없는 타입이다. 바로 앞 줄에 “char는 2바이트”라고 써놓고 반례를 놓쳤다. 정확한 내용으로 정리한다.
Java의 기본 자료형
| 타입 | 크기 | 범위 | 부호 |
|---|---|---|---|
byte |
1바이트 | -128 ~ 127 | 있음 |
short |
2바이트 | -32,768 ~ 32,767 | 있음 |
int |
4바이트 | -2^31 ~ 2^31-1 | 있음 |
long |
8바이트 | -2^63 ~ 2^63-1 | 있음 |
char |
2바이트 | 0 ~ 65,535 | 없음 |
float |
4바이트 | IEEE 754 | - |
double |
8바이트 | IEEE 754 | - |
boolean |
명시 안 됨 | true / false | - |
char가 유일한 예외다
char는 문자를 담기 위한 타입이라 음수가 필요 없다. 그래서 0부터 65535까지의 부호 없는 16비트 정수다. Java에서 유일한 unsigned 타입이다.
크기가 2바이트인 이유는 Java가 문자열을 UTF-16으로 다루기 때문이다. 정확히 말하면 char 하나는 문자 하나가 아니라 UTF-16 코드 유닛 하나다. 이모지나 일부 한자처럼 BMP를 벗어나는 문자는 char 두 개(서로게이트 페어)로 표현된다.
String emoji = "😀";
System.out.println(emoji.length()); // 2 ← char 개수
System.out.println(emoji.codePointCount(0, 2)); // 1 ← 실제 문자 개수
unsigned가 필요할 때
서버와 바이너리 프로토콜로 통신할 때 이게 문제가 된다. C++ 서버가 unsigned char로 0~255를 보내는데 Java의 byte는 -128~127이라, 128 이상의 값이 음수로 읽힌다.
해결은 더 큰 타입으로 받으면서 비트 마스크를 씌우는 것이다.
byte b = -1; // C++에서 보낸 unsigned char 255
int unsigned = b & 0xFF; // 255
short s = -1; // unsigned short 65535
int unsigned = s & 0xFFFF; // 65535
int i = -1; // unsigned int 4294967295
long unsigned = i & 0xFFFFFFFFL; // 4294967295
long을 unsigned로 다뤄야 한다면 담을 더 큰 기본 타입이 없으므로 BigInteger를 쓰거나, 아래 헬퍼 메서드를 쓴다.
Java 8부터 unsigned 헬퍼가 추가되었다
Java 8(2014)부터는 부호 없는 연산을 위한 정적 메서드들이 표준에 들어왔다. 값 자체는 여전히 부호 있는 타입에 담기지만, 연산과 출력을 부호 없는 것처럼 처리해준다.
int x = -1;
Integer.toUnsignedString(x); // "4294967295"
Integer.toUnsignedLong(x); // 4294967295L
Integer.divideUnsigned(x, 2); // 2147483647
Integer.compareUnsigned(x, 1); // 양수 (x가 더 큼)
Byte.toUnsignedInt((byte) -1); // 255
Short.toUnsignedInt((short) -1); // 65535
Long.toUnsignedString(-1L); // "18446744073709551615"
바이너리 프로토콜을 다룬다면 ByteBuffer도 함께 알아두면 좋다. 엔디안 지정이 가능하다.
ByteBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
int value = buf.getShort() & 0xFFFF; // unsigned short로 읽기
댓글 남기기