Post

Java Standard - 연산자(Operator)

다양한 연산자와 연산자가 동작하는 방식까지 알아보자.

Java Standard - 연산자(Operator)

연산자(Operator)

연산자(operator)
연산을 수행하는 기호, 피연산자로 연산을 수행하고 항상 결과값을 반환한다.
피연산자(operand)
연산자의 작업대상으로 연산자는 1~3개의 피연산자를 필요로 한다.
식(expression)
연산자와 피연산자를 조합해 계산하고자하는 바를 표현하는 것, 식을 계산해 결과를 얻는 것은 식을 평가한다고 한다.
대입 연산자
식을 평가해 얻은 결과를 변수와 같이 값을 저장할 수 있는 공간에 저장는 연산자

연산자의 종류

산술 연산자
사칙 연산과 나머지 연산, + - * / % << >> 비교 연산자
크고 작음, 같음과 다름을 비교, > < >= <= == !=
논리 연산자
그리고와 또는 으로 조건을 연결, && || ! & | ^ ~
대입 연산자
우변의 값을 좌변에 저장, =
기타
형변환 연산자, 삼항 연산자, instanceof 연산자, (type) ?: instanceof

개수에 의한 종류

  • 단항: 피연산자가 1개인 경우, 대표적으로 부호 연산자가 있다.
  • 이항: 피연산자가 2개인 경우, 대부분 이항 연산자 이다.
  • 삼항: 피연산자가 3개인 경우, ?: 한개 뿐이다.

연산자의 우선순위와 결합규칙

우선순위
여러 연산자를 사용했을 경우, 연산 순서를 결정하는 순위
단항 → 이항 → 삼항
산술 → 비교 → 논리 → 대입
결합규칙
같은 우선순위의 연산자가 있을 경우, 연산의 진행 방향
단항 연산자, 대입 연산자의 진행 방향
나머지 연산자의 진행 방향

산술 변환(usual arithemetic conversion)

산술 변환이란 연산 전에 피연산자 타입의 일치를 위해 자동으로 형변환 되는 것을 이야기하며, 단항 연산과 이항 연산에서 일어난다.

발생 규칙
두 피연산자의 타입을 같게 일치시킨다.(보다 큰 타입으로 일치) 값손실을 최소화하기 위해서
피 연산자 타입이 int보다 작은 경우, int로 변환 int가 가장 효율적으로 처리할 수 있는 타입이기 때문에 연산중에 오버플로우가 발생할 가능성이 높기 때문에
쉬프트 연산자<< >>, 증감 연산자++ --는 예외로 발생하지 않는다.

단항 연산자

증감 연산자 ++ , --

증가 연산자++
피연산자의 값을 1 증가시킨다.
감소 연산자--
피연산자의 값을 1 감소시킨다.
위치
일반적으로 연산자는 피연산자의 왼쪽에 위치하지만, 증감 연산자는 왼쪽과 오른쪽 위치에 따라 결과가 다르다. 독립적으로 사용된 경우에는 결과에 차이가 없다. Build source
  • 전위형: 값이 참조되기 전에 증가, ++i
  • 후위형: 값이 참조된 후에 증가, i++

식에 2번 이상 포함된 변수에 증감연산자를 사용하는 것은 피해야한다. (복잡함)

부호 연산자 + , -

부호 연산자 -
피연산자의 부호를 반대로 변경한 결과를 반환한다.
부호 연산자 +
하는 일이 없으며, - 가 있으니 형식적으로 + 를 추가해 놓은 것 뿐이다.

booleanchar을 제외한 나머지에만 사용할 수 있다.

산술 연산자

사칙 연산은 일상생활에서 사용하는 것이라 주의할 사항을 중심으로 적어보겠다.

사칙 연산자 +, -, *, /

