Ch 3.1 연산자(Operator) 기초
프로그램에서 데이터를 가공하고 계산하기 위해 사용되는 기호를 연산자(Operator) 라고 합니다. 자바는 수학적 계산, 논리적 판단 등을 위한 다양한 연산자를 제공합니다. 이 챕터에서는 연산자의 개념, 분류, 우선순위, 결합 방향을 체계적으로 정리합니다.
1. 연산자와 피연산자
- 연산자(Operator): 연산을 수행하는 기호 (예:
+,-,*,/,=) - 피연산자(Operand): 연산자의 작업 대상 (변수, 상수, 리터럴, 수식)
int result = x + 3;
// 연산자: = , +
// 피연산자: result, x, 3
연산자는 피연산자로 연산을 수행한 후 항상 결과값(Result) 을 반환합니다. 그 결과값은 변수에 저장하거나 다른 연산의 피연산자로 사용할 수 있습니다.
public class OperatorBasic {
public static void main(String[] args) {
int x = 10;
int y = 3;
// + 연산자: x와 y를 더한 결과(13)를 sum에 저장
int sum = x + y;
// * 연산자: sum과 2를 곱한 결과(26)를 product에 저장
int product = sum * 2;
// == 연산자: x와 10이 같은지 비교한 결과(true)를 isEqual에 저장
boolean isEqual = (x == 10);
System.out.println("sum = " + sum); // 13
System.out.println("product = " + product); // 26
System.out.println("isEqual = " + isEqual); // true
}
}
2. 연산자의 분류
자바의 연산자는 피연산자의 개수 에 따라 세 가지로 분류할 수 있습니다.
| 분류 | 설명 | 예시 |
|---|---|---|
| 단항 연산자 (Unary) | 피연산자가 1개 | ++i, --i, -x, !flag |
| 이항 연산자 (Binary) | 피연산자가 2개 | a + b, x > y, a && b |
| 삼항 연산자 (Ternary) | 피연산자가 3개 | 조건 ? 참값 : 거짓값 |
또한 기능 에 따라 다음과 같이 분류할 수 있습니다.
| 종류 | 연산자 | 설명 |
|---|---|---|
| 산술 연산자 | +, -, *, /, % | 사칙연산 및 나머지 |
| 비교 연산자 | ==, !=, >, <, >=, <= | 크기 및 동등 비교, 결과는 boolean |
| 논리 연산자 | &&, ||, !, ^ | 논리 AND, OR, NOT, XOR |
| 대입 연산자 | =, +=, -=, *=, /=, %= | 우변 값을 좌변 변수에 저장 |
| 비트 연산자 | &, |, ^, ~, <<, >>, >>> | 이진수 비트 단위 연산 |
| 조건 연산자 | ? : | 삼항 연산자, 조건에 따라 값 선택 |
| 기타 | instanceof, ->, :: | 타입 확인, 람다, 메서드 참조 |
3. 연산자 우선순위(Precedence)
식에 여러 연산자가 섞여 있을 때, 어느 연산자를 먼저 처리할지 결정 하는 규칙입니다. 수학의 사칙연산 우선순위(*가 +보다 먼저)와 유사합니다.
아래 표는 우선순위가 높은 것부터 낮은 순 으로 정리한 것입니다.
| 우선순위 | 연산자 | 결합 방향 | 설명 |
|---|---|---|---|
| 1 (최고) | (), [], . | 좌 → 우 | 괄호, 배열 접근, 멤버 접근 |
| 2 | ++, -- (후위) | 좌 → 우 | 후위 증감 |
| 3 | ++, -- (전위), +, -, ~, ! | 우 → 좌 | 전위 증감, 단항 부호, 비트 NOT, 논리 NOT |
| 4 | *, /, % | 좌 → 우 | 곱셈, 나눗셈, 나머지 |
| 5 | +, - | 좌 → 우 | 덧셈, 뺄셈 |
| 6 | <<, >>, >>> | 좌 → 우 | 비트 시프트 |
| 7 | <, >, <=, >=, instanceof | 좌 → 우 | 대소 비교, 타입 확인 |
| 8 | ==, != | 좌 → 우 | 동등 비교 |
| 9 | & | 좌 → 우 | 비트 AND |
| 10 | ^ | 좌 → 우 | 비트 XOR |
| 11 | | | 좌 → 우 | 비트 OR |
| 12 | && | 좌 → 우 | 논리 AND |
| 13 | || | 좌 → 우 | 논리 OR |
| 14 | ? : | 우 → 좌 | 삼항(조건) 연산자 |
| 15 (최저) | =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, >>>= | 우 → 좌 | 대입 연산자 |
public class Precedence {
public static void main(String[] args) {
// * 가 + 보다 우선순위가 높으므로 먼저 계산됨
int result1 = 2 + 3 * 4; // 2 + 12 = 14
System.out.println(result1); // 14
// 괄호로 우선순위를 직접 지정
int result2 = (2 + 3) * 4; // 5 * 4 = 20
System.out.println(result2); // 20
// 비교 연산자가 논리 연산자보다 우선순위가 높음
boolean check = 5 > 3 && 10 < 20; // (5>3) && (10<20) = true && true = true
System.out.println(check); // true
}
}
우선순위 표를 외우려 하지 않아도 됩니다. 복잡한 식에서는 괄호 ()를 사용해 우선순위를 직접 명시 하는 것이 가독성도 높이고 버그도 줄이는 좋은 습관입니다.
4. 결합 방향(Associativity)
우선순위가 동일한 연산자 가 여러 개 나올 때, 왼쪽부터 처리할지 오른쪽부터 처리할지를 결정하는 규칙입니다.
좌결합(Left-to-Right)
대부분의 이항 연산자는 왼쪽에서 오른쪽으로 처리됩니다.
// 좌결합 예시: - 연산자
int a = 10 - 3 - 2;
// (10 - 3) - 2 = 7 - 2 = 5 (왼쪽부터)
System.out.println(a); // 5
// 좌결합 예시: / 연산자
int b = 24 / 4 / 2;
// (24 / 4) / 2 = 6 / 2 = 3 (왼쪽부터)
System.out.println(b); // 3
우결합(Right-to-Left)
단항 연산자, 대입 연산자, 삼항 연산자는 오른쪽에서 왼쪽으로 처리됩니다.
// 우결합 예시: 대입 연산자 =
int x, y, z;
x = y = z = 10;
// z = 10 → y = 10 → x = 10 (오른쪽부터)
System.out.println(x + ", " + y + ", " + z); // 10, 10, 10
// 우결합 예시: 전위 단항 연산자
int n = 5;
int result = -(-n); // -(-(5)) = -(-5) = 5
System.out.println(result); // 5
5. 대입 연산자와 복합 대입 연산자
단순 대입 연산자 (=)
우변(오른쪽)의 값을 좌변(왼쪽) 변수에 저장합니다. 자바에서 가장 빈번하게 사용되는 연산자입니다.
int score = 100; // 100을 score에 저장
String name = "Alice"; // "Alice"를 name에 저장
boolean pass = true; // true를 pass에 저장
복합 대입 연산자 (Compound Assignment)
산술 연산 + 대입을 한 번에 처리하는 축약 표현입니다.
| 복합 대입 | 풀어쓴 표현 | 설명 |
|---|---|---|
a += b | a = a + b | a에 b를 더한 값을 a에 저장 |
a -= b | a = a - b | a에서 b를 뺀 값을 a에 저장 |
a *= b | a = a * b | a와 b를 곱한 값을 a에 저장 |
a /= b | a = a / b | a를 b로 나눈 값을 a에 저장 |
a %= b | a = a % b | a를 b로 나눈 나머지를 a에 저장 |
a &= b | a = a & b | 비트 AND 후 a에 저장 |
a |= b | a = a | b | 비트 OR 후 a에 저장 |
a ^= b | a = a ^ b | 비트 XOR 후 a에 저장 |
a <<= b | a = a << b | 왼쪽 시프트 후 a에 저장 |
a >>= b | a = a >> b | 오른쪽 시프트 후 a에 저장 |
public class CompoundAssignment {
public static void main(String[] args) {
int score = 80;
score += 10; // score = score + 10 = 90
System.out.println("+=: " + score); // 90
score -= 5; // score = score - 5 = 85
System.out.println("-=: " + score); // 85
score *= 2; // score = score * 2 = 170
System.out.println("*=: " + score); // 170
score /= 4; // score = score / 4 = 42 (정수 나눗셈, 소수점 버림)
System.out.println("/=: " + score); // 42
score %= 10; // score = score % 10 = 2
System.out.println("%=: " + score); // 2
}
}
복합 대입 연산자는 내부적으로 자동 형변환(묵시적 캐스팅) 을 수행합니다.
byte b = 10;
// b = b + 5; // 컴파일 에러! b+5의 결과는 int이므로 byte에 바로 대입 불가
b += 5; // 정상 동작! 내부적으로 b = (byte)(b + 5) 로 처리됨
System.out.println(b); // 15
단순 대입에서는 명시적 캐스팅이 필요한 경우라도, 복합 대입 연산자는 자동으로 처리해 줍니다.
6. 연산자 우선순위 실수 예제와 괄호 권장
우선순위를 잘못 이해하면 예상치 못한 결과가 나올 수 있습니다.
public class PrecedenceTrap {
public static void main(String[] args) {
// 함정 예시 1: + 와 * 의 우선순위
int a = 1 + 2 * 3; // 1 + (2*3) = 1 + 6 = 7 (의도가 7이면 OK)
int b = (1 + 2) * 3; // (1+2) * 3 = 3 * 3 = 9 (의도가 9이면 괄호 필수)
System.out.println("a = " + a); // 7
System.out.println("b = " + b); // 9
// 함정 예시 2: 비트 연산자와 비교 연산자
// & 의 우선순위는 == 보다 낮음!
int flags = 0b0110;
// 잘못된 코드: flags & 0b0010 == 0 → flags & (0b0010 == 0) → flags & false → 컴파일 에러
// 올바른 코드: 괄호로 명확히
boolean hasBit = (flags & 0b0010) != 0;
System.out.println("hasBit = " + hasBit); // true
// 함정 예시 3: 논리 연산자와 대입
int x = 5, y = 3;
boolean result = x > 0 && y > 0; // (x > 0) && (y > 0) → true
System.out.println("result = " + result); // true
// 함정 예시 4: 문자열 + 연산자 순서
System.out.println(1 + 2 + "3"); // "33" (1+2=3, 3+"3"="33")
System.out.println("1" + 2 + 3); // "123" ("1"+2="12", "12"+3="123")
System.out.println("1" + (2 + 3)); // "15" (괄호로 먼저 더함)
}
}
&, |, ^ 연산자는 ==, != 보다 우선순위가 낮습니다. 비트 연산 결과를 비교할 때는 항상 괄호로 묶어야 의도한 대로 동작합니다.
// 잘못된 예
if (flags & MASK == 0) { ... } // flags & (MASK == 0) 로 해석됨
// 올바른 예
if ((flags & MASK) == 0) { ... } // 괄호로 비트 연산을 먼저 수행
7. 실전 예제: 복잡한 수식 계산
아래 예제는 실제 시험 점수 처리와 등급 계산을 통해 다양한 연산자를 함께 사용하는 모습을 보여줍니다.
public class ScoreCalculator {
public static void main(String[] args) {
// 과목별 점수
int korean = 85;
int english = 92;
int math = 78;
int science = 88;
int history = 76;
// 총점 계산 (산술 연산자)
int total = korean + english + math + science + history;
// 평균 계산 (산술 연산자 + 형변환)
double average = (double) total / 5;
// 최고점 계산 (삼항 연산자 중첩)
int maxScore = (korean > english) ? korean : english;
maxScore = (maxScore > math) ? maxScore : math;
maxScore = (maxScore > science) ? maxScore : science;
maxScore = (maxScore > history) ? maxScore : history;
// 합격 여부 (비교 + 논리 연산자)
boolean pass = (average >= 70.0) && (math >= 60);
// 보너스 점수 (복합 대입 연산자)
double bonusAverage = average;
bonusAverage += 5.0; // 출석 보너스 5점
// 등급 판별 (복합 비교)
String grade;
if (bonusAverage >= 90) {
grade = "A";
} else if (bonusAverage >= 80) {
grade = "B";
} else if (bonusAverage >= 70) {
grade = "C";
} else {
grade = "F";
}
// 출력
System.out.println("===== 성적표 =====");
System.out.println("총점 : " + total + "점");
System.out.printf("평균 : %.2f점%n", average);
System.out.printf("보너스 : %.2f점%n", bonusAverage);
System.out.println("최고점 : " + maxScore + "점");
System.out.println("합격 여부: " + (pass ? "합격" : "불합격"));
System.out.println("등급 : " + grade);
}
}
실행 결과:
===== 성적표 =====
총점 : 419점
평균 : 83.80점
보너스 : 88.80점
최고점 : 92점
합격 여부: 합격
등급 : B
8. 핵심 정리
| 개념 | 설명 |
|---|---|
| 연산자(Operator) | 연산을 수행하는 기호 |
| 피연산자(Operand) | 연산의 대상이 되는 값 또는 변수 |
| 우선순위(Precedence) | 여러 연산자가 있을 때 처리 순서 (높을수록 먼저) |
| 결합 방향(Associativity) | 같은 우선순위의 연산자를 왼쪽 또는 오른쪽부터 처리 |
| 복합 대입 연산자 | +=, -= 등 연산과 대입을 한 번에 수행 |
- 우선순위가 불확실한 식에는 괄호를 적극 활용 하세요.
- 복잡한 산술식은 단계별로 변수에 나눠 저장 하면 디버깅이 쉬워집니다.
- 대입 연산자 체이닝(
x = y = z = 0)은 간단한 초기화에만 사용하세요.