영권's

3주차 과제: 연산자 본문

스터디/백기선 라이브 스터디(자바)

3주차 과제: 연산자

ykkkk 2021. 1. 18. 17:40

목표

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

학습할 것

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

 

 

프로그램에서 데이터를 처리하여 결과를 산출하는 것을 연산이라고 한다.

연산에서 사용되는 표시나 기호를 연산자라고 하고, 연산되는 데이터는 피연산자라고 한다.

 

자바에서 제공하는 연산자

 

우리가 일반적으로 말하는 사칙연산인 더하기(+),빼기(-),곱하기(*),나누기(/)와 나머지를 구하는 연산자(%)를 포함해서 산술 연산자는 총 5개이다.

이 산술 연산자는 boolean 타입을 제외한 모든 기본 타입에 사용할 수 있다.

 

여기서 주의할 점은 나눗셈의 경우 /를 사용하며 %는 나눗셈의 몫이 아닌 나머지를 구하는 연산자이다.

ex) int result = num % 3 ; // 일 때 result가 들어올 수 있는 값은 0,1,2 중 하나가 된다.

 

산술 연산자의 특징은 피연산자들의 타입이 동일하지 않을 경우 다음과 같은 규칙을 사용해서 피연산자들의 타입을 일치시킨 후 연산을 수행한다.

1. 피연산자들이 모두 정수 타입이고, int 타입(4byte)보다 크기가 작은 타입일 경우 모두 int 타입으로 변환 후, 연산을 수행한다. 따라서 연산의 산출 타입은 int이다.

ex) byte + byte -> int + int = int

2. 피연산들이 모두 정수 타입이고, long 타입이 있을 경우 모두 long 타입으로 변환 후, 연산을 수행한다. 따라서 연산의 산출 타입은 long이다.

ex) int + long -> long + long = long

3. 피연산자 중 실수 타입이 있을 경우, 크기가 큰 실수 타입으로 변환 후, 연산을 수행한다. 따라서 연산의 산출 타입은 실수 타입이다.

ex) int + double -> double + double = double

 

간단하게 정리하면 long을 제외한 정수 탕비 연산은 int 타입으로 산출 되고, 피연산자 중 하나라도 실수 타입이면 실수 타입으로 산출된다. 

 

위 내용을 따르면 다음 코드는 컴파일 에러가 난다.

 

byte byte1 = 1;

byte byte2 = 1;

byte byte3 = byte1 + byte2;

 

상식적으로 생각하면 byte1 과 byte2를 덧셈하면 2가 나오고, 2는 1byte로 표현 가능한 수 이므로 byte 타입 변수인 byte3에 저장할 수 있을 것 같다. 하지만 컴파일 에러가 나는데, long 타입을 제외한 정수의 산술 연산은 무조건 int타입으로 변환 후 연산을 수행하고, 산출 타입이 int이기 때문에 int 타입 변수에 산출값을 대입해야 한다. 따라서 다음과 같이 수행해야 한다.

int result1 = byte1 + byte2;

 

정수 타입 연산의 결과가 int타입으로 나오는 이유는 JVM이 기본적으로 32비트 단위로 계산하기 때문이다.

 

다른 예로 

int int1 = 10;

int int2 = 4; 

int result 2 = int1 / int2;

double result3 = int1 / int2;

에서 10 / 4 는 2.5가 나오는데 연산 결과는 소수점 이하 부분을 버리고 2만 산출된다. 따라서 result2에는 2가 저장된다.

result3는 double 타입 변수이므로 2.5가 저장될까? 아니다. 연산 후 결과가 2이므로 2를 실수화 하여 2.0이 저장된다.

만약 2.5를 산출 결과로 얻고 샆다면 다음과 같이 피연산자 중 하나는 실수 타입이어야 한다.

double result3 = (int * 1.0) / int2;

double result3 = (double) int1 / int2;

double result3 = int1 / (double) int2;

 

산술연산에서 올바른 계산을 위해 값을 미리 검정해야 하고, 정확한 계산을 위해 실수 타입을 피해야 하며, 특수 값 처리에 신경써야 한다.

 

오버플로우 탐지

산술 연산을 할 때 주의할 점은 연산 후의 산출값이 산출 타입으로 충분히 표현 가능한지 확인해야한다.

산출 타입으로 표현할 수 없는 값이 산출 되었을 경우 오버플로우가 발생하고 쓰레기값(엉뚱한 값)을 얻을 수 있기 때문이다.

 

정확한 계산

정확한 계산이 필요할 댸는 실수 타입을 사용하지 않는 것이 좋다.

실수로 계산을 하다보면 예상 값은 0.3인데 실제 출력되는 값은 0.2999999999993이 나오는 것을 볼 수 있는데 이것은 이진 포맷의 가수를 사용하는 부동소수점 타입은 0.1을 정확히 표현할 수 없어 근사치로 처리하기 때문인데 이럴 때는 정수로 바꾸어 계산을 하거나 BigInteger, BigDecimal 를 사용하는 것이 좋다.

 

비트연산자

 

비트 연산자는 데이터를 비트(bit) 단위로 연산한다. 즉 0과 1이 피연산자가 된다. 그렇기 때문에 0과1로 표현이 가능한 정수만 비트 연산을 할 수 있다.

비트 연산자는 기능에 따라 비트 논리 연산자(&,|,^,~)와 비트 이동 연산자(<< , >>, >>>)로 구분한다. 일반 논리 연산자가 true와 false를 연산한다면 비트 논리 연산자는 0과 1을 연산한다. 비트 이동 연산자는 비트를 좌측 또는 우측으로 이동하는 연산자이다.

 

비트 논리 연산자의 종류와 기능

 

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

