Java/Java

Java - Java 연산자

Dlise 2023. 2. 9. 20:16

Java 연산자에 대해 알아보자.

 

Java 연산자 우선순위

모든 언어가 그렇듯 연산자엔 우선순위가 있다.

아래 사진은 Wiwkipedia에 올라와 있는 Java의 연산자 우선순위이다.

정리하면 Java 연산자는

- 산술 > 비교 > 논리 > 대입 순으로 수행된다.

- 단항 > 이항 > 삼항 연산자 순으로 우선순위가 높다.

- 단항 연산자와 대입 연산자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽이다.

 

예를 들어 y = - x + 2라는 수식에 대해 단항연산자인 -가 먼저, 이항연산자인 +가 두 번째로, 대입 연산자인 =가 마지막으로 수행된다.

글로 표현하면 어렵게 느껴지지만 실제 우리가 연산하는 순서와 비슷하다. 

또, 헷갈릴만한 부분은 괄호로 묶음으로써 해결할 수 있다.

 

각 연산자에 대해 알아보자.

 

증감 연산자(++, --)

증감 연산자는 ++, -- 2가지가 있다.

++는 증가 연산자로 변수의 값을 1 증가시키며,

--는 감소 연산자로 변수의 값을 1 감소시킨다.

 

증감 연산자는 변수에 붙는 위치에 따라 전위형후위형으로 나눠지는데,

전위형은 ++x 와 같이 변수의 왼쪽에,

후위형은 x++와 같이 변수의 오른쪽에 붙는다.

 

둘의 차이는 값을 언제 증가시키는가이다.

간단한 코드를 예로 들어보자.

x는 후위형, y는 전위형일 때의 출력 결과이다.

x는 1, y는 2이다.

이러한 결과가 나오는 이유는

전위형은 변수의 값을 먼저 증가시킨 후 해당 값을 이용하며,

후위형은 변수의 값을 이용한 후 값을 증가시키기 때문이다.

 

감소 연산자(--)도 1을 감소시킬 뿐 내용은 동일하다.

 

해당 연산자는 boolean형 이외의 모든 기본형 변수에 사용할 수 있다.

즉, 실수형 변수에도 사용할 수 있는데, 

소수부는 유지되고 정수부가 1 증감했음을 볼 수 있다.

 

i = i+1과 ++i의 차이점
i = i+1과 ++i는 i의 값이 1 증가한다는 동일한 결과를 보인다. 그렇다면 이 둘의 차이점은 무엇일까?

결과부터 말하자면 ++i가 i = i+1보다 속도가 더 빠르다.

그 이유는 위의 두 코드가 컴파일되어 바이트코드로 변환되었을 때 명령 개수 차이가 있기 때문인데,
++i는 2번의 명령, i = i+1은 5번의 명령이 필요하다.

 

부호 연산자(+, -)

부호 연산자는 boolean, char형 이외의 기본형에 사용할 수 있다.

우리가 현실에서 사용하는 부호와 동일하며, 각각 +1, -1을 곱한 것과 같다.

 

따라서 다음과 같이 음수인 값에 + 부호를 붙여도 결과는 음수이다.

 

비트 전환 연산자(~)

비트 전환 연산자(~)는 정수형(byte, short, int, long)과 char형에만 사용할 수 있다.

해당 연산자는 비트 값이 0이면 1, 1이면 0으로 바꾸는 작업을 한다.

 

예를 들어 다음과 같이 byte형 변수가 있다고 할 때, ~x는 -2로 바뀐다. 

 

비트를 직접 보자.

처음 byte x = 1;는 아래와 같이 저장된다.

여기서 비트 전환 연산을 하면 0은 1, 1은 0이 되므로 아래와 같이 바뀐다.

이때, MSB(Most Significant Bit)가 1이므로 해당 값은 음수가 되며 결과는 -2인 것이다.

즉, 1의 보수가 된 형태이다. 해당 주제에 대해 자세한 내용은 아래서 확인할 수 있다.

https://dlise.tistory.com/12

 

주의할 점!
byte, short형은 컴파일 시 JVM에 의해 int형으로 바뀐다. 즉, 위의 byte 변수도 실행 시 int형으로 바뀐다는 것이다.
해당 오류로 알 수 있는데, byte형에 byte형을 넣어서 정상 동작할 것 같지만 컴파일 시 byte x가 int형으로 바뀌기 때문에 byte에 int를 넣는 꼴이 된다. 따라서 빨간 줄이 그어진 것이다. 

해당 문제를 해결하기 위해선 아래와 같이 byte형으로 형변환을 하거나 int형으로 받아야 한다.

 

논리 부정 연산자(!)

