Java 연산자(Operator) 완전 정리
Java의 산술·비교·논리·비트·대입·삼항 연산자 전체를 우선순위와 함께 정리하고, 단락 평가·정수 오버플로·패턴 매칭 instanceof 등 실무에서 자주 마주치는 함정까지 예제 중심으로 완전히 해설한다
지난 글에서 타입 변환을 살펴봤다. 이번에는 Java 코드 어디서나 등장하는 연산자(Operator)를 카테고리별로 체계적으로 정리한다. 연산자는 단순히 기호를 외우는 것을 넘어, 우선순위와 평가 순서를 이해해야 실제 코드에서 예상치 못한 버그를 피할 수 있다.
산술 연산자
가장 기본적인 계산을 담당한다. +, -, *, /, % 다섯 가지와 증감 연산자 ++, --로 이루어진다.
int a = 17, b = 5;
System.out.println(a + b); // 22
System.out.println(a - b); // 12
System.out.println(a * b); // 85
System.out.println(a / b); // 3 (정수 나눗셈: 소수점 버림)
System.out.println(a % b); // 2 (나머지)
// 전위(pre) vs 후위(post) 증감
int x = 10;
System.out.println(++x); // 11 (먼저 증가, 그 값 반환)
System.out.println(x++); // 11 (반환 후 증가)
System.out.println(x); // 12
정수끼리 나눌 때 /는 소수점을 버린다. 실수 나눗셈이 필요하면 한쪽을 double로 캐스팅해야 한다.
double result = (double) 17 / 5; // 3.4
비교 연산자
두 값을 비교해 boolean을 돌려준다. ==, !=, >, <, >=, <= 여섯 가지다.
int score = 85;
System.out.println(score >= 80); // true
System.out.println(score == 100); // false
String s1 = "hello";
String s2 = new String("hello");
System.out.println(s1 == s2); // false (참조 비교)
System.out.println(s1.equals(s2)); // true (값 비교)
==는 기본형에서는 값을, 참조형에서는 메모리 주소를 비교한다. 문자열이나 객체의 내용을 비교할 때는 반드시 equals()를 써야 한다.
논리 연산자와 단락 평가
&&, ||, !, ^가 있으며, &&와 ||는 단락 평가(short-circuit evaluation)를 수행한다.
int divisor = 0;
// divisor != 0 이 false → 오른쪽(10/divisor)은 평가하지 않음 → ArithmeticException 방지
boolean safe = divisor != 0 && 10 / divisor > 2;
// true || ... → 오른쪽은 평가되지 않음
boolean flag = true || someExpensiveMethod();
&와 |는 단락 평가 없이 양쪽을 모두 평가하는 비단락 연산자다. 사이드 이펙트가 반드시 필요한 경우가 아니면 &&, ||를 쓰는 것이 일반적이다.
비트 연산자와 시프트
비트 수준 조작이 필요할 때 사용한다. 플래그 관리, 해시 계산, 저수준 알고리즘 등에서 등장한다.
int flags = 0b0000_1010; // 비트 2, 3이 켜진 상태
// 비트 OR로 플래그 추가
flags |= 0b0000_0001; // 0b0000_1011
// 비트 AND로 특정 비트 확인
boolean bit1Set = (flags & 0b0000_0010) != 0; // true
// 시프트
int n = 8;
System.out.println(n << 1); // 16 (×2)
System.out.println(n >> 1); // 4 (÷2, 부호 유지)
System.out.println(-8 >>> 1); // 양수 (부호 비트 포함해 0으로 채움)
>> 는 산술 우측 시프트로 부호 비트를 복사하고, >>> 는 논리 우측 시프트로 최상위 비트를 항상 0으로 채운다.
대입 연산자
= 기본 대입과 복합 대입(+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=)이 있다.
int val = 10;
val += 5; // val = val + 5 → 15
val *= 2; // val = val * 2 → 30
val >>= 1; // val = val >> 1 → 15
복합 대입 연산자는 내부적으로 묵시적 캐스팅이 포함되어 있어 주의가 필요하다.
byte b = 10;
b += 5; // OK: (byte)(b + 5) — 컴파일러가 자동 처리
// b = b + 5; // 컴파일 에러: int를 byte에 직접 대입 불가
삼항 연산자
condition ? valueIfTrue : valueIfFalse 형태로 간결한 조건부 값 선택에 쓴다.
int temp = 36;
String status = temp >= 37.5 ? "발열" : "정상";
// 중첩 삼항 (가독성을 위해 switch expression 권장)
int score = 82;
String grade = score >= 90 ? "A"
: score >= 80 ? "B"
: score >= 70 ? "C"
: "F";
// grade = "B"
instanceof 와 패턴 매칭
instanceof는 객체의 런타임 타입을 검사한다. Java 16부터 패턴 매칭 instanceof가 정식 도입돼 캐스팅 코드를 제거할 수 있다.
Object obj = "Hello, Java";
// 전통적인 방식
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// 패턴 매칭 (Java 16+)
if (obj instanceof String s) {
System.out.println(s.length()); // 캐스팅 불필요
}
연산자 우선순위
우선순위가 헷갈릴 때는 괄호 ()를 명시적으로 사용하는 것이 가장 안전하다.
| 높음 | () [] . | 후위 ++ -- | 전위 ++ -- ! ~ |
|---|---|---|---|
| ↓ | 산술 * / % | 산술 + - | 시프트 << >> >>> |
| ↓ | 비교 < > <= >= instanceof | 동등 == != | 비트 & ^ | |
| 낮음 | 논리 && || | 삼항 ?: | 대입 = += … |
오버플로와 부동소수점 주의
int max = Integer.MAX_VALUE; // 2_147_483_647
System.out.println(max + 1); // -2_147_483_648 (오버플로 — 예외 없음!)
// 큰 수 연산은 long 또는 BigInteger 사용
long safe = (long) max + 1; // 2_147_483_648
// 부동소수점 정밀도 문제
System.out.println(0.1 + 0.2); // 0.30000000000000004
// 정확한 금융 계산은 BigDecimal 사용
Java의 정수 연산은 오버플로가 발생해도 예외를 던지지 않고 조용히 래핑(wrap-around)된다. 큰 수를 다룰 때는 long이나 BigInteger를 선택해야 한다.
정리
Java 연산자의 핵심은 세 가지다. 첫째, &&/||의 단락 평가를 활용하면 null 검사나 나눗셈 가드를 간결하게 쓸 수 있다. 둘째, ==는 참조형에서 참조를 비교하므로 내용 비교엔 equals()를 쓴다. 셋째, 정수 오버플로는 예외 없이 발생하므로 범위를 의식해야 한다. 우선순위가 불분명하면 괄호로 명확히 하는 습관이 버그를 미연에 방지한다.
지난 글: Java 타입 변환(Type Conversion) 완전 정리
다음 글: Java 제어 흐름(Control Flow) 완전 정리
읽어주셔서 감사합니다. 😊