Chpt. 3 연산자 - 주제 1. 모든 연산자는 값을 리턴한다.
주제의 제목을 외우자.
1. 연산자와 피연산자
연산자는 연산을 수행하는 기호이다. 연산자는 연산의 대상을 필요로 하는데 이를 피연산자라고 한다.
3 + 5
위를 보면 3과 5는 피연산자이고 +는 연산자이다.
[참고]
위와 같은 예를 식이라 하고 식은 항상 결과 값을 동반한다. 반대로 문장(if, for문 등)은 결과 값이 없다.
2. 연산자의 구분
구분 | 연산자 | 설명 |
산술 연산자 | + - * / % << >> | 사칙연산, 나머지 연산 |
비교 연산자 | > < >= <= == != | 크기, 같고 다름을 비교 |
논리 연산자 | && || ! & | ^ ~ | 그리고, 또는, 반대의 조건을 나타냄 |
대입 연산자 | = | 우변을 좌변에 대입 |
기타 | (type) ?: instanceof | 형변환 연산자 (다음 주제), 삼항 연산자, instanceof 연산자 |
연산자의 종류는 위 표와 같고 강조된 부분을 유념해서 보자.
[참고]
피연산자의 개수 | 구분 | 예 |
1 | 단항 연산자 | -3, i++; |
2 | 이항 연산자 | -3 - 4 |
3 | 삼항 연산자 | result = if (score > 90) ? "Distinction" : "Average"; |
피연산자의 개수로 연산자를 구분할 수도 있다.
1) 증감 연산자 (++, --)
증감 연산자가 단독으로 쓰였을 때는 피연산자의 왼쪽에 쓰였든 오른쪽에 쓰였든 결과가 변하지 않는다. 다른 식에 포함된 경우 다음과 같이 생각하자.
System.out.println(++i); | → | ++i; // i의 값을 1 증가시킨 후 System.out.println(i); // 출력 |
System.out.println(i++); | → | System.out.println(i); // i의 값을 출력한 후 i++; // i의 값을 1 증가 |
2) 부호, 논리 부정 연산자 (-, !)
피연산자의 부호를 반대로 바꿔준다고 생각하면 된다. 비슷한 맥락으로 !도 true 또는 false의 값을 반대로 바꿔준다고 생각하자.
3) 사칙 연산자 (+, -, *, /)
다른 것보다도 같은 타입끼리의 사칙연산은 같은 타입의 결과 (Rule 1) 를 보여준다는 것을 유념하자. 따라서 실수형의 결과를 원하면 정수형 두 개 중 하나를 실수형 타입으로 형변환해야 한다.
7 / 2 = 3 (int / int = int)
7 / (float) 2 → 7 / 2f → 3.5f
이와 함께 다른 자동 형변환 규칙을 알아두자.
Rule 2. 두 피연산자의 타입 중 큰 타입으로 타입을 일치시킨다.
Rule 3. int보다 타입이 작은 byte, short, char 타입의 피연산자는 int 타입으로 변환돼 연산된다.
[예]
'2' - '0' = 50 - 48 = 2
(char - char = int - int = int)
'A' > 'B' ⇔ 65 > 66
(> 또한 연산자이기 때문에 위와 같이 정수형 타입으로 변환돼 연산된다 (Rule 3))
Rule 3.의 경우 위의 예를 참고하자.
[예 1]
byte a = 10;
byte b = 20;
byte c = a + b; // a + b의 결과는 int이기 때문에 (byte)로 형변환 해주어야 함. (Rule 3)
char ch = 'A';
ch = ch + 2; // ch + 2의 결과는 int이기 때문에 (byte)로 형변환 해주어야 함. (Rule 3)
float f = 3 / 2; // 3/2의 결과는 int이기 때문에 둘 중 하나를 (float)로 형변환 해주어야 함. (Rule 1)
long l = 3000 * 3000 * 3000; // 결과가 int이기 때문에 셋 중 하나를 (long)으로 형변환 해주어야 함. (Rule 1)
long l2 = 100_000_000 * 200_000_000; // int * int는 int이지만 결과가 int의 범위 값(약 ± 20억)을 넘어버리기 때문에 둘 중 하나의 값을 (long)으로 형변환 해주어야 함. (Rule 1)
float f2 = 0.1f;
double d = 0.1;
boolean result = d==f2; // 정확한 비교를 위해서는 타입이 일치해야 하기 때문에 (double) f2로 형변환 해주어야 함. (Rule 1)
System.out.println("c="+c);
System.out.println("ch="+ch);
System.out.println("f="+f);
System.out.println("l="+l);
System.out.println("result="+result);
위 코드는 잘못된 부분이 있다. 주석을 유념해서 보자.
4) 나머지 연산자(%)
짝수, 홀수, 배수 검사, 반복되는 수를 일정한 값으로 만드는 역할을 한다.
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
i%3 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 | 0 |
위와 같은 방법은 반복문과 함께 많이 쓰이는 형태이므로 잘 알아두자.
[예 2]
(1) 5%3 = 2
(2) -5%3 = -2
(3) 5%-3 = 2
(4) -5%-3 = -2
(1)과 같이 부호를 모두 없앤 나머지 연산의 결과에 왼쪽 부호를 붙여주면 된다. 또한 나누는 연산자는 0 포함 양의 정수만 허용한다.
5) 비교 연산자 (<, >, <=, >=, ==, !=)
다음은 비교 연산자의 사용 가능 여부에 대해 나타낸 표이다.
참조형 | 기본형 | |
대소 비교 연산자 (<, >, <=, >=) | X | O |
등가 비교 연산자 (==, !=) | O | O |
[참고] 기본형, 참조형끼리는 비교가 불가능하다.
6) 논리 연산자 (&&, ||), 논리 부정 연산자 (!)
집합의 개념과 동일하며 자주 사용되는 예로는 배수 체크, 문자 범위 확인 등이 있다. &&이 ||와 함께 쓰일 경우 &&의 우선 순위가 더 높기 때문에 헷갈릴 가능성이 높다. 따라서 그냥 원하는 연산 순서대로 괄호를 쳐주길 바란다.
[예]
ch < 'A' || ch > 'Z' ⇔ !('A' < ch && ch < 'Z')
위는 변수 ch가 대문자가 아님을 나타낸다. 왼쪽 식은 오른쪽 식과 같지만 가독성 측면에서는 오른쪽이 더 좋다.
7) 조건 연산자 (?:)
if else문과 동일하지만 조건 연산자는 결과 값을 리턴한다.
8) 대입 연산자
오른쪽 피연산자를 왼쪽에 대입할 때 쓴다는 것은 잘 알 것이다. 대입 후 저장된 값을 반환한다는 것을 알아두자.
9) 복합 대입 연산자
[예 3]
x *= 5 + y ⇔ x = x * (5 + y)
x *= 5 + y ⇎ x = x * 5 + y
3. 연산자의 우선 순위와 결합 규칙
연산자의 우선 순위와 결합 규칙은 아래 규칙을 참고하여 상식 선에서 해결하도록 하자. 그래도 확실하지 않다면 괄호를 통해 우선 순위를 정해주자.
1. 산술 > 비교 > 논리 > 대입 (우선순위)
2. 단항 > 이항 > 삼항 (우선순위)
3. 단항, 대입 연산자의 연산 진행만 오른쪽에서 왼쪽이고, 나머지는 반대이다.
4. 반올림과 버림
반올림과 버림은 연산자 활용과 위 세 가지 규칙을 적용할 수 있는 좋은 예시이다.
[예 1] 소수점 넷째자리에서 반올림하기
double pi = 3.14159
(1) Math.round(pi * 1000) // 3141.59가 된 후 Math.round를 통해 반올림하기 때문에 3142 (int) 결과값이 나온다.
(2) Math.round(pi * 1000) / 1000.0 // (1)의 결과가 정수형이기 때문에 1000.0 실수형으로 나눠주어야 형변환이 일어나고, 실수형의 결과가 나온다.
[참고] Math.round()는 소수점 첫째 자리에서 반올림한 결과를 정수형으로 반환한다.
[예 2] 소수점 넷째자리에서 버림하기
double pi = 3.14159
pi * 1000 // 3141.59
(1) (int) pi * 1000 // 3141 정수형의 결과가 나온다.
(2) (int) pi * 1000 / 1000.0 // (1)의 결과가 정수형이기 때문에 1000.0 실수형으로 나눠주어야 형변환이 일어나고, 실수형의 결과가 나온다.