3회차. 연산자

KIMA·2023년 1월 15일
1
post-thumbnail

목표

자바가 제공하는 다양한 연산자를 학습하기

학습할 것

연산 관련 용어 정의

  • 연산자 : 연산을 수행하는 기호
    • 예) +, -, *, /
  • 피연산자 : 연산자의 작업 대상
    • 종류 : 변수, 상수, 리터럴, 수식
    • 예) 3 + 4에서의 34
  • 연산식 : 연산자와 피연산자를 사용하여 연산의 과정을 기술한 것

산술 연산자

종류

  • + (덧셈), - (뺄셈), * (곱셈), / (나눗셈), % (나머지)
  • % 나머지 연산 이란? 나눗셈을 하고 난뒤의 나머지를 뜻한다.
    • 예) 10 % 3 = 1 // 3 * 3(몫) + 1(나머지) = 10

특징

  • 정수형끼리의 연산 후 나오는 결과의 범위도 정수형이다.
    • 예) 10 / 3 = 3
      • 정수형 끼리의 연산이므로 결과도 정수이다. 따라서 소숫점은 버려진다.
      • 기대하는 결과인 3.333 ...을 얻으려면 실수형으로 형변환하여 연산해주어야한다.
  • 두 피연산자에서 표현 범위가 더 넓은 자료형으로 타입 프로모션되어 연산이 진행된다.
    • 예) 1 + 2.3 = 1.0 + 2.3 = 3.3
  • 정수의 기본형은 int이기 때문에 정수형 끼리 연산해줄 때 피연산자가 int 미만의 자료형이라면 자동으로 int로 타입 프로모션된다.
    • 이를 산술 변환이라 부른다.
    • 이 규칙은 이항 연산일 때, 적용된다.
  • 피연산자의 자료형 범위를 넘어서는 연산 결과가 나오면 오버플로우가 발생하므로 피연산자를 충분한 범위의 자료형으로 변경해 오버플로우를 미리 방지하자.
  • 보통 수학에서는 1/0(0에 가까운 수)를 계산하면 무한대로 발산한다. 프로그래밍에서는 어떻게 될까?
    • 피연산자가 정수형이라고 가정했을 때 0으로 나눌 경우, 다음과 같은 에러를 발생시킨다.
    • 피연산자가 실수형이라고 가정했을 때 0.0으로 나눌 경우, 다음과 같은 결과를 보여준다.
    • 차이를 보이는 이유는 실수형은 정수형과는 다르게 Infinity라는 특수한 값을 지원하기 때문이다.

비트 연산자

  • 비트 연산자는 피연산자를 이진수로 표현했을 때의 각 자리마다 다음의 비트 연산을 수행한다.
    • 피연산자로 실수는 허용하지 않는다.

종류

  • | (OR) : 피연산자 중 한 쪽의 값이 1이면, 1을 결과로 얻는다. 그 외에는 0을 얻는다.
  • & (AND) : 피연산자 양 쪽이 모두 1이어야만 1을 결과로 얻는다. 그 외에는 0을 얻는다.
  • ^ (XOR, eXclusive OR, 배타적 XOR) : 피연산자의 값이 서로 다를 때만(즉, 배타적 일때만) 1을 결과로 얻는다. 같을 때는 0을 얻는다.
  • ~ (비트 전환 연산자, 1의 보수 연산자) : 0은 1로, 1은 0으로 비트를 전환 시켜준다.
  • <<, >> (shift 연산자) : 비트를 각각 왼쪽, 오른쪽으로 이동시켜준다.
    • << (왼쪽 shift)
      • 비트 이동 후에 저장범위를 벗어난 비트들은 버려진다.
      • 비트 이동 후에 빈자리는 0으로 채워진다.
    • >> (오른쪽 shift)
      • 비트 이동 후에 부호를 유지하기 위해 다음의 규칙을 따른다.
        • 음수인 경우, 빈자리를 1로 채운다.
        • 양수인 경우, 빈자리를 0으로 채운다.

