Language/📕 Java

[자바/Java] 백기선 온라인 스터디 - 연산자

posted by sangmin

목표

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

학습할 것

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

산술 연산자

산술 연산자에는 사칙 연산자와 나머지 연산자가 있다. 사칙 연산자 (+, -, *, /)는 일상생활에서도 빈번히 쓰이기 때문에 주의 사항을 위주로 학습했다.

class Operator {
    public static void main(String[] args) {
        int a = 10;
        int b = 4;

        System.out.println("%d / %d = %d", a, b, a/b);                  // 10 / 4 = 2
        System.out.println("%d / %f = %f", a, float(b), a/float(b));    // 10 / 4.000000 = 2.500000
    }
}

10 나누기 4는 2.5인데, 첫 번째 출력문은 2를 출력하고 있다. 이는 두 피연산자 모두 int 타입이기 때문이다. 그래서 int 타입을 반환하기 때문에 실제 결과는 2.5일지라도 소수점을 버리고 2를 반환한다. 그래서 올바른 결과를 얻기 위해서는 두 피연산자 중 한 쪽을 실수형으로 캐스팅해줘야 한다. 그래야 다른 한 쪽도 같이 실수형으로 자동 형변환되어 원하는 값을 얻을 수 있다.

또한 피연산자가 정수형인 경우 0으로 나눌 수 없다. 만일 0으로 나눈다면 ArithmeticException 에러가, 0.0f0.0d 처럼 부동 소수점값으로 나눈다면 Infinity가 출력된다.

class Operator {
    public static void main(String[] args) {
        int a = 1_000_000;
        int b = 2_000_000;
        long c = a * b;

        long d = 1_000_000 * 1_000_000;
        long e = 1_000_000 * 1_000_000L;
        
        System.out.println(c);      // -1454759936
        
        System.out.println(d);      // -727379968
        System.out.println(e);      // 1000000000000
    }
}

그러면 c가 왜 2000000000000 이 나오지 않는지 알 수 있을 것이다. long 의 범위가 더 크지만, 연산결과가 int 이기 때문에 전혀 다른 값이 출력된 것이다. d는 오버플로우가 발생한 예이다. int 의 최대값이 *2 * 109* 이기 때문이다.

나머지 연산자(%)는 왼쪽 피연산자를 오른쪽 피연산자로 나누고 난 나머지 값을 반환한다. 기본적인 나눗셈처럼 0으로 나누는 것은 안되며, 음수로 나누는 것은 허용한다. 음수로 나눈 나머지는 부호를 무시하고 계산한 후 왼쪽 피연산자의 부호를 붙이면 된다.

System.out.println(10 % -8);    // 2
System.out.println(-10 % 8);    // -2
System.out.println(-10 % -8);   // -2

비트 연산자

비트 연산자는 피연산자를 비트단위로 논리 연산한다. 이 때 피연산자는 정수만 허용한다.

  • | (OR) : 피연산자 중 한 쪽 값이 1이면 1, 그 외에는 0
    • 특정 비트의 값을 변경할 때 사용
  • & (AND) : 피연산자 양쪽 모두 1이어야만 1, 그 외에는 0
    • 특정 비트의 값을 뽑아낼 때 사용
  • ^ (XOR) : 피연산자의 값이 서로 다를 때만 1, 같을 때는 0
    • 간단한 암호화에 사용

관계 연산자

관계 연산자는 비교 연산자라고도 불리는데, 두 피연산자를 비교할 때 사용한다. 주로 조건식에 사용되며 연산결과는 오직 true 아니면 false 이다.

  • 대소비교 연산자
    • '<' : 좌변 값이 작으면 true, 아니면 false
    • '>' : 좌변 값이 크면 true, 아니면 false
    • '<=' : 좌변 값이 작거나 같으면 true, 아니면 false
    • '>=' : 좌변 값이 크거나 같으면 true, 아니면 false
  • 등가비교 연산자
    • '==' : 두 값이 같으면 true, 아니면 false
    • '!=' : 두 값이 다르면 true, 아니면 false