우선 순위
*/%+-
나눗셈 연산자 /
피연산자가 정수형인경우, 나누는 수로 0을 사용하면 에러가 발생한다.
1
2
int a = 10;
int result = a / 0; // 에러 발생
두 피연산자가 int 타입인 경우, 연산결과가 int타입이기 때문에 소수점 이하는 버려진다.
1
2
3
int a = 10;
int b = 4;
System.out.println(a / b); // 연산 결과는 2.5가 아닌 2가 출력된다.
덧셈 연산자 +
피연산자가 int형보다 작을 때에는 int형으로 변환한 다음 연산을 때문에 연산 결과 역시 int형이 나오게 된다. 그래서 byte형의 덧셈을 하더라도 연산 결과는 int가 나오게 되어 byte형에 바로 대입을 할 수 없다.
1
2
3
4
byte a = 10;
byte b = 20;
// byte result = a + b; // 에러 발생
byte result = (byte) a + b;
곱하기 연산자 *
피연산자의 두개가 int타입이라면, 결과도 int타입이기 때문에 연산결과가 int의 범위를 넘는 다면(=오버플로우가 발생한다면), 잘못된 결과값을 출력한다.
1
2
3
4
int a = 1_000_000;
int b = 2_000_000;
//long result = a * b; // a * b의 결과값은 int타입이고, 이후에 자동 형변환되는 것이다.
long result = (long) a * b;
문자의 사칙연산
문자도 사칙연산이 가능한데, 문자는 문자의 유니코드 값으로 바뀌어 저장됨으로 정수간의 사칙연산과 동일하다.
연산을 할 때에는 int타입으로 자동 형변환 되어 연산이 되기 때문에 결과는 int타입이다. 코드의 가독성과 유지보수를 위해 풀어쓰는 경우가 있는데, 풀어써도 컴파일러에 의해 미리 계산되기 때문에 실행 시의 성능 차이는 없다.
1
2
3
4
5
char a = 'a';
int b = a + 1;
char c = (char) b;
//char d = a + 1; //에러 발생
char d = 'a' + 1;
  • 4에러가 발생하는 이유는, 연산 결과가 int타입이기 때문이다.
  • 5에러가 발생하지 않는 이유는, 리터럴 간의 연산이기 떄문이다.
    상수 또는 리터럴 간의 연산은 실행과정동안 변하는 값이 아니기 때문에 컴파일러가 컴파일시 그 결과로 대체함으로써 코드를 보다 효율적으로 만든다. 그렇게 때문에 연산 결과인 ‘b’ 라는 값을 변수 d에 저장한다.

나머지 연산자 %

나머지 연산자 %
왼쪽의 피연산자를 오른쪽 피연산자로 나누고 난 나머지 값을 결과로 반환하는 연산자이다.
나누는 수(오른쪽 피연산자)로 0을 사용할 수 없다.
나누는 수(오른쪽 피연산자)로 음수도 허용하지만, 부호는 무시되어 절대값으로 나눈 결과와 같다.

비교 연산자

두 연산자를 비교하는데 사용되는 연산자로, 연산의 결과는 오직 true와 false 둘 중 하나이다. 비교 연산자도 이항 연산자이므로 피연산자의 타입이 다른 경우 자료형의 범위가 더 큰 쪽으로 자동 형변환해 타입을 일치시킨 후 비교한다.

대소비교 연산자 <, >, <=, >=

값의 크기를 비교하는 연산자로, 기본형의 boolean과 참조형에서는 사용할 수 없다.

종류

>
좌변 값이 크면 true, 아니면 false
<
우변 값이 크면 true, 아니면 false
>=
좌변 값이 크거나 같으면 true, 아니면 false
<=
우변 값이 크거나 같으면 true, 아니면 false
형변환 과정 예시
'A' > 'B' —(유니코드로 변환)→ 65 > 66 —(연산)→ false

등가비교 연산자 ==, !=

값이 같은지, 다른지를 비교하는 연산자로, 모든 자료형에 사용할 수 있다.

비교 대상
기본형 - 변수에 저장된 값이 같은지를 비교
참조형 - 같은 객체를 가리키고 있는지를 비교
기본형과 참조형은 서로 형변환이 가능하지 않기 때문에, 서로를 비교할 수는 없다.

형변환 과정 예시

1
2
3
4
5
 `'A' == 'B'`  —(유니코드로 변환)→ `65 == 66` —(연산)→ `false`
 `10 == 10.0f`  —(형변환)→ `10 == 10.0f` —(연산)→ `true`
 `10.0 == 10.0f`  —(형변환)→ `10.0 == 10.0` —(연산)→ `true`
 `0.1 == 0.1f` —(형변환)→ `0.1 == 0.1` —(연산)→ `false`
 `(float)0.1 == 0.1f` —(형변환)→ `0.1 == 0.1` —(연산)→ `true`
  • 310.0f은 오차없이 저장이 가능한 값이라 double로 형변환을 해도 그대로 10.0이 된다.
  • 40.1f은 저장할 때 2진수로 변환하는 과정에서 오차가 생기기고, double형에서도 오차가 발생하지만 float형보다 더 적은 오차가 발생하기 때문에 서로 다른 값으로 저장되어 결과가 false가 나오게 된다.
  • 5앞의 예제에서는 자동 형변환이 일어난 것이라 값의 범위가 더 넓은 double로 형 변환이 되어 다른 값이 되는데, double 형을 float 형으로 형변환을 한다면 0.1이 같은 오차로 저장되기 때문에 결과가 true가 나오게 된다.