비교 연산자는 대소(<,<=,>,>=), 동등(==,!=)을 비교해서 boolean 타입인 true/false를 산출한다. 대소 연산자는 boolean 타입을 제외한 기본 타입에 사용할 수 있고, 동등 연산자는 모든 타입에 사용할 수 있다.

 

String 타입의 문자열을 비교할 때는 대소 연산자를 사용할 수 없고 동등 연산자를 사용할 수 있으나 문자열이 같은지 다른지를 비교하는 용도로는 사용하지 않는다.

 

Ex)

String strVar1 = "test";

String strVar2 = "test";

String strVar3 = new String("test");

 

자바는 문자열 리터럴이 동일하다면 동일한 String 객체를 참조하도록 되어있다. 그래서 변수 strVar1 과 strVar2는 동일한 String 객체의 번지 값을 가지고 있다 하지만 변수 strVar3는 객체 생성 연산자인 new로 생성한 새로운 String 객체의 번지 값을 가지고 있다.

이 경우 strVar1과 strVar2의 == 연산은 true를 산출하고 strVar2와 strVar3의 == 연산은 false를 산출한다.

== 연산자는 변수에 저장된 값만 비교하기 때문에 이러한 결과가 나오는 것인데 동일한 String 객체이건 다른 String 객체이건 상관없이 String 객체의 문자열 만을 비교하고 싶다면 == 연산자 대신에 equals() 메소드를 사용해야 한다.

 

논리연산자

논리 연산자는 논리곱(&&),논리합(||),배타적 논리합(^), 그리고 논리 부정(!) 연산을 수행한다.

논리 연산자의 피연산자는 boolean 타입만 사용할 수 있다.

논리 연산자의 종류와 기능

&&와 &는 산출 결과는 같지만 연산 과정이 조금 다르다.

&&는 앞의 피연산자가 false라면 뒤의 피연산를 평가하지 않고 바로 false라는 결과를 낸다. 왜냐하면 하나라도 false라면 전체 연산식은 false이기 때문이다.

그러나 &는 두 피연산자 모두를 평가해서 산출 결과를 낸다. |와 || 도 마찬가지이다.

 

instanceof

참조 변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof 연산자를 사용합니다.

주로 조건문에 사용되며, instanceof의 왼쪽에는 참조변수를 오른쪽에는 타입이 피연산자로 위치합니다.

그리고 연산결과로 boolean값를 반환합니다.

 

assignment(=) operator

대입 연산자는 오른쪽 피연산자의 값을 좌측 피연산자인 변수에 저장한다.

오른쪽 피연산자는 리터럴 및 변수, 그리고 다른 연산식이 올 수 있다.

대입 연산자는 오른쪽 피연산자의 값을 변수에 저장하는 단순 대입 연산자가 있고, 정해진 연산을 수행 후 결과를 변수에 저장하는 복합 대입 연산자도 있다.

단순 대입 연산자

 

복합 대입 연산자

 

화살표(->) 연산자

 

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

 

함수형 프로그래밍 언어에서 주로 사용되던 문법이며 유연성이 좋아 모던 프로그래밍 언어에서도 많이 사용합니다.

메소드 파라미터 목록, 연산자, 코드 블럭 순으로 구성 됩니다.

 

코드 블럭이 한 문장으로 끝난다면 중괄호({, })를 생략할 수 있습니다.

 

3항 연산자(?:)

삼항 연산자는 세 개의 피연산자를 필요로 하는 연산자를 말한다.

삼항 연산자는 ? 앞의 조건식에 따라 콜론(:) 앞뒤의 피연산자가 선택된다고 해서 조건 연산식이라고 부르기도 한다.

 

삼항 연산자 사용법

 

연산자 우선 순위

연산자 우선순위

이렇게 우선순위와 연산방향이 정해져 있다 하더라도 여러가지 연산자들이 섞여있다면 어느 연산자가 먼저 처리될지 헷갈리는데 그래서 괄호()를 사용해서 먼저 처리해야 할 연산식을 묶는 것이 좋다.

 

(optional) Java 13. switch 연산자

 

java13에서 달라진 switch 문법 중 yield 키워드를 사용하여 switch 표현식을 리턴할 수 있습니다.

또한 ->를 사용하여 case 구문 처리가 가능합니다.

 

int result;

switch (mode) {
    case "a":
        result = 1;
        break;
    case "b":
        result = 2;
        break;
    case "c", "d", "e":
        result = 3;
    case "f", "g":
        System.out.println("this is f or g");
        result = 4;
        break;
    default:
        result = -1;
}

위와 같은 switch 문을 yield를 사용한다면 아래와 같습니다.

 

int result = switch (mode) {
    case "a":
        yield 1;
    case "b":
        yield 2;
    case "c", "d", "e":
        yield 3;
    case "f", "g":
        System.out.println("this is f or g");
        yield 4;
    default:
        yield -1;
};

문법이 간결해지고 result 변수의 변화가 없기 때문에 안전하게 코딩할 수 있습니다.

->를 사용한다면 조금 더 간단하게 변경할 수 있습니다.

 

 

int result = switch (mode) {
    case "a" -> 1;
    case "b" -> 2;
    case "c", "d", "e" -> 3;
    case "f", "g" -> {
        System.out.println("this is f or g");
        yield 4;
    }
    default -> -1;
};

case문 별로 한 문장으로 리턴이 가능하다면 yield까지 생략할 수 있습니다.

두 문장 이상이 된다면 중괄호로 처리하고 yield문을 사용해야 합니다.

 

 

참고 : 

도서) 이것이 자바다

 

blog.baesangwoo.dev/posts/java-livestudy-3week/#%ED%99%94%EC%82%B4%ED%91%9C-%EC%97%B0%EC%82%B0%EC%9E%90-lamda-expression---

Comments