참고로 자바에서 두 문자열을 비교할 때에는 '=='가 아닌 equals() 메서드를 사용해야 한다.

논리 연산자

논리 연산자는 둘 이상의 조건을 연결하여 하나의 식으로 표현할 수 있게 한다.

  • || (OR) : 피연산자 중 어느 한 쪽만 true면, 결과 true
  • && (AND) : 피연산자 양쪽 모두 true여야만 결과 true
  • ! (NOT) : 피연산자가 true면 false로, false면 true로 반환

instanceof

instanceof 는 파이썬만 사용하는 내게는 좀 생소했다. 객체 타입을 확인하는 연산자로 형변환이 가능한지 여부true / false 로 반환해준다.

class Parent {
    ...
}

class Child extends Parent {
    ...
}

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

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

Child 클래스는 Parent 클래스를 상속했다. 즉, Parent 가 더 상위 클래스이기 때문에 하위 클래스로 형변환이 불가능하다.

assignment(=) operator

assignment operator 는 변수에 값을 대입할 때 사용하는 대입 연산자이다.

int a = 10;
Child child = new Child();

단순히 변수에 값을 넣는 개념에서 확장되어 객체를 참조하기 위해 할당하는 데에도 사용된다. 또한 아래와 같은 복합 대입 연산자도 있다.

  • i += x : i = i + x
  • i -= x : i = i - x
  • i *= x : i = i * x
  • i /= x : i = i / x
  • i %= x : i = i % x

화살표(->) 연산자

화살표 연산자는 java 8 이상의 람다식에서 익명함수를 만들 때 사용한다. 람다함수 (익명함수)의 장단점은 다음과 같다.

  • 장점

    • 코드의 간결성
    • 불필요한 연산 최소화
    • 병렬처리 가능
  • 단점

    • 까다로운 호출
    • 과하게 사용하면 오히려 가독성 떨어짐
  • 람다 표현식

    • (매개변수) ->
    • () ->
      • 매개변수가 하나일 경우 생략 가능
    • (매개변수) -> 함수몸체
      • 함수몸체가 단일 실행문이면 괄호 생략 가능
    • (매개변수) ->
      • 함수몸체가 return문으로만 구성되어 있는 경우 괄호 생략 불가능
class Temp {
    ...
}

class Lambda {
    public static void main(String[] args) {
        Temp temp = new Temp() {        // 내부 익명 클래스
            @Overide
            public void print() {
                System.out.print("lambda");
            }
        }
        temp.print();
    }
}

따라서 위와 같은 코드를 아래처럼 짧게 쓸 수 있다.

class Lambda {
    public static void main(String[] args) {
        Temp temp = () -> System.out.print("lambda");
    }
}

3항 연산자

3항 연산자를 사용하면 if문 을 획기적으로 줄일 수 있다. 조건식 ? 참인 경우 : 거짓인 경우 이런 식으로 구성되는데 바로 예제를 보자.

result = (x > y) ? x : y;

xy 보다 크면 x 를 반환하고, 반대의 경우에는 y 를 반환한다는 뜻이다. 원래대로 if문 을 사용한다면 다음과 같을 것이다.

if (x > y)
    result = x;
else
    result = y;

연산자 우선 순위

연산자 우선 순위는 대부분 상식적인 선에서 해결된다. 몇 가지 예제를 보면 "그렇구나" 하고 금방 이해할 수 있다.

  • -x + 3
    • 단항 연산자가 이항 연산자보다 우선순위가 높기 때문에 x의 부호를 바꾼 다음 3을 더한다.
  • x + 3 * y
    • 곱셈과 나눗셈이 덧셈과 뺄셈보다 우선순위가 높기 때문에 3과 y를 곱한 후 x를 더한다.
  • x + 3 > y - 2
    • 산술 연산자가 비교 연산자보다 우선순위가 높기 때문에 '>' 좌항 우항을 각각 계산한 후 비교한다.
  • x > 3 && x