본문으로 건너뛰기

Ch 3.2 단항 연산자 (Unary Operator)

단항 연산자(Unary Operator) 는 피연산자가 단 하나인 연산자입니다. 부호 연산자(+, -), 증감 연산자(++, --), 논리 부정 연산자(!), 비트 반전 연산자(~) 가 여기에 속합니다.


1. 단항 연산자 종류 요약

연산자사용 형태설명
++a피연산자의 양의 부호 (거의 사용 안 함)
--a피연산자의 부호를 반전
++++a / a++피연산자를 1 증가 (전위 / 후위)
----a / a--피연산자를 1 감소 (전위 / 후위)
!!aboolean 값을 반전 (true ↔ false)
~~a정수의 모든 비트를 반전

2. 부호 연산자 (+, -)

수학에서의 양수/음수 부호와 동일한 개념입니다.

  • +a: 피연산자의 값을 그대로 반환합니다 (실질적으로 아무 일도 하지 않음).
  • -a: 피연산자의 부호를 반전한 값을 반환합니다.
public class SignOperator {
public static void main(String[] args) {
int a = 10;
int b = -a; // b = -10, a는 변하지 않음
int c = -b; // c = 10

System.out.println("a = " + a); // 10
System.out.println("b = " + b); // -10
System.out.println("c = " + c); // 10

// 음수를 다시 음수로 → 양수
int temperature = -15;
System.out.println("온도: " + temperature); // -15
System.out.println("절댓값처럼: " + (-temperature)); // 15
}
}
int 변환 주의 (byte, short, char)

부호 연산자를 byte, short, char 타입에 적용하면 결과는 int 타입 으로 자동 변환됩니다.

byte b = 10;
// byte result = -b; // 컴파일 에러! -b의 결과는 int 타입
int result = -b; // 정상: int에 저장
System.out.println(result); // -10

// 명시적 캐스팅으로 byte에 저장하려면:
byte b2 = (byte)(-b); // 명시적 형변환 필요

booleanchar(+ 부호 연산자의 경우)를 제외한 기본형에만 사용할 수 있습니다.


3. 증감 연산자 (++, --)

증감 연산자는 피연산자의 값을 1 증가시키거나 1 감소시킵니다. 반복문 에서 가장 빈번하게 사용됩니다.

  • 증가 연산자(++): 피연산자의 값을 1 증가
  • 감소 연산자(--): 피연산자의 값을 1 감소

전위형(Prefix)과 후위형(Postfix) 비교

연산자의 위치에 따라 식이 계산되는 시점 이 달라집니다.

형태형식동작 순서
전위(Prefix)++i, --i먼저 증감→ 그 값을 식에서 사용
후위(Postfix)i++, i--먼저 현재 값을 사용→ 그 후 증감
public class IncrementDecrement {
public static void main(String[] args) {
// 후위형(Postfix): 현재 값을 먼저 사용, 그 후 증가
int i = 5;
int j = i++; // j에 i의 현재 값(5)을 대입한 뒤, i를 1 증가
System.out.println("후위형 - i: " + i + ", j: " + j); // i=6, j=5

// 전위형(Prefix): 먼저 증가, 증가된 값을 사용
int x = 5;
int y = ++x; // x를 먼저 1 증가(6)시킨 뒤, 그 값(6)을 y에 대입
System.out.println("전위형 - x: " + x + ", y: " + y); // x=6, y=6

// 감소 연산자
int a = 10;
int b = a--; // b=10, a=9
int c = --a; // a=8, c=8
System.out.println("감소 - a: " + a + ", b: " + b + ", c: " + c); // a=8, b=10, c=8
}
}

단독 사용 시 전위·후위 차이 없음

증감 연산자를 단독으로 사용할 때(다른 연산이나 대입 없이)는 전위·후위의 차이가 없습니다.

int n = 0;
n++; // n = 1 (후위)
++n; // n = 2 (전위) → 단독 사용 시 동일
n--; // n = 1 (후위)
--n; // n = 0 (전위) → 단독 사용 시 동일

식 내에서의 전위·후위 예제 (심화)

public class PrefixPostfixInExpr {
public static void main(String[] args) {
int a = 3;
// 식 안에서 혼합 사용 → 복잡해서 실무에서는 지양
int result = a++ + ++a; // 3 + 5 = 8 (a는 5가 됨)
// 설명: a++(현재 값 3 사용 후 a=4), ++a(a=5로 먼저 증가 후 5 사용)
System.out.println("result = " + result); // 8
System.out.println("a = " + a); // 5
}
}
증감 연산자를 식 안에서 복잡하게 사용하지 마세요

