[Live-Study] 연산자

Livenow·2021년 1월 18일
0
post-thumbnail

목표

자바가 제공하는 다양한 연산자를 학습하세요.

학습할 것

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

2주차까지 하며, 그동안 대충 알고있던 개념들을 확실히 잡을 수 있었습니다. 3주차도 부족하지 않은 정리를 해볼까합니다.

연산자(Operator)?

여러종류의 연산자 를 살펴보기 전, 연산자란 무엇인가에 대해 살펴볼 필요가 있습니다.

연산자는 변수와 값에 대한 작업을 수행하는데 쓰이는데 사용됩니다.

아래의 예제에선 + 연산자를 사용하여 두개의 값을 더하는 것을 볼 수 있습니다.

int x = 100 + 50;

+ 연산자는 위의 예시처럼 두 값을 더하는데 자주 사용되지만, 변수와 값 또는 변수와 다른 변수를 더하는 곳에서도 사용될 수 있습니다.

int sum1 = 100 + 50;        // 150 (100 + 50)
int sum2 = sum1 + 250;      // 400 (150 + 250)
int sum3 = sum2 + sum2;     // 800 (400 + 400)

산술 연산자 (Arithmetic Operators)

산술 연산자는 일반적인 수학 연산을 수행하는데 사용됩니다.

연산자이름설명에제
+더하기(Addition)두 값을 더함x + y
-빼기(Subtraction)하나의 값에 다른값을 뺌x - y
*곱하기(Multiplication)두 값을 곱함x * y
/나누기(Division)하나의 값에 다른 값을 나눔x / y
%계수(Modulus)나눗셈 나머지를 반환x % y
++증가(Increment)변수값을 1 씩 증가++x
--감소(Decrement)변수값을 1 씩 감소--x

비트 연산자(Bitwise Operators)

비트 연산자는 비트(bit) 단위로 논리 연산을 할 때 사용하는 연산자입니다.
또한, 비트 단위로 전체 비트를 왼쪽이나 오른쪽으로 이동시킬 때도 사용합니다.

연산자설명예제이진수결과십진수
&대응되는 비트가 모두 1이면 1을 반환함. (비트 AND 연산)5 & 10101 & 000100011
대응되는 비트 중에서 하나라도 1이면 1을 반환함. (비트 OR 연산)5 ㅣ 10101 ㅣ000101015
~비트를 1이면 0으로, 0이면 1로 반전시킴. (비트 NOT 연산)~5~0101101010
^대응되는 비트가 서로 다르면 1을 반환함. (비트 XOR 연산)5 ^ 10101 ^ 000101004
<<지정한 수만큼 비트들을 전부 왼쪽으로 이동시킴. (left shift 연산)9 << 11001 << 100102
>>부호를 유지하면서 지정한 수만큼 비트를 전부 오른쪽으로 이동시킴. (right shift 연산)9 >> 11001 >> 1110012
>>>0을 채우면서 오른쪽 시프트9 >>> 11001 >>> 101004

중간값 구하기 실습

양수의 중간 값을 구할 때는, 아래와 같은 방법을 써야 오버플로우가 발생하지 않는다 ( 중요 )

public class MidNumber {
    public static void main(String[] args) {
        int start = 2_000_000_000;
        int end = 2_100_000_000;

        int mid = start + (end - start) / 2;

        System.out.println("mid = " + mid);
    }
}

조금 멋을 부리면 아래와 같이 사용하는 것도 가능 ( 오른쪽 쉬프트를 하며 0을 채우면 2로 나눈것 과 같은 원리를 이용함)

public class MidNumber {
    public static void main(String[] args) {
        int start = 2_000_000_000;
        int end = 2_100_000_000;

				int mid = (end + start) >>> 1;

        System.out.println("mid = " + mid);
    }
}

XOR 실습

public class Hello {

    // TODO numbers라는 Int형 배열이 있다.
    //  해당 배열에 들어있는 숫자들은 오직 한 숫자를 제외하고는 모두 두번씩 들어있다.
    //  오직 한 번만 등장하는 숫자를 찾는 코드를 작성하라
    public static void main(String[] args) {
        Hello hello = new Hello();
        int result = hello.solution(new int[]{5, 4, 3, 2, 1, 2, 3, 4, 5});
        System.out.println("result = " + result);
    }