사용 용도 (이해를 위해 8자리로 표현한다.)

  • | (OR) : 특정 비트의 값을 변경할 때
    • 예) 10101011 | 00001111 = 110101111 // 마지막 4bit를 'F'로 변경
  • & (AND) : 특정 비트의 값을 뽑아낼 때
    • 예) 10101011 & 00001111 = 00001011 // 마지막 4bit를 뽑아냄
  • ^ (XOR, eXclusive OR, 배타적 XOR) : 어떤 수에 똑같은 수로 XOR 연산을 두번 수행하면 원래의 값으로 돌아오는 특성을 이용하여, 간단한 암호화에 쓰인다.
  • shift 연산자 : 어떤 수에 2*n을 곱하거나 나눌 때 사용
    • x << n // x * 2^n
    • x >> n // x / 2^n
    • 곱셈이나 나눗셈 연산자를 사용할 때보다 속도가 빠른 것이 장점이나 가독성이 떨어진다는 단점이 있다. 용도에 맞게 사용하자.

특징

  • 모든 비트 연산자들은 산술 변환이 적용된다.
    • shift 연산자의 경우, 왼쪽 피연산자에만 적용된다.
  • 비트 연산의 결과를 보고 싶다면, toBinaryString()을 이용하자.
  • 비트(bit) : 0 or 1

❓ 산술 변환이란?

  • int보다 작은 타입은 int타입으로 자동 변환

관계 연산자

  • 두 피연산자를 비교하는데 사용된다.
  • 연산결과는 true, false 중 하나이다.

종류

  • 대소비교 연산자 : <, >, <=, >=
  • 등가비교 연산자 : == (두 값이 같은가?), != (두 값이 다른가?)

용도

  • 주로 조건문과 반복문의 조건식에 사용된다.

특징

  • 대소비교 연산자는 기본형 중에 boolean을 제외하고 모두 사용가능하지만, 참조형에는 사용할 수 없다.
  • 등가비교 연산자는 모든 자료형에서 사용할 수 있다.
    • 참조형의 경우 객체의 주소값을 저장하고 있어 주소값으로 값을 비교한다.
  • 모든 관계 연산자들은 이항 연산자이므로 산술 변환이 적용된다.

논리 연산자

종류

  • || or | : 피연산자 중 한 쪽의 값이 true이면, true를 결과로 얻는다. 그 외에는 false을 얻는다.
    • ||는 첫번째 조건이 참이면 두번째 조건은 확인하지 않는다.
    • | 는 첫번째 조건이 참이어도 두번째 조건을 확인한다.
  • && or & : 피연산자 양 쪽이 모두 true이어야만 true를 결과로 얻는다. 그 외에는 false을 얻는다.
    • &&는 첫번째 조건이 참이 아니면 두번째 조건은 확인하지 않는다.
    • &는 첫번째 조건이 참이 아니어도 두번째 조건을 확인한다.
  • ! : truefalse로, falsetrue로 전환 시켜준다.

용도

  • 주로 조건문과 반복문의 조건식에 사용된다.

instanceof

레퍼런스 변수 instanceof 타입(클래스 or 인터페이스)

  • 왼쪽 레퍼런스 변수가 오른쪽에 명시한 타입인지 아닌지 확인한다.
  • 반환 값은 boolean이다.

용도

  • 형변환이 가능한지 확인하기 위해
class Parent {}
class Child extends Parent {}

Parent parent = new Child();

System.out.println(parent instanceof Child);  //true
System.out.println(parent instanceof Parent); //true
System.out.println(parent instanceof Object); //true

if(parent instanceof Parent) {
  Parent parentInstance = (Parent) parent;
}

assignment(=) operator

lvalue = rvalue

  • 변수(lvalue, left value)에 값 또는 수식의 연산 결과(rvalue, right value)를 저장하는데 사용한다.

특징

  • 연산자 중 가장 낮은 우선순위를 가지고 있어 가장 나중에 수행된다.
  • 진행 방향은 우측에서 좌측으로 진행된다.
    • 예) x = y = z = 3 // z = 3 -> y = z -> x = y

복합 대입 연산자

a operator= b // a = a operator b와 같다.

3항 연산자

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

  • 가독성 문제로 잘 사용하지 않는다.
  • 중첩이 가능하다.

