코딩/Java

Java 3 : 연산자

americanoallday 2025. 2. 5. 16:46

연산자 우선순위(Precedence)

✔ 동일한 우선순위에서는 결합 방향(Associativity)에 따라 실행됨.

순위 연산자 설명 결합 방향
1 (), [], . 괄호, 배열 접근, 객체 접근 왼 > 오
2 i++, i-- 후위 증가/감소 연산자 왼 > 오
3 ++i, --i, +, -, ~, ! 전위 증가/감소, 부호, 비트 NOT, 논리 NOT 오 > 왼
4 *, /, % 곱셈, 나눗셈, 나머지 왼 > 오
5 +, - 덧셈, 뺄셈 왼 > 오
6 <<, >>, >>> 비트 이동(시프트) 왼 > 오
7 <, <=, >, >=, instanceof 비교 연산자 왼 > 오
8 ==, != 등가 연산자 왼 > 오
9 & 비트 AND 왼 > 오
10 ^ 비트 XOR 왼 > 오
11 | 비트 OR 왼 > 오
12 && 논리 AND 왼 > 오
13 || 논리 OR 왼 > 오
14 ?: 삼항 연산자 오 > 왼
15 =, +=, -=, *=, /=, %= 등 대입 연산자 오 > 왼

* 후위/전위 증가/감소 차이

- 후위 : 현재 값을 먼저 사용한 후 증가

- 전위 : 먼저 증가한 후 값을 사용

 

✅ 3줄 요약

1. 산술 > 비교 > 논리 > 대입 : 연산자의 기능에 따른 분류

**산술 연산자 (+, -, *, /)**가 **비교 연산자(>, <)**보다 우선순위가 높음.

**논리 연산자(&&, ||)**는 비교 연산자보다 낮음.

대입 연산자(=)는 가장 마지막에 실행됨.

 

2. 단항(1) > 이항(2) > 삼항(3) : 연산자 개수에 따른 분류

**단항 연산자(++, --, !, ~)**가 가장 우선순위가 높음.

**이항 연산자(+, -, *, /, %, &&, ||)**는 단항보다 낮음.

삼항 연산자(?:)는 이항보다 낮고, 대입(=)보다 높음.

더보기

단항 연산자(Unary Operator)

피연산자 개수 1개

예제 : ++a, !a, ~a, --a

이항 연산자(Binary Operator)

피연산자 개수 2개

예제 : a + b, a - b, a * b

삼항 연산자(Ternary Operator)

피연산자 개수 3개

예제 : a > b ? x : y

3. 단항, 대입 제외 모든 연산 방향은 왼쪽 → 오른쪽

대부분의 연산자는 왼쪽에서 오른쪽 방향으로 진행됨.

하지만 단항 연산자(++i, --i, !, ~)와 대입 연산자(=)는 오른쪽에서 왼쪽으로 진행됨.

 

부호 연산자

- : 피연산자의 부호를 반대로 변경

public class App {
    public static void main(String[] args) throws Exception {
        int i = -10;
        i = +i;
        System.out.println("i = +i; = " + i);

        i = -10;
        i = -i;
        System.out.println("i = -i; = " + i);
    }
}
i = +i; = -10
i = -i; = 10

 

형변환

변수 또는 상수의 타입을 변환하는 것

변환 수식 결과
int -> char (char)65 A'
char -> int (int)'A' 65
float -> int (int)1.6f 1
int -> float (float)10 10.0f

 

자동 형변환

float f = 1234;

* 형변환이 생략되어 있음, 컴파일러에 의해 자동 형변환 (float)1234이 진행 된 것

 

int i = 3.14f 

* 작은 타입에 큰타입을 저장할 때는 Error 발생(이런 경우 컴파일러가 자동 형변환을 하지 않음)

 

산술 변환

1. 두 피연산자의 타입을 같게 일치시킨다.(보다 큰 타입으로 일치)
  • long + int -> long + long => long
  • float + int -> float + float => float
  • double + float -> double + double => double
2. 피연산자의 타입이 int 보다 작은 타입 이면 int 로 변환된다.
  • byte + short -> int + int => int
  • char + short -> int + int => int

 

Math 클래스