    // TODO XOR
    //  5 ^ 0 = 5
    //  5 ^ 5 = 0
    //  101
    //  000
    //  ---
    //  000
    private int solution(int[] numbers) {
        int result = 0;
        for (int number : numbers) {
            result ^= number;
        }
        return result;
    }
}

관계 연산자(Relational operators)

관계 연산자는 이항 연산자로서 한 연산항이 다른 연산항에 대해 가지는 관계를 결정합니다.
이 연산들의 결과 값은 항상 불린형(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

논리 연산자(Logical Operators)

논리 연산자는 피연산자(operand)의 값을 평가하여 결과로 true 또는 false값을 반환합니다.

특이 사항으로 &&(논리곱)와 ||(논리합) 단축 논리 연산자(Short-circuit logical operator)가 있습니다.

&&(논리곱)은 첫번째 조건이 참이 아니면 두번째 조건은 확인하지 않습니다.

&는 첫번째 조건이 참이 아니어도 두번째 조건을 확인합니다.

||(논리합)은 첫번째 조건이 참이면 두번째 조건을 확인하지 않습니다.

|는 첫번째 조건이 참이어도 두번째 조건을 확인합니다.

예를 들면, 아래와 같은 수식에서 flag가 true이면 "count > index++"의 수식은 처리가 되지 않아 index는 증가하지 않는다. 반대로 flag가 false이면 index가 1만큼 증가를 한다.

(flag == true) || (count > index++)
연산자기능예제설명
&짧은 순환 ANDop1 & op2op1과 op2가 모두 true인 경우 true, op1과 op2를 모두 평가한다.
짧은 순환 ORop1 ㅣ op2op1과 op2중 둘 중 하나 이상이 true인 경우 true, op1과 op2를 모두 평가한다.
^XOR(배타적 OR)op1 ^ op2op1과 op2중 둘 중 하나 이상이 true인 경우 false, op1과 op2를 모두 평가한다.
&&논리적 ANDop1 && op2op1과 op2가 모두 true인 경우 true, op1가 false이면 op2를 평가하지 않는다.
ㅣㅣ논리적 ORop1 ㅣㅣ op2op1과 op2중 둘 중 하나 이상이 true인 경우 true, op1가 true이면 op2를 평가하지 않는다.
!논리적 NOT!opop가 true이면 false, false이면 true

instanceof

레퍼런스 타입 변수가 참조하고 있는 인스턴스의 실제 데이터 타입을 알아보기위해 사용합니다.

아래와 같이 사용합니다.

레퍼런스 타입 변수 instanceof 레퍼런스 타입(클래스명)

instanceof를 이용한 연산결과로 true를 얻었다는 것은 레퍼런스 타입 변수가 검사한 타입으로 형변환이 가능하다는 것을 뜻합니다.

class Parent{}
class Child extends Parent{}

class instancofTest{
	public static void main(String[] args){
		Parent parent = new Parent();
		Child child = new Child();

		System.out.println( parent instanceof Parent );
		System.out.println( child instanceof Parent );
		System.out.println( parent instanceof Child );
		System.out.println( child instanceof Child );
	}
}

----output----
true
true
false
true

할당 연산자 (assignment(=) operator)

변수에 값을 할당하는데 사용됩니다.

사용법은

변수 = 할당값

아래 예에서는 할당 연산자 (=)를 사용하여 값 10을 x라는 변수에 할당합니다.

int x = 10;
연산자예제자세히
=x = 5x = 5
+=x += 3x = x + 3
-=x -= 3x = x - 3
*=x *= 3x = x * 3
/=x /= 3x = x / 3
%=x %= 3x = x % 3
&=x &= 3x = x & 3
ㅣ=x ㅣ=3x = x ㅣ 3
^=x ^= 3x = x ^ 3
>>=x >>= 3x = x >>3
<<=x <<= 3x = x << 3

화살표(->) 연산자

람다 표현식(익명함수)에 사용되는 연산자

사용법은

사용할 매개변수 나열 → 메소드, 함수(구현)

익명 함수?

익명 함수를 이해하기 위해선 우선 익명 클래스(Anonymous class)을 이해해야 합니다.

익명 클래스(Anonymous class))?

클래스 인스턴스화 동시에 클래스를 정의하는 클래스를 의미합니다.

특정 클래스가 여러번 호출되지 않거나, 클래스 내부에 필드나 여러개의 메소드를 정의할 필요가 있는 경우 익명클래스로 정의해 사용할 수 있습니다.

