Post

[스터디 할래] week03. 연산자 (+ switch)

백기선님의 스터디 “스터디 할래”를 참고로 한 정리입니다.

연산자

학습할 것

  • 산술 연산자
  • 비트 연산자
  • 관계 연산자
  • 논리 연산자
  • instanceof
  • assignment(=) operator
  • 화살표(->) 연산자
  • 3항 연산자
  • 연산자 우선 순위
  • (optional) Java 13. switch 연산자

용어 정의

  • 프로그램에서 데이터를 처리하여 결과를 산출하는 것을 연산(operation)이라고 한다.
  • 연산에 사용되는 표시나 기호를 연산자(operator)라고 한다.
  • 연산의 대상이 되는 데이터는 피연산자(operand)라고 한다.
  • 연산자와 피연산자로 연산의 과정을 기술한 것을 연산식(expressions)이라고 한다.

연산자의 종류

종류연산자결과 값설명
산술 연산자+ , - , * , / , %숫자사칙연산 및 나머지 계산
증감 연산자++ , –숫자1만큼 증가/감소
비트 연산자& , | , ^ , ~숫자, boolean비트 NOT, AND, OR, XOR 연산
시프트 연산자» , « , »>숫자비트를 좌측/우측으로 밀어서 이동
관계(비교) 연산자> , < , >= , <= , == , !=boolean값의 비교
논리 연산자&& , || , !boolean논리적 NOT, AND, OR 연산
조건(삼항) 연산자? , :다양조건식에 따라 A 또는 B 중 하나를 선택
대입 연산자= , *= , /= , %= , += , -=다양우변의 값을 좌변의 변수에 대입

산술 연산자

자바에서 산술 연산은 사칙 연산 (덧셈, 뺄셈, 곱셈, 나눗셈)과 나머지 연산이 있다. 산술 연산자는 정수, 부동소수점, 문자열 등 boolean 타입을 제외한 모든 프리미티브 타입에서 사용이 가능하다. 피연산자들 중 부동 소수점이 있다면 부동소수점 산술이 되고 아니면 정수 산술이 된다.

더하기 ( + )

+ 연산자는 기본적으로 두 수를 더하는 연산이나 문자열을 연결할 수도 있다. 만약 +의 피연산자 중 문자열이 있다면 나머지 피연산자도 문자열로 변환 된다.

1
2
3
4
5
6
7
8
9
int num1 = 10;
int num2 = 3;

int sum1 = num1 + num2;
System.out.println(sum1); // 13

String str = "Hello ";
String sum2 = str + num1;
System.out.println(sum2); // Hello l10

빼기 ( - )

- 연산자는 첫번째 피연산자에서 두번째 피연산자를 빼는 연산자이다. 더하기 연산처럼 문자열 연산은 불가능하다.

1
2
3
4
5
6
7
8
int num1 = 10;
int num2 = 3;

int sum1 = num1 - num2;
System.out.println(sum1); // 7

String str = "Hello ";
String sum2 = str - num1; // 컴파일 에러

곱하기 ( * )

* 연산자는 두 피연산자를 곱한다. 마찬가지로 문자열 연산은 불가능하다.

1
2
3
4
5
6
7
8
int num1 = 10;
int num2 = 3;

int sum1 = num1 * num2;
System.out.println(sum1); // 30

String str = "Hello ";
String sum2 = str * num1; // 컴파일 에러

나누기 ( / )

/ 연산자는 첫번째 피연산자를 두번째 피연산자로 나눈다. 두 피연산자가 모두 정수라면 결과도 정수이며 나머지는 내림으로 없어진다. 피연산자 중 부동소수점이 있다면 결과도 부동소수점이다.

정수를 0으로 나눌 경우 ArithmeticException이 발생한다.

1
2
System.out.println(10 / 3); // 3
System.out.println(10 / 3.0); // 3.33333335

나머지 ( % )