수학과 관련된 메서드를 가지고 있는 클래스

  • round() : 실수를 소수점 첫 째자리에서 반올림정수를 반환합니다.

  • ceil() : 올림값을 double 형으로 반환합니다.
  • floor() : 내림값을 double 형으로 반환합니다.
  • abs() : int, double 기본형 모두 사용 가능하며 절대값을 얻습니다.
class Operator5_2 {
    public static void main(String[] args) {
        double num = 3.14;
        
        System.out.println("반올림 : " + Math.round(num)); // 반올림 : 3
        System.out.println("올림 : " + Math.ceil(num)); // 올림 : 4.0
        System.out.println("내림 : " + Math.floor(num)); // 내림 : 3.0
        System.out.println("절대값 : " + Math.abs(num*-1)); // 절대값 : 3.14
    }
}

 

 

나머지 연산자 %

나머지를 반환, 피연산자는 0이 아닌 정수만 허용, 부호는 무시 됨

class Operator5_3 {
    public static void main(String[] args) {
        int x = 10;
        int y = 8;

        System.out.print("x를 y로 나눈 몫 = ");
        System.out.println(x / y);

        System.out.print("x를 y로 나눈 나머지 = ");
        System.out.println(x % y);

        // 부호 무시 확인
        int n = 3;
        int z = -3;
        System.out.println("x % n = " + x % n);
        System.out.println("x % z = " + x % z);
    }
}
x를 y로 나눈 몫 = 1
x를 y로 나눈 나머지 = 2
x % n = 1
x % z = 1

 

비교 연산자 (Relational Operators)

두 개의 값을 비교하는 연산자로, 결과가 true 또는 false로 반환됨.

== : 두 값이 같은지 비교

!= : 두 값이 다른지 비교

public class ComparisonExample {
    public static void main(String[] args) {
        int a = 10, b = 20;

        System.out.println("a == b: " + (a == b)); // false
        System.out.println("a != b: " + (a != b)); // true
        System.out.println("a > b: " + (a > b));   // false
        System.out.println("a < b: " + (a < b));   // true
        System.out.println("a >= b: " + (a >= b)); // false
        System.out.println("a <= b: " + (a <= b)); // true
    }
}

 

문자열 비교 (== vs equals())

문자열 비교할 때 == 사용하면 안 됨!

문자열 비교는 equals() 메서드를 사용해야 정확한 결과를 얻을 수 있음.

public class StringComparison {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = "Hello";
        String str3 = new String("Hello");

        System.out.println("str1 == str2: " + (str1 == str2)); // true (같은 리터럴)
        System.out.println("str1 == str3: " + (str1 == str3)); // false (다른 객체)
        System.out.println("str1.equals(str3): " + str1.equals(str3)); // true (내용 비교)
    }
}

 

*Java의 문자열은 “문자열 리터럴 풀(String Literal Pool)“이라는 특별한 메모리 영역을 사용함.

📌 즉, str1str2는 같은 메모리 주소를 가리키지만, str3은 새로운 객체이기 때문에 주소가 다름!

✔ "Hello"는 **String Literal Pool(문자열 리터럴 풀)**에 저장됨.

✔ str1과 str2는 같은 "Hello" 리터럴을 참조하므로, 메모리 주소가 같음.

✔ 따라서 str1 == str2 → true

📌 즉, 같은 문자열 리터럴을 사용하면, Java는 메모리를 절약하기 위해 같은 객체를 공유

✔ new String("Hello")를 사용하면 Heap 메모리에 새로운 객체가 생성됨.

✔ str1과 str3는 같은 "Hello" 값을 가지지만, 다른 메모리 주소를 가짐.

✔ 따라서 str1 == str3 → false

 

 

논리 연산자(Logical Operators)true 또는 false 값을 다루는 연산

&& : AND 연산, 모두 참이면 참을 반환

|| : OR 연산, 둘 중 하나라도 참이면 참을 반환

! : NOT 연산, 결과가 참이면 거짓을, 거짓이면 참을 반환

 

비트 연산자(Bitwise Operators) → 숫자의 각 비트 단위(0과 1)를 직접 연산

& : 비트 AND 연산, 대응되는 비트가 모두 1이면 1을 반환

| : 비트 OR 연산, 대응되는 비트 중 하나라도 1이면 1을 반환 

^ : 비트 XOR 연산, 서로 다르면 1을 반환 (0101 ^ 0011 -> 0110)