논리 부정 연산자는 boolean형에만 사용할 수 있는 연산자이다.

true는 false, false는 true로 바꾸는 동작을 한다.

 

ture였던 x에 논리 부정 연산을 했을 때 false가 됨을 알 수 있다.

 

 

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

사칙 연산자 들어가기 전!

이항 연산자부터는 피연산자의 자료형을 중요하게 봐야 한다.
그 이유는, 연산을 수행하기 전 

1. 크기가 4byte 이하인 자료형은 int형으로 변환하기 때문이다.(byte, char, short)
즉, byte + byte는 int + int로 변환한 후 연산을 수행해 int형 결과가 나온다.

2. 피연산자의 타입을 일치시키기 때문이다.
이때, 더 작은 크기의 타입이 큰 크기의 타입으로 바뀐다. 
예를 들어 int + long는 long + long로 변환되어 long형 결과가 나온다.
(int + float은 float + float, float + double은 double + double로 변한다.)

예로, 위 코드는 byte + byte는 int형인데, byte 변수에 담으려고 해서 오류가 발생한 것이다.
그럼 byte로 형변환을 해보자.

또 오류가 발생했다. 그 이유는 연산자 우선순위에 의해 +연산보다 (byte) 연산이 먼저 수행되기 때문이다.
따라서 아래와 같이 괄호로 묶어야 한다.

위의 변환 때문에 Java의 사칙 연산은 모르고 사용했다가 이상한 값이 나올 가능성이 크다.

예를 들어 

a * b는 1000000000000이다. 이 값은 long이 수용할 수 있는 값이기 때문에 올바르게 나올 것 같지만 결과는 아래와 같다.

오버플로우가 발생한 것인데, 그 이유는 int * int의 결과는 int이기 때문이다.

즉, a * b의 결과가 int형으로 나온 후 long에 담긴 것이다.

이를 해결하기 위해선 아래처럼 아무 변수나 long형으로 바꾸면 된다.

int와 long의 연산은 long으로 결과가 나오기 때문이다.

 

 

 

이와 비슷한 예로

해당 연산의 결과도 아래와 같이 나온다.

위의 예와 비슷한데, int * int의 결과가 int로 나왔고, 그 값이 long a에 담긴 것이기 때문이다.

해결하려면 아래처럼 L을 붙여 long형임을 알리면 된다.

 

 

나머지 연산자(%)

나머지 연산자는 나머지를 반환하며  boolean형 이외의 자료형에 사용할 수 있다.

아래 연산은 정수일 때로 5를 3으로 나눴기에 2가 나왔음을 알 수 있다.

실수일 때에도 정상 동작한다.

 

 

나머지 연산자는 음수의 영향을 받는데, 아래와 같이 연산을 수행하면 

이러한 결과가 나온다.

즉, 나머지 연산자는 왼쪽 피연산자의 부호에만 영향을 받는다. 

 

 

쉬프트 연산자(>>, <<, >>>)

쉬프트 연산자는 정수형 변수에만 사용할 수 있다.

이 연산자는 변수의 비트를 왼쪽 혹은 오른쪽으로 이동시키는 연산을 한다.

 

예를 들어 >> 연산자의 경우 오른쪽으로 이동시킨다.

아래와 같이 4 >> 1은 오른쪽으로 1번 이동시킨다는 것으로 결과는 2가 나온다.

비트의 이동을 보면 아래 그림과 같다.

가장 오른쪽에 있던 비트는 사라진다. 따라서 만약 4 >> 3을 한다면 결과는 0이 나온다.

 

반면 << 연산자는 왼쪽으로 이동시킨다.

아래와 같이 4 << 1은 왼쪽으로 1번 이동시킨다는 것으로 결과는 8이 나온다.

비트의 이동을 보면 아래 그림과 같다.

 

피연산자가 양수일 땐 큰 단순히 옮기기만 하며, 빈자리는 0으로 채운다.

하지만 음수일 땐 오른쪽으로 이동할 때 다르게 동작한다.

 

먼저 아래와 같이 왼쪽으로 옮기는 -4 << 1을 실행하면 -8이 나오는데,

아래와 같이 동작하기 때문이다. 

만약, -4 >> 1이라면 결과는 -2이다.

이것의 비트 이동을 보면 아래와 같다.

다를 것이 없어 보이지만 음수임을 감안해 MSB가 0이 아닌 1로 채워진다. 그래야 계속 음수이기 때문이다.

계속 1로 채워지기 때문에 음수를 아무리 오른쪽 쉬프트 연산을 해도 결국 결과는 -1이다.

아래와 같이 모든 비트가 1로 채워지기 때문이다.

 