% 연산자는 첫번째 피연산자를 두번째 피연산자로 나누고 남은 나머지를 정수로 리턴한다. 부동소수점에도 사용이 가능하고 이러한 경우 부동소수점으로 리턴한다.

1
2
3
System.out.println(10 % 4); // 2
System.out.println(-10 % 4); // -2 (첫번째 피연산자의 부호와 결과의 부호 동일)
System.out.println(10 % -4); // 2

비트 연산자

비트 연산자와 시프트 연산자는 개별 비트를 조작하는 연산자이다. 비트 연산을 이해하기 위해서는 이진수와 음의 정수를 나타내는데 사용되는 2의 보수를 알아야한다.

부동소수점, boolean, 배열, 객체 등을 피연산자로 사용할 수 없다.

비트 보수 ( ~ )

~ 연산자는 비트 반전 혹은 비트 NOT 연산자라고 한다. 각 비트를 반전시켜 1을 0으로, 0을 1로 변환한다.

1
2
3
4
5
int i = ~1; 
byte b = ~1;

System.out.println(i); // -2 (바이너리 값 : 0b11111111_11111111_11111111_11111110)
System.out.println(b); // -2 (바이너리 값 : 0b11111110)

둘다 출력해보면 -2가 나오지만 바이너리 값은 다르다. int는 32bit이고 byte는 8bit이기 때문이다.

AND (&)

&연산자는 두 정수 피연산자를 AND 연산한다. (비트곱) 비트곱은 두 피연산자의 해당 비트가 모두 1일때만 1 아니면 0을 리턴한다

1
2
3
int num1 = 10; // (바이너리 값 : 0b00001010)
int num2 = 20; // (바이너리 값 : 0b00010100)
System.out.println( num1 & num2 ); // 0 (바이너리 값 : 0b00000000)

OR (|)

| 연산자는 두 정수 피연산자의 OR 연산을 한다. (비트합)

1
2
3
int num1 = 10; // (바이너리 값 : 0b00001010)
int num2 = 20; // (바이너리 값 : 0b00010100)
System.out.println( num1 | num2 ); // 30 (바이너리 값 : 0b00011110)

XOR (^)

^ 연산자는 두 정수 피연산자의 XOR 연산을 한다. (exclusive OR) 두 피연산자의 해당 비트가 같으면 0, 다르면 1을 리턴한다.

1
2
3
int num1 = 10; // (바이너리 값 : 0b00001010)
int num2 = 20; // (바이너리 값 : 0b00010100)
System.out.println( num1 ^ num2 ); // 30 (바이너리 값 : 0b00011110)

왼쪽 시프트 연산 (<<)

<<연산자는 비트를 왼쪽으로 두번째 피연산자로 제시된 비트 수만큼 이동시킨다. 시프트 될 때 가장 된쪽 비트는 삭제되고 가장 오른쪽 비트는 0으로 채워진다.

1
2
3
int num = 10; // (바이너리 값 : 0b00001010)
int result1 = num << 1; // 20 (바이너리 값 : 0b00010100)
int result2 = num << 2; // 40 (바이너리 값 : 0b00101000)

오른쪽 시프트 연산 (>>)

>>연산자는 비트를 오른쪽으로 두번째 피연산자로 제시된 비트 수만큼 이동시킨다. 시프트 될 때 가장 오른쪽 비트는 삭제되고 가장 왼쪽쪽 비트는 0으로 채워진다. 음수인 경우는 1이 채워진다.

1
2
3
4
5
6
7
int num1 = 10; // (바이너리 값 : 0b00001010)
int result1 = num1 >> 1; // 20 (바이너리 값 : 0b00000101)
int result2 = num1 >> 2; // 40 (바이너리 값 : 0b00000010)

int num2 = -10; // (바이너리 값 : 0b1110110)
int result3 = num2 >> 1; // -5 (바이너리 값 : 0b1111011)
int result4 = num2 >> 2; // -2 (바이너리 값 : 0b1111101)