~ : 비트 NOT 연산, 1이면 0, 0이면 1을 반환(반대값 반환) (~0101 -> 1010)

<< : left shift 연산, 명시된 수만큼 비트를 전부 왼쪽 이동 (0001 << 2 -> 0100)

>> : right shift 연산, 명시된 수만큼 비트를 전부 오른쪽 이동 (1000 >> 2 -> 0010)

>>> : 지정 수만큼 비트를 전부 오른쪽 이동, 새로운 비트는 전부 0이 됨 (1000 >>> 2 -> 0010)

 

📌 즉, 논리 연산자는 boolean 값을 다루고, 비트 연산자는 int, long 같은 정수의 비트 단위를 다루는 연산자

 

예제 : 논리 연산자

public class LogicalOperators {
    public static void main(String[] args) {
        boolean a = true, b = false;

        System.out.println("a && b: " + (a && b)); // false
        System.out.println("a || b: " + (a || b)); // true
        System.out.println("!a: " + (!a)); // false
    }
}

 

예제 : 비트 연산자

public class BitwiseExample {
    public static void main(String[] args) {
        int a = 5;  // 0101 (2진수)
        int b = 3;  // 0011 (2진수)

        System.out.println("a & b: " + (a & b)); // 0001 = 1
        System.out.println("a | b: " + (a | b)); // 0111 = 7
        System.out.println("a ^ b: " + (a ^ b)); // 0110 = 6
        System.out.println("~a: " + (~a));       // 1010 (음수 표현)
    }
}

 

음수 표현

1. 부호-절대값 (Sign-Magnitude)

가장 왼쪽 비트(MSB, 최상위 비트)를 부호 비트로 사용함

0이면 양수, 1이면 음수

예제 (8비트 표현)

+ 5 : 00000101

- 5 : 10000101

 

2. 1의 보수 (One’s Complement)

음수를 만들 때 모든 비트를 반전(0 → 1, 1 → 0)하는 방식

가장 왼쪽 비트(MSB)는 부호 비트로 사용

예제 (8비트 표현)

+ 5 : 00000101

- 5 : 11111010 (비트 반전)

 

3. 2의 보수 (Two’s Complement) ← 💡실제 컴퓨터에서 사용하는 방식!

0이 한 가지 형태(00000000)로만 존재해서 비효율이 없음! (부호-절대값, 1의 보수에는 0이 2개임(+0, -0))

1의 보수에서 +1을 더한 값

덧셈/뺄셈이 간단해지고, 0이 하나만 존재함!

예제 (8비트 표현)

+ 5 : 00000101

- 5 : 11111011 (1의 보수 + 1)

 

비트 연산(&, |, ^, ~), 시프트 연산(<<, >>), 오버플로우 처리할 때 개념을 이해해야 함.

면접에서 자주 나오는 개념이라 기본적으로 알고 있으면 좋다고 함.

📌 즉, 기본 개념만 알고 있으면 되고, 실제 코딩할 때는 신경 쓸 일이 거의 없음. 

 

 

조건 연산자(삼항 연산자, ?:) → 조건에 따라 값을 선택하는 연산자

형식 : 조건이 true이면 값1을 반환, false이면 값2를 반환

📌 조건 연산자는 if-else를 짧게 줄일 때 유용

조건 ? 값1 : 값2;

 

예제

public class TernaryOperatorExample {
    public static void main(String[] args) {
        int a = 10, b = 20;
        
        // 조건 연산자 사용
        int min = (a < b) ? a : b; // a가 b보다 작으면 a, 아니면 b
        
        System.out.println("더 작은 값: " + min);
    }
}
더 작은 값: 10

 

if-else, 조건 연산자 비교

// if-else 사용
int min;
if (a < b) {
    min = a;
} else {
    min = b;
}

// 조건 연산자로 줄이기
int min = (a < b) ? a : b;

 

대입 연산자(= 및 복합 대입 연산자 +=, -=, *= 등) → 변수에 값을 저장하는 연산자

형식

변수 = 값;

 

예제

int x = 10;
int y = x; // x의 값을 y에 복사
System.out.println(y); // 10

 

 

복합 대입 연산자 

형식

x += y;  // x = x + y 와 동일
x -= y;  // x = x - y 와 동일
x *= y;  // x = x * y 와 동일
x /= y;  // x = x / y 와 동일
x %= y;  // x = x % y 와 동일