비트 연산과 플래그
비트 연산은 정수의 이진 표현을 직접 조작합니다.
일상적인 업무 코드에서는 자주 쓰이지 않지만 권한 플래그, 프로토콜 필드, 색상 값처럼 여러 상태를 한 정수에 담을 때 유용합니다.
부호 있는 정수에서는 오른쪽 시프트가 부호 비트를 채우는지 0을 채우는지 구분해야 합니다.
비트 위치의 의미를 이름 없이 숫자로만 쓰면 유지보수가 어렵습니다.
이 절에서는 마스크를 만들고 검사하고 제거하는 흐름을 중심으로 봅니다.
AND는 필요한 비트만 검사한다
값과 마스크를 &로 계산하면 마스크가 켜진 위치만 남습니다.
결과가 0이 아닌지 확인해 특정 권한이 포함됐는지 판단할 수 있습니다.
OR와 XOR는 상태를 켜고 뒤집는다
|는 지정한 비트를 켜고 ^는 서로 다른 비트를 1로 만듭니다.
토글이 목적이 아니라면 XOR보다 명시적인 설정과 해제가 읽기 쉽습니다.
PermissionBits.java는 읽기·쓰기 권한을 서로 다른 비트에 배치하고, 검사·추가·제거를 차례로 수행합니다. 각 단계의 이진 표현을 함께 보면 마스크가 어느 자리만 바꾸는지 추적할 수 있습니다.
public class PermissionBits {
static final int READ = 1 << 0;
static final int WRITE = 1 << 1;
static final int DELETE = 1 << 2;
public static void main(String[] args) {
int permissions = READ | WRITE;
System.out.println("read=" + has(permissions, READ));
System.out.println("delete=" + has(permissions, DELETE));
permissions |= DELETE;
permissions &= ~WRITE;
System.out.println("bits=" + Integer.toBinaryString(permissions));
}
static boolean has(int value, int mask) {
return (value & mask) == mask;
}
}권한 값에 READ를 OR 하면 해당 비트만 1로 바뀌고, XOR 하면 현재 값과 반대로 뒤집힙니다. “허용한다”처럼 결과 상태를 정하는 작업에는 OR를, “토글한다”처럼 기존 상태를 반전하는 작업에만 XOR를 사용합니다.
read=true
delete=false
bits=101최종 101은 READ와 DELETE만 켜져 있음을 보여 줍니다.
보수 연산으로 플래그를 제거한다
마스크를 ~로 뒤집은 값과 AND 하면 특정 비트를 끌 수 있습니다.
정수의 전체 폭이 함께 뒤집힌다는 점을 기억해야 합니다.
플래그를 끌 때 flags & ~WRITE를 쓰는 이유는 WRITE 자리만 0이고 나머지는 1인 마스크를 만들기 위해서입니다. 빼기 연산은 대상 비트가 이미 꺼져 있거나 플래그 값이 겹칠 때 의도를 보장하지 못합니다.
int packed = 0xFF00_0000;
int arithmetic = packed >> 24; // -1, 부호 비트 유지
int logical = packed >>> 24; // 255, 0으로 채움int toggled = READ ^ WRITE; // 서로 다른 비트를 켬
// 숫자 1, 2, 4 대신 이름 붙은 마스크를 사용한다.시프트는 부호 정책을 포함한다
>>는 부호를 유지하는 산술 시프트이고 >>>는 왼쪽을 0으로 채우는 논리 시프트입니다.
바이트 필드를 추출할 때는 >>>가 더 분명한 경우가 많습니다.
음수 -8에 >> 1과 >>> 1을 각각 적용해 이진 문자열을 비교해 보세요. 전자는 부호 비트를 채우고 후자는 0을 채우므로, 산술 나눗셈인지 부호 없는 비트 열 처리인지에 따라 연산자를 골라야 합니다.
비트 플래그는 각 위치에 이름을 붙이고 설정·검사·해제 연산을 메서드로 감쌀 때 안전하게 사용할 수 있습니다.