unsigned 오른쪽 시프트 연산 (>>>)

오른쪽 시프트 연산과 비슷하지만 피연산자의 부호와 상관없이 왼쪽 비트는 0으로 채워진다.


관계 연산자 (비교 연산자)

  • 같거나 같지 않음을 평가하는 비교 연산자
  • 크고 작은 관계를 평가하는 관계 연산자
  • 이 연산들의 결과 값은 항상 부울형(true 또는 false)의 형태이기 때문에 if문과 같은 제어문이나 for문 같은 반복문장에 자주 사용된다.
연산자기능사용법사용 설명
>보다 크다op1 > op2op1이 op2보다 큰 경우 true 아니면 false
>=보다 크거나 같다.op1 >= op2op1이 op2보다 크거나 같은 경우 true 아니면 false
<보다 작다op1 < op2op1이 op2보다 작은 경우 true 아니면 false
<=보다 작거나 같다op1 <= op2op1이 op2보다 작거나 같은 경우 true 아니면 false
==같다op1 == op2op1과 op2이 같은 경우 true 아니면 false
!=같지 않다op1 != op2op1과 op2이 같지 않은 경우 true 아니면 false

논리 연산자

비트 연산과 비슷하지만 피연산자가 boolean 타입의 논리 값이다.

  • !는 논리적인 부정을 뜻하며 true를 false, false를 true로 바꿔준다.
  • &&는 첫번째 피연산자의 결과가 false라면 두번째 피연산자를 평가하지 않고 false를 리턴한다.
  •  는 첫번째 피연산자의 결과가 true라면 두번째 피연산자를 평가하지 않고 true를 리턴한다.
1
2
3
4
5
6
7
8
9
boolean myTrue = true;
boolean myFalse = false;

if (myTrue & myFalse) System.out.println("myTrue & myFalse 는 true"); 
if (myTrue | myFalse) System.out.println("myTrue | myFalse 는 true"); // 출력
if (myTrue && myFalse) System.out.println("myTrue && myFalse 는 true");
if (myTrue || myFalse) System.out.println("myTrue || myFalse 는 true"); // 출력

if (!myFalse) System.out.println("!myFalse 는 true"); // 출력

instanceof

객체또는 배열 값을 어떠한 참조 유형에 맞는 값인지를 평가하는 연산자이다. 만약 null을 평가한다면 항상 false가 리턴된다. 만약 평가 결과가 true라면 비교된 참조 유형으로 안전하게 캐스팅하고 할당할 수 있다는 것을 의미한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
"str" instanceof String // true
null instanceof String // false

Obejct o = new int[]{1, 2, 3};
o instanceof int[] // true

// safety casting
if (obj instance of MyClass) {
    MyClass c = (MyClass) obj;
}

// Primitive type은 사용할 수 없다.
i instanceof int // 컴파일 에러

assignment(=) operator

오른쪽의 피연산자를 왼쪽의 피연산자의 값으로 할당할 때 사용한다. 메모리에 값을 저장하거나 할당한다는 의미이다.


화살표(->) 연산자 (람다 표현식)

자바8부터 도입된 연산자로 람다 표현식이라고 하며 메서드 본문에 해당 실행 가능한 자바 코드의 익명 컬렉션이다.

메서드 파라미터 목록, 연산자, 코드 블럭 순으로 구성된다. 코드 블럭이 한 문장으로 끝난다면 중괄호는 생략할 수 있다.

1
2
3
Runnable r = () -> {
    sout
}

3항 연산자

if -else 문장을 연산자로 표현할 수 있고 조건에 따라 결과를 리턴할 수 있다.

` 조건 ? 조건이 참일 때 실행 : 조건이 거짓일 때 실행 `