연산자 우선 순위

출처 : 자바의 정석 3rd Edition, 남궁성 지음

  • 산술 > 비교 > 논리 > 대입
  • 단항 > 이항 > 삼항
  • 단항/대입 연산자를 제외한 연산자들의 진행 방향 : 좌측 -> 우측
    • 단항/대입 연산자 : 우측 -> 좌측

🤯 이걸 언제 다 외워?

  • 사용하면서 저절로 외워지기 때문에, 처음에 연산자 우선순위를 완벽히 외우는 것을 추천하지 않는다.
  • 그리고 괄호를 적지 않으면 다른 사람이 보기에 무척이나 헷갈린다.
  • 따라서 괄호의 우선순위가 가장 높으므로, 괄호로 가독성을 최대한 확보하자!
  • 또한, 연산자들을 여러 줄로 나눠서 적으면 가독성이 올라간다!

화살표(->) 연산자

나오게된 배경 : 함수형 인터페이스

: 추상 메서드가 단 1개인 인터페이스

  • 람다식이 나오기전에 익명 내부 클래스를 만들어 함수형 인터페이스를 사용하는 방법

    @FunctionalInterface // 인터페이스가 두 개 이상의 추상 메서드를 가질 경우, 컴파일 에러
    interface Calculator {
      int cal(int a, int b);
    }
    
    Calculator calculator = new Calculator() {
      @Override
      int cal(int a, int b) {
        return a + b;
      }
    };
  • 단점 : 코드가 길어 가독성이 떨어짐

  • 해결방법 : 람다식

람다식

  • 기존의 메서드에서 메서드의 이름, 반환 타입을 없애고 람다식(화살표 연산자)으로 표현하면 익명함수(anonymous function)이라 불린다.
    • 즉, 메서드를 하나의 식(expression)을 표현할 수 있다.
    Calculator calculator = (a, b) -> a + b;
    calculator.cal(1, 2);
  • java8 이후 나온 기술이다.

특징

  • 함수를 일급 객체로 사용할 수 있다.
    • 함수를 메서드의 인수, 반환 타입, 변수로 만들어 사용이 가능하다.
    • 즉, 고차 함수(Higher - Order Function)이다.

🤔 아직 헷갈리는 부분이 많지만, 나중에 람다식 배울 때 제대로 해보자!

Java 13. switch 연산자

  • switch문은 if문에서 조건에 따라 분기해야 할 내용이 많아졌을 때, 가독성과 속도를 향상시키기 위한 목적으로 사용한다.

  • 자바 12 버전부터 switch문에 대해 바뀐 부분이 있어 이에 대해 알아보자.

  • 기존 switch 문이다.

    int result;
    switch (str) {
      case "a":
        result = 1;
        break;
      case "b": case "c":
        result = 2;
        break;
      default:
        result = -1;
    }
  • 자바 12 버전 - ,(콤마)를 사용하여 여러 case를 한 줄에 나열한다.

    int result;
    switch (str) {
      case "a":
        result = 1;
        break;
      case "b", "c":
        result = 2;
        break;
      default:
        result = -1;
    }
  • 자바 12 버전 - switch operator가 람다식을 통해 결과를 반환할 수 있다.

    • switch fall through가 존재하지 않아 break를 쓰지 않아도 된다.
    int result = switch (str) {
      case "a" -> 1;
      case "b", "c" -> 2;
      default -> -1;
    };
  • 자바 13 버전 - switch operator가 yield 키워드를 사용하여 결과를 반환할 수 있다.

    • 람다식을 비롯하여 기존의 :를 이용한 switch문에서도 사용가능하다.
    int result = switch (str) {
      case "a"-> 1;
      case "b", "c" -> {
        System.out.println("{} 블록으로 멀티 라인 수행");
        yield 2;
      }
      default -> -1;
    };
    
    int result = switch (str) {
      case "a":
        yield 1;
      case "b", "c": {
        System.out.println("{} 블록으로 멀티 라인 수행");
        yield 2;
      }
      default:
        yield -1;
    };

Reference

profile
안녕하세요.

0개의 댓글