interface Car {
        void move();
        void madeBy();
        void printStatus();
    }

    public static void main(String[] args) {
        Car g80 = new Car() {
            String madeBy = "Genesis";
            int status = 0;
            @Override
            public void move() {
                status += 2;
                printStatus();
            }

            @Override
            public void madeBy() {
                System.out.println("madeBy = " + madeBy);
            }

            @Override
            public void printStatus() {
                System.out.println("status = " + status);
            }
        };
    }

익명 클래스의 특징

  • new를 사용
  • 인스턴스화 시 파라미터가 없어야 함
  • interface를 implement(구현)하거나 class를 extends(확장)해아함

익명 클래스는 Inner class의 일종이기 때문에 고로 다음의 특징도 가집니다.

  • 클래스 내에 static 변수를 선언할 수 없음.(static final은 가능)
  • Outer class의 멤버변수나 scope 내의 지역변수에 접근할 수 있음

익명 클래스가 일반적인 클래스 정의 방법에 비해 간단하지만, 클래스 내부에 필드도 필요하지 않고, 딱 하나의 메소드만 존재하는 경우엔 익명 클래스 문법 조차 과하게 느껴질 수 있습니다. 이럴때 람다 표현식을 이용한다면 single method class에 대해 간편하게 처리할 수 있게됩니다.

그래서 사용법은?

FunctionalInterface f = (arg1, arg2, ...) -> {implementation};
() -> System.out.println("0 argument");
a -> {
       if (a > 0){
				 System.out.println(a);
			}
       System.out.println("single argument");
};

인자를 0개부터 받아서 implementation 부분에서 활용할 수 있고, argument list 부분의 인자가 하나라면 구현부의 소괄호를 생략할 수 있으며, statement 개수가 1개이면 중괄호를 생략할 수 있습니다.

람다식은 앞서 언급한 코드의 간결성 뿐 아니라, stream을 통한 데이터의 독립적 처리에도 많이 쓰입니다.

			String poll = priorityQ.poll();
      priorityQ.stream()
                    .forEach(s -> {
                        if (s.startsWith(poll)) {
                         System.out.print(s);
                        }
                    });

3항 연산자

저는 가독성문제로 잘 쓰지 않는 편입니다.

3항 연산자는 항이 3개라 3항이라 부릅니다.

사용법은

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

3항 연산자를 쓰지 않을 때

돈이 5000원 이상이라면 1000원을 디스카운트 해주는 메소드를 작성해봅니다.

public int discount(int money) {
        if (money > 5000) {
            return 1000;
        }
        return 0;
    }

money하나만을 검증하는데 라인이 길다고 생각할 수 있습니다. 이럴때 3항연산자를 사용합니다.

3항 연산자를 쓸 때

public int discount(int money) {
        return money > 5000 ? 1000 : 0;
    }

확실히 라인이 줄어든 것을 볼 수 있습니다. (인텔리제이에서 if에서 option + Enter를 하면 삼항 연산자로 바꿔줌)

3항 연산자 안에 또 3항 연산자를 중첩할 수 있지만, 복잡해질 수 있기 때문에 권장하지 않습니다.

연산자 우선 순위

사칙연산에도 우선순위가 있듯이, 연산자에도 우선순위가 있습니다.

https://user-images.githubusercontent.com/48986787/104847924-eb396000-5925-11eb-81a9-0f702102f36c.png

연산진행 방향또한 중요한 부분이 될 수 있으니 한번쯤 생각해볼만 합니다.

int x = 5;
int y = 10;
int z = ++x * y--;
System.out.println("x = " + x);
System.out.println("y = " + y);
System.out.println("z = " + z);

--- outputs---
x = 6
y = 9
z = 60

(optional) Java 13. switch 연산자

Java 13의 switch연산자에서는 yield키워드가 추가되어 switch 연산자 에서 값을 반환(12에서 확장됨)하게 되었습니다.

이전까지 switch는 값을 반환하지 않았습니다. 즉 현재는 처리한 결과가 존재합니다.
이는 switch 자체가 operator(연산자)로 취급된다는 것입니다.

[switch statement]

다수의 case,break가 존재하게 된다.

break; 를 빼먹을 경우 다음 분기로 넘어가게 됨.

return값이 존재할수없다.

[switch operator]

break를 사용하지 않아도 된다.

yield 존재함

return값 존재해도됨

case -> A 같은 형식으로 표현가능