문자열을 비교시에는 == 연산자가 아닌 equals() 를 사용해야한다. == 은 같은 객체인지를 확인하는 메서드이기때문에, 문자열 내용이 같아도 false를 반환 할 수 있다.

논리 연산자

논리 연산자는 둘 이상의 조건을 그리고또는 으로 연결하여 하나의 식으로 표현 할 수 있도록해준다.

논리 연산자 &&, ||, !

AND연산 &&
그리고 에 해당하며, 두 피연산자가 모두 true일 때만 true를 결과로 얻는다.
효율적인 연산 - 좌측 피연산자가 false라면 우측 피연산자의 값은 평가하지 않는다.
OR연산 ||
또는 에 해당하며, 두 피연산자 중 한쪽만 true이여도 true를 결과로 얻는다.
우선순위는 && 연산자가 먼저지만, 한 문장에 &&|| 가 여러번 포함된 경우에는 괄호를 사용해 우선순위를 명확히 해주는 것이 좋다.
효율적인 연산 - 좌측 피연산자가 true라면 우측 피연산자의 값은 평가하지 않는다.
논리 부정 연산자 !
truefalse반대로 바꾸는 결과를 반환하며, 피연산자와 가까운 것부터 연산된다.

비트 연산자 &, |, ^, ~, «, »

  • 피연산자를 비트단위로 논리 연산을 한다.
  • 피연산자로는 정수형(문자포함)만 허용된다.
  • 산술변환이 일어날 수 있다.
OR연산 |
피연산자 중 한 쪽의 값이 1이면, 1을 결과로 얻고 그 외에는 0을 얻는다.
특정 비트의 값을 변경할 때 사용된다.
AND연산 &
피연산자 모두 1이어야 1을 결과로 얻고 그 외에는 0을 얻는다.
특정 비트의 값을 뽑아낼 때 사용된다.
XOR연산 ^
피연산자의 값이 서로 다를때만 1을 결과로 얻고 그 외에는 0을 얻는다.
간단한 암호화에 사용된다.
비트 변환 연산자 ~
피연산자를 2진수로 표현했을때 0과 1을 바꾼다. 즉, 피연산자의 1의 보수를 얻게한다.
쉬프트 연산자 << , >>
피연산자의 각 자리를 왼쪽 << 또는 오른쪽 >>으로 이동시킨다.
이동시의 빈칸은 0으로 채우고, >> 연산시 부호가 있는 정수인경우에는 1로 채운다.
산술변환이 적용되지 않는다.
x << n = x * 2ⁿ, x >> n = x / 2ⁿ
연산의 속도가 빠르다. 다만 가독성이 떨어지니 실행속도가 요구되는 곳에서만 사용하는것이 좋다.

그 외의 연산자

조건 연산자 ? :
조건식, 식1, 식2 이렇게 3개의 피연산자를 요구하는 삼항 연산자이다.
조건식에 따라 다른 결과를 반환하는데, 조건식의 결과가 true면 식1, false면 식2가 반환된다.
간단한 if문을 대신해서 쓸 수 있고, 코드가 간략해 질 수 있지만, 너무 여러변 중첩해서 사용하면 가독성이 떨어지니 꼭 필요한 경우에 한번 정도만 중첩하는 것이 좋다.
대입 연산자 =
대입 연산자는 변수와 같은 저장공간에 값 또는 수식의 연산결과를 저장하는데 사용된다.
대입 연산자는 가장 낮은 우선순위를 가지고 있다.
lvalue와 rvalue
대입연산자의 왼쪽 피연산자를 lvalue, 오른쪽 피연산자를 rvalue라고 한다.
rvalue는 변수, 식, 상수 등 모두 가능하지만, lvalue는 반드시 변수처럼 값을 변경할 수 있는 것이여야한다.
복합 대입 연산자 op=
다른 연산자(op)와 결합해 op= 와 같은 방식으로 사용할 수 있다.

Build source 복답 대입 연산자 종류

This post is licensed under CC BY 4.0 by the author.