하지만 >>> 연산은 다른데, 음수일 경우에도 MSB를 0으로 채운다.

따라서 결과는 아래와 같이 양수가 나온다.

int형의 최댓값이 2147483647 임을 통해 MSB와 LSB를 제외한 모든 비트가 1로 채워졌음을 알 수 있다. 

 

만약 아래와 같이 연산을 수행하면 int형 최댓값이 나온다. MSB를 제외한 모든 비트가 1이기 때문이다.

 

쉬프트 연산을 정리하자면

<< 연산은 2의 x제곱만큼 곱한 것이며,

>> 연산은 2의 x제곱만큼 나눈 것이다.

 

쉬프트 연산은 곱셈보다 빠르다. 즉, 8 / 4보다 8 >>2가 더 빠르다는 것이다.
하지만 이는 가독성이 떨어지므로 *, / 를 사용하는 것을 권장한다.

 

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

boolean, 참조형을 제외한 기본 자료형에 사용할 수 있다.

두 피연산자의 크고 작음을 비교해 true, false를 반환한다. 따라서 if문, while문에 주로 사용한다.

 

 

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

대소 비교 연산자와 같이 true, false를 반환한다. 차이점은 모든 자료형에 사용할 수 있다는 것이다.

아래 코드에서 빨간 줄이 없는 것을 볼 수 있다.

(노란 줄은 동일한 표현, 없어도 되는 코드라는 의미)

단, 참조형과 기본형은 자료형을 통일할 수 없기 때문에 동시에 사용할 수 없다.

 

 

논리 연산자(&&, ||)

논리 연산자는 true, false만 피연산자로 받는다.

&&는 피연산자가 둘 다 true여야 true를, ||는 둘 중 하나만 true면 true를 반환한다.

해당 연산자들은 표로 정리하면 확실히 알 수 있다.

&&는 둘 중 하나가 false면 false, ||는 둘 중 하나가 true면 true이다.

따라서 컴퓨터는 해당 연산을 할 때 앞 피연산자가 무엇인가에 따라 뒤 피연산자는 계산하지 않는다.

 

예를 들어 (5 > 3) || (4 > 2) 연산을 할 때

5 > 3이 ture이므로 4 > 2의 결과에 상관없이 전체 결과는 true이다.

 

또, (5 < 3) && (4 < 2) 연산을 할 때

5 < 3이 false이므로 4 < 2의 결과에 상관없이 전체 결과는 false이다.

 

이런 경우, 뒤의 연산은 수행하지 않는다.

이것을 Short Circuit Rule이라고 한다.

 

따라서 ||일 땐 true일 가능성이 높은 것을, &&일 땐 false일 가능성이 높은 것을 앞에 배치한다면

조금이나마 속도를 높일 수 있다.

 

 

비트 연산자(&, |, ^)

비트 연산자는 두 피연산자의 비트를 대상으로 하는 연산으로, double, float을 제외한 기본형에 사용할 수 있다.

(boolean도 가능)

 

&연산은 두 피연산자의 비트가 1이면 1, 아니면 0을 반환 (AND 연산)

| 연산은 두 피연산자의 비트가 하나라도 1이면 1, 아니면 0을 반환 (OR 연산)

^연산은 두 피연산자의 비트가 다르면 1, 같으면 0을 반환한다. (XOR 연산)

 

예를 들어 아래와 같은 코드가 있다고 할 때, 

결과는 다음과 같다.

x,  y의 비트가 아래와 같으므로 

결과는 다음과 같다.

 

아래와 같이 boolean형에도 사용할 수 있다.

결과는 아래와 같다.

true는 1, false는 0이라고 생각하면 쉽다.

&, | 는 &&, ||와 달리 항상 양 쪽의 피연산자를 모두 검사한다.

 

삼항 연산자

삼항 연산자는 이름에서 알 수 있듯 세 가지 피연산자를 사용한다.

형태는 (조건식)? 식1 : 식2이며 if문을 보다 짧게 쓴다는 느낌이다.

만약 조건식이 true면 식1을, false면 식2를 수행한다.

 

예를 들면 아래와 같으며 

아래의 if문과 동일한 역할을 수행한다.

참고로 x > 5가 true이므로 10이 출력된다.

 

대입 연산자(=, op=)

대입 연산자는 위에서 실컷 쓴 연산자로 

x = y 일 때, y의 값을 x에 대입하는 연산자이다.

 

해당 연산자는 우선순위가 가장 낮다.

변수 앞에 final을 붙이면 더 이상 값을 바꿀 수 없다.

 

x += 1;과 같이 사용할 수 있다.

x *= y + 1; 은 x = x * (y + 1)과 같다.