switch의 반환값이 따로 필요하지 않거나 case가 switch 들어오는 모든 인자를 커버하는 경우

default 항목을 넣어주지 않아도 되나 그렇지 않은 경우는 default -> code를 작성해야 한다.

[Java12]

  1. ->(화살표) 표현이 가능하고 data만 존재할 경우 return이 가능하다.

  2. -> 구문을 사용할 경우 break;를 적지 않아도 다음 case 구문으로 넘어가지 않는다.

  3. -> 표현 오른쪽은 꼭 단일 수행일 필요는 없다. 블록 {} 안에서의 작업도 가능하다.

[Java13]

  1. yield 예약어가 추가됨. yield x 하게 되면 x가 리턴됨.

  2. yield는 예약어이지만 변수명으로 사용가능하다. int yield = 3;

break의 제거

break 구문은 Java 13에서 컴파일되지 않고, 대신 yield를 사용합니다.

    // Java 12에선 break를 통해 값을 리턴할 수 있었습니다.
        int result = switch (mode) {
            case "a":
            case "b":
                break 1;
            case "c":
                break 2;
            case "d":
            case "e":
            case "f":
                break 3;
            default:
                break -1;
        };
        return result;
    }
//break 구문은 Java 13에서 컴파일되지 않고, 대신 yield를 사용합니다. 
private static int getValueViaYield(String mode) {
        int result = switch (mode) {
            case "a", "b":
                yield 1;
            case "c":
                yield 2;
            case "d", "e", "f":
                // do something here...
                System.out.println("Supports multi line block!");
                yield 3;
            default:
                yield -1;
        };
        return result;
    }

Java 12에서 제공한 switch표현식도 함꼐 지원합니다.

private static int getValueViaArrow(String mode) {
        int result = switch (mode) {
            case "a", "b" -> 1;
            case "c" -> 2;
            case "d", "e", "f" -> {
                // do something here...
                System.out.println("Supports multi line block!");
                yield 3;
            }
            default -> -1;
        };
        return result;
    }

Java 13의 switch 연산자

public class JEP354 {

    public static void main(String[] args) {

        System.out.println(getValueViaYield("a"));
        System.out.println(getValueViaYield("c"));
        System.out.println(getValueViaYield("e"));
        System.out.println(getValueViaYield("z"));

    }

    // 기본적인 형태의 switch문
    private static int getValueBefore12(String mode) {
        int result;
        switch (mode) {
            case "a":
            case "b":
                result = 1;
                break;
            case "c":
                result = 2;
                break;
            case "d":
            case "e":
            case "f":
                result = 3;
                break;
            default:
                result = -1;
        }
        ;
        return result;
    }

    // Java 12부터 쉼표(, 콤마)를 사용하여 여러 case를 한줄에 나열
    private static int getValueMultipleLabels(String mode) {
        int result;
        switch (mode) {
            case "a", "b":
                result = 1;
                break;
            case "c":
                result = 2;
                break;
            case "d", "e", "f":
                result = 3;
                break;
            default:
                result = -1;
        }
        ;
        return result;
    }

		// break 구문은 Java 13에서 컴파일되지 않고, 대신 yield를 사용합니다. 
    // Java 12에선 break를 통해 값을 리턴할 수 있었습니다. 
    private static int getValueViaBreak(String mode) {
        int result = switch (mode) {
            case "a":
            case "b":
                break 1;
            case "c":
                break 2;
            case "d":
            case "e":
            case "f":
                break 3;
            default:
                break -1;
        };
        return result;
    }

    // Java 12부터 화살표 (arrow ->)를 사용해여 결과를 반활 할 수 있습니다. 
		// 아래 예제는 Java 13에서 사용
    private static int getValueViaArrow(String mode) {
        int result = switch (mode) {
            case "a", "b" -> 1;
            case "c" -> 2;
            case "d", "e", "f" -> {
                // do something here...
                System.out.println("Supports multi line block!");
                yield 3;
            }
            default -> -1;
        };
        return result;
    }

    // Java 13, switch expression returns a value via yield
    private static int getValueViaYield(String mode) {
        int result = switch (mode) {
            case "a", "b":
                yield 1;
            case "c":
                yield 2;
            case "d", "e", "f":
                // do something here...
                System.out.println("Supports multi line block!");
                yield 3;
            default:
                yield -1;
        };
        return result;
    }

}
profile
경험의 연장선

0개의 댓글