1
2
System.out.println(1 > 3 ? "true 리턴해줘" : "false 리턴해줘"); // false 리턴해줘
System.out.println(5 > 3 ? "true 리턴해줘" : "false 리턴해줘"); // true 리턴해줘

연산자 우선 순위

우선 순위연산자동작
1( ),[ ]요소 접근
2!, ~, ++, –비트 보수, 부정 연산, 후위 전위 증감
3*, /, %곱하기, 나누기, 나머지
4+, -더하기, 빼기
5«, », »>왼쪽 시프트, 오른쪽 시프트, 부호없는 오른쪽 시프트
6<, <=, >, >=작음, 작거나 같음, 큼, 크거나 같음
7==, !=같음, 같지 않음
8&AND
9^XOR
10|OR
11&&AMD
12||OR
13? :삼항 연산자
14=, +=, -=, *=, /=, «=, »=, &=, ^=, ~=대입 연산자
15->람다 표현식

(optional) Java 13. switch 연산자

swtich 연산자는 조건에 따라 분기해야할 내용이 많아질 경우, 가독성과 실행 속도 측면에서 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
int result;
switch (str) {
    case a:
        result = 1;
        break;
    case b:
        result = 2;
        break;
    case c:
        result = 3;
        break;
};
return result;

위의 switch 문의 사용방식이 전통적인 switch문의 문법이다.

이는 몇가지 문제점이 있는데 break 키워드 쓰는 것을 잊었을 경우 그 아래있는 코드 블럭이 실행된다는 것과 하나의 case에는 하나의 값만 비교할 수 있다는 것이다. 자바 12, 13부터는 위 방식의 문제점을 개선한 새로운 기능들이 추가되었다.

  • 자바12부터는
    • 쉼표( , )를 사용하여 여러 케이스를 한 줄에 나열할 수 있다.
    • 화살표 ( -> )를 사용하여 결과를 반환할 수 있으며 break 키워드를 사용하지 않아도 된다.
  • 자바13부터는
    • yield 키워드를 사용하여 switch 결과를 반환할 수 있다.
    • scope를 사용하여 case-level의 scope를 이용할 수 있다.

쉼표 사용

1
2
3
4
5
6
7
8
9
10
11
12
private static int switchWithMultiCase(String str) {
    int result;
    switch (str) {
        case "a":
            result = 1;
            break;
        case "b","c"
            result = 2;
            break;
    };
    return result;
}

화살표 사용

switch문과 함께 화살표 연산자(->)를 사용할 수 있다. 화살표 연산자를 사용하면 break은 사용할 필요가 없다.

화살표 연산자의 우변에 올 수 있는 것은 다음과 같다.

  • Statement / expression
  • throw statement
  • {} block
1
2
3
4
5
6
7
8
9
private static int switchWithArrow(String str) {
    int result = switch (str) {
        case "a", "b" -> 1;
        case "c" -> 2;
        case "d", "e", "f" -> 3;
        default -> -1;
        };
    return result;
}

yield 키워드 사용

Java 13부터는 yield라는 키워드를 통해 switch문이 값을 반환할 수 있게 되었다. 따라서 switch문을 식으로도 사용이 가능하게 되었다. yield 키워드가 값을 반환하면서 switch문을 끝내므로 break은 사용할 필요가 없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
private static int switchWithYield(String str) {
    int result = switch (str) {
        case "a", "b":
            yield 1;
        case "c":
            yield 2;
        case "d", "e", "f" :
            yield 3;
        default:
            yield -1;
    };
    return result;
}

scope 사용

Java 13부터 switch문의 각 case에 {}을 쓸 수 있게되어, case-level의 scope를 사용할 수 있게 되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static int switchWithScope(String str) {
    int result;
    switch (str) {
        case "a": {
            result = 1;
            break;
        }
        case "b": {
            result = 2;
            break;
        }
        case "c", "d": {
            result = 3;
            break;
        }
    };
    return result;
}
This post is licensed under CC BY 4.0 by the author.