a++ + ++a 처럼 하나의 식에 증감 연산자를 여러 번 섞어 쓰면 가독성이 극도로 낮아지고 실수가 발생하기 쉽습니다. 반복문 카운터처럼 단독으로 사용하는 것이 권장됩니다.

반복문에서의 증감 연산자

public class LoopIncrement {
public static void main(String[] args) {
// for 루프에서 i++ (후위형) - 가장 일반적인 패턴
for (int i = 0; i < 5; i++) {
System.out.print(i + " "); // 0 1 2 3 4
}
System.out.println();

// for 루프에서 ++i (전위형) - 단독 사용이므로 결과 동일
for (int i = 0; i < 5; ++i) {
System.out.print(i + " "); // 0 1 2 3 4
}
System.out.println();

// while 루프에서 감소 연산자
int count = 5;
while (count > 0) {
System.out.print(count + " "); // 5 4 3 2 1
count--;
}
System.out.println();
}
}
++i vs i++ 성능 차이 (이론)

Java의 기본형(int 등)에서는 JIT 컴파일러가 최적화하므로 실질적 성능 차이는 없습니다. 그러나 C++에서 반복자(Iterator) 같은 객체에는 전위형이 더 효율적입니다. Java에서는 스타일 차이로 봐도 무방하며, 관례적으로 i++ 을 더 많이 사용합니다.


4. 논리 부정 연산자 (!)

boolean 값을 반전 시킵니다. truefalse로, falsetrue로 변환됩니다.

public class LogicalNot {
public static void main(String[] args) {
boolean isLoggedIn = false;
boolean isGuest = !isLoggedIn; // true

System.out.println("로그인 상태: " + isLoggedIn); // false
System.out.println("게스트 여부: " + isGuest); // true

// 조건문에서 활용
int age = 17;
boolean isAdult = (age >= 18);
if (!isAdult) {
System.out.println("미성년자입니다.");
}

// 이중 부정: !!x 는 x와 동일 (의미 없음)
boolean flag = true;
System.out.println(!flag); // false
System.out.println(!!flag); // true (권장하지 않는 표현)
}
}

! 연산자는 boolean 타입에만 사용 가능합니다. 다른 타입에 사용하면 컴파일 에러가 발생합니다.

int n = 5;
// boolean b = !n; // 컴파일 에러! int에는 ! 사용 불가

5. 비트 반전 연산자 (~)

정수형 피연산자의 모든 비트를 반전 시킵니다 (0은 1로, 1은 0으로). 결과는 -(n + 1) 과 동일합니다. 이는 2의 보수 표현 방식 때문입니다.

public class BitwiseNot {
public static void main(String[] args) {
int a = 5; // 2진수: 00000000 00000000 00000000 00000101
int b = ~a; // 2진수: 11111111 11111111 11111111 11111010 = -6
System.out.println("~5 = " + b); // -6

int c = 0;
System.out.println("~0 = " + (~c)); // -1

int d = -1; // 모든 비트가 1
System.out.println("~(-1) = " + (~d)); // 0

// 공식: ~n == -(n + 1)
for (int n = -3; n <= 3; n++) {
System.out.println("~" + n + " = " + (~n) + " [검증: -("+n+"+1) = " + (-(n+1)) + "]");
}
}
}

실행 결과:

~5 = -6
~0 = -1
~(-1) = 0
~-3 = 2 [검증: -(-3+1) = 2]
~-2 = 1 [검증: -(-2+1) = 1]
~-1 = 0 [검증: -(-1+1) = 0]
~0 = -1 [검증: -(0+1) = -1]
~1 = -2 [검증: -(1+1) = -2]
~2 = -3 [검증: -(2+1) = -3]
~3 = -4 [검증: -(3+1) = -4]
비트 반전의 활용

~ 연산자는 비트 마스킹, 특정 비트 끄기 등에서 사용됩니다.

int flags = 0b1111;    // 모든 비트 ON
int mask = 0b0010; // 2번째 비트만 끄고 싶음
flags = flags & (~mask); // flags & 0b1101 = 0b1101
System.out.println(Integer.toBinaryString(flags)); // 1101

6. 단항 연산자 사용 시 주의사항

연산 후 타입 변환

byte, short 타입에 단항 산술 연산자를 적용하면 결과가 int로 승격됩니다.

byte b1 = 100;
byte b2 = 20;

// byte result = b1 + b2; // 컴파일 에러: 결과가 int
int result = b1 + b2; // 정상: 120

// 복합 대입 연산자는 내부적으로 캐스팅 처리
b1 += b2; // b1 = (byte)(b1 + b2) = 120
System.out.println(b1); // 120

오버플로우 주의

증감 연산자로 인해 자료형의 최댓값을 초과하면 오버플로우 가 발생합니다.

int max = Integer.MAX_VALUE; // 2147483647
max++;
System.out.println(max); // -2147483648 (오버플로우!)

int min = Integer.MIN_VALUE; // -2147483648
min--;
System.out.println(min); // 2147483647 (언더플로우!)

7. 실전 예제: 토글(Toggle) 기능 구현 패턴

토글은 어떤 상태를 켜고 끄는 스위치 같은 기능입니다. ! 연산자를 이용하면 간단하게 구현할 수 있습니다.

public class ToggleExample {
public static void main(String[] args) {
// 패턴 1: 불리언 토글
boolean isPlaying = false;
System.out.println("초기 상태: " + (isPlaying ? "재생 중" : "정지"));

// 버튼을 누를 때마다 상태 반전
isPlaying = !isPlaying;
System.out.println("1번 클릭: " + (isPlaying ? "재생 중" : "정지")); // 재생 중

isPlaying = !isPlaying;
System.out.println("2번 클릭: " + (isPlaying ? "재생 중" : "정지")); // 정지

isPlaying = !isPlaying;
System.out.println("3번 클릭: " + (isPlaying ? "재생 중" : "정지")); // 재생 중

System.out.println();

// 패턴 2: 증감 연산자를 이용한 카운터
int clickCount = 0;
System.out.println("클릭 횟수: " + clickCount); // 0

clickCount++;
clickCount++;
clickCount++;
System.out.println("3번 클릭 후: " + clickCount); // 3

clickCount--;
System.out.println("실행 취소 후: " + clickCount); // 2

System.out.println();

// 패턴 3: 방향 전환 (부호 연산자 토글)
int direction = 1; // 1: 오른쪽, -1: 왼쪽
System.out.println("초기 방향: " + (direction > 0 ? "오른쪽" : "왼쪽"));

direction = -direction; // 방향 반전
System.out.println("반전 후: " + (direction > 0 ? "오른쪽" : "왼쪽")); // 왼쪽

direction = -direction;
System.out.println("재반전: " + (direction > 0 ? "오른쪽" : "왼쪽")); // 오른쪽

System.out.println();

// 패턴 4: 비트 XOR를 이용한 토글 (비트 연산자 활용)
int lamp = 0; // 0: OFF, 1: ON
System.out.println("램프: " + (lamp == 1 ? "ON" : "OFF")); // OFF
lamp ^= 1;
System.out.println("스위치 후: " + (lamp == 1 ? "ON" : "OFF")); // ON
lamp ^= 1;
System.out.println("스위치 후: " + (lamp == 1 ? "ON" : "OFF")); // OFF
}
}

실행 결과:

초기 상태: 정지
1번 클릭: 재생 중
2번 클릭: 정지
3번 클릭: 재생 중

클릭 횟수: 0
3번 클릭 후: 3
실행 취소 후: 2

초기 방향: 오른쪽
반전 후: 왼쪽
재반전: 오른쪽

램프: OFF
스위치 후: ON
스위치 후: OFF

8. 핵심 정리

연산자피연산자 타입주요 특징
+, -숫자형 (byte/short → int로 승격)부호 표시, -는 값 반전
++, --숫자형전위: 먼저 증감 후 사용, 후위: 먼저 사용 후 증감
!boolean만true ↔ false 반전
~정수형모든 비트 반전, 결과 = -(n+1)
실무 권장 사항
  • 단순 카운터에는 i++ 또는 ++i 중 일관성 있게 하나를 선택하세요 (Java에서는 성능 차이 없음).
  • 식 내에서 증감 연산자와 다른 연산자를 함께 사용하는 것은 피하세요.
  • 토글 로직에는 !flag 패턴을 사용하면 코드가 명확해집니다.