1. 연산자 (operator)
- 연산자는 연산을 수행하는 기호를 의미한다.
- 피연산자(operand)는 연산의 대상을 의미한다.
- 피연산자로 상수, 변수 또는 식 등을 사용할 수 있다.
- 연산자는 연산을 수행하고 나면 항상 하나의 결과값을 반환한다.
- 식은 연산자와 피연산자의 조합을 의미한다.
- 식을 계산하여 결과를 얻는 것을 식을 평가한다고 한다.
- 피연산자의 개수로 연산자를 분류하기도 한다.
- 단항연산자, 이항연산자, 삼항연산자
1-1. 연산자의 우선순위와 결합규칙
- 식에 사용된 연산자가 둘 이상인 경우, 연산자의 우선순위에 의해 연산 순서가 결정된다.
- 참고로 괄호는 연산자가 아니다. 단지 우선순위를 임의로 지정할 때 사용하는 기호일 뿐이다.
1. 산술 > 비교 > 논리 > 대입. 대입이 가장 마지막에 수행된다.
2. 단항 > 이항 > 삼항. 단항 연산자의 우선순위가 이항 연산자보다 높다.
3. 단항 연산자와 대입 연산자를 제외한 모든 연산의 진행 방향은 왼쪽에서 오른쪽이다.
1-2. 산술 변환
- 이항 연산자는 두 피연산자의 타입이 일치해야 연산이 가능하다.
- 피연산자의 타입이 서로 다르다면 앞서 배웠던 형변환 연산자로 타입을 일치시켜야 한다.
- 대부분의 경우 두 피연산자 중 더 큰 타입으로 일치시킨다. 작은 타입으로 형변환하면 원래 값이 손실될 수 있기 때문이다.
- 피연산자의 타입이 int보다 작은 타입이면 int로 변환된다.
- 피연산자의 타입과 연산결과의 타입은 일치한다.
2. 단항 연산자
2-1. 증감 연산자 ++, --
- 증감 연산자는 피연산자에 저장된 값을 1 증가 또는 감소시킨다.
- 피연산자로 정수와 실수 모두 사용할 수 있지만, 상수는 값을 변경할 수 없으므로 사용할 수 없다.
- 피연산자의 왼쪽에 위치하면 전위형(prefix), 오른쪽에 위치하면 후위형(postfix)이다.
- 전위형은 값이 참조되기 전에 증가시키고, 후위형은 값이 참조된 후에 증가시킨다.
2-2. 부호 연산자 +, -
- 부호 연산자 `-`는 피연산자의 부호를 반대로 변경한 결과를 반환한다.
3. 산술 연산자
3-1. 사칙 연산자
- 피연산자가 정수형인 경우 나누는 수로 0을 사용할 수 없다.
byte a = 10;
byte b = 20;
byte c = a+b; // 에러 발생
- byte형은 연산시 int형으로 변환되어 `a+b`의 연산 결과 또한 int형인데, byte 타입의 변수에 형변환없이 저장하려고 했기 때문에 에러가 발생한다.
byte a = 10;
byte b = 30;
byte c = (byte)(a*b);
System.out.println(c); // 44
- `a*b`의 값은 300으로, byte의 저장 범위인 -128~127을 넘기 때문에 데이터 손실이 발생하여 의도하지 않은 값이 출력된다.
long a = 1_000_000 * 1_000_000;
System.out.println(a); // -1254759936
- 이번에는 큰 범위의 타입에 저장했으므로 값이 올바르게 출력될 것 같지만 그렇지 않다. 그 이유는 이미 int 타입끼리 연산을 수행하여 오버플로우가 발생한 값을 큰 범위의 타입에 저장해봤자 소용이 없기 때문이다.
- `1_000_000 * 1_000_000L`과 같이 연산을 수행해야 올바른 값을 얻을 수 있다.
- 사칙연산의 피연산자로 문자를 사용하면 유니코드로 바뀌어 연산이 수행된다. 만약 문자 '2'를 숫자로 변경하려면 문자 '0'을 빼주면 된다.
- 유니코드는 '0'~'9', 'A'~'Z', 'a'~'z'가 각각 연속적으로 배치되어 있다.
char c1 = 'a';
char c2 = c1;
char c3 = ' ';
int i = c1 + 1;
c3 = (char)(c1 + 1);
c2++;
c2++;
System.out.println(i); // 'a'+1 = 98
System.out.println(c2); // 'c'
System.out.println(c3); // 'a'+1 = 98, (char)98 = 'b'
- 증감 연산자는 형변환을 하지 않고 값만 변경한다.
char c1 = 'a';
// char c2 = c1 + 1; // 에러
char c2 = 'a' + 1;
System.out.println(c2); // b
- 위 코드에서 에러가 발생하지 않는 이유는 리터럴 간의 연산이기 때문이다.
- 상수 또는 리터럴 간의 연산은 실행하는 동안 수행되는 것이 아니라, 컴파일하는 동안 수행된다.
- 컴파일 할 때는 단순히 덧셈연산 결과인 문자 'b'를 변수 c2에 저장할 뿐이다.
char lowerCase = 'a';
char upperCase = (char)(lowerCase - 32);
System.out.println(upperCase); // 'A'
- 대문자와 소문자 간의 코드값 차이는 32이다.
float pi = 3.141592f;
float shortPi = (int)(pi * 1000) / 1000f;
System.out.println(shortPi);
// (int)(3.141592f * 1000) / 1000f
// (int)(3141.592f) / 1000f
// 3141 / 1000f
// 3.141f
- 소수점 셋째자리까지만 빼내는 예제이다.
double pi = 3.141592;
double shortPi = (int)(pi * 1000 + 0.5) / 1000.0;
System.out.println(shortPi);
// (int)(3.141592 * 1000 + 0.5) / 1000.0;
// (int)(3141.592 + 0.5) / 1000.0;
// (int)(3142.092) / 1000.0;
// 3142 / 1000.0;
// 3.142
- 소수점 넷째자리에서 반올림하는 예제이다.
double pi = 3.141592;
double shortPi = Math.round(pi * 1000) / 1000.0;
System.out.println(shortPi);
// Math.round(3.141592 * 1000) / 1000.0;
// Math.round(3141.592) / 1000.0;
// 3142 / 1000.0
// 3.142
- round 메서드는 매개변수로 받은 값을 소수점 첫째자리에서 반올림하고 결과를 정수로 반환한다.
3-2. 나머지 연산자
- 나머지 값을 결과로 반환
- 나누는 수로 0을 사용할 수 없다.
- 주로 짝/홀수 또는 배수 검사에 사용된다.
- 나누는 수로 음수도 사용할 수 있지만 부호가 무시되므로 절대값으로 나눈 나머지와 결과가 같다.
4. 비교 연산자
- 비교하는 피연산자의 타입이 서로 다를 경우 저장 범위가 큰 쪽으로 자동 형변환 후 연산이 진행된다.
- 등가비교 연산자 `==`, `!=`
- 기본형에 사용할 경우 변수에 저장되어 있는 값이 같은지 알 수 있다.
- 참조형에 사용할 경우 같은 객체를 가리키고 있는지 알 수 있다.
- 기본형과 참조형을 등가비교 연산자로 비교할 수 없다.
- 두 문자열의 내용을 비교할 때는 equals() 메서드를 사용해야 한다. `==`은 완전히 같은 문자열(객체)인지 확인하기 때문이다.
- 대소문자를 구별하지 않고 비교하고 싶다면 equalsIgnoreCase()를 사용한다.
5. 논리 연산자
- 둘 이상의 조건을 `and` 또는 `or`를 사용하여 하나의 식으로 표현할 수 있다.
- `&&`는 두 피연산자가 모두 true일 때만 true를 반환한다.
- `||`는 두 피연산자 중 어느 한 쪽만 true이어도 true를 반환한다.
5-1. 효율적인 연산
- 논리 연산자는 효율적인 연산을 한다.
- `||` 연산의 경우 좌측 피연산자가 true이면 우측 피연산자의 값을 평가하지 않는다. (항상 true)
- `&&` 연산의 경우 좌측 피연산자가 false이면 우측 피연산자의 값을 평가하지 않는다. (항상 false)
int a = 5;
int b = 0;
System.out.println(a!=0 || ++b!=0); // true
System.out.println(a==0 && ++b!=0); // false
System.out.println(b); // 0
- 위 예제에서 b의 값이 0으로 출력되는 이유는 `||` 연산에서 좌측 피연산자가 참이고, `&&` 연산에서 좌측 피연산자가 거짓이라서 두번 다 우측 피연산자를 평가하지 않았기 때문이다.
- 논리 부정 연산자는 피연산자가 true이면 false를, false이면 true를 반환한다.
6. 그 외의 연산자
6-1. 조건 연산자
- 조건 연산자는 세개의 피연산자를 필요로 하는 삼항 연산자이다.
- `조건식 ? 식1 : 식2;` 조건식이 true이면 식1이, flase이면 식2가 연산 결과가 된다.
- 결합 규칙은 오른쪽에서 왼쪽이다.
- 식1과 식2의 타입이 다른 경우, 산술 변환이 발생한다.
6-2. 대입 연산자
- 저장공간에 값 또는 수식의 연산 결과를 저장할 때 사용한다.
- 연산자들 중 우선순위가 가장 낮다.
- 복합 대입 연산자는 대입 연산자가 다른 연산자와 결합한 것이다.
- `i *= 10 + j;`는 `i = i * (10+j);`임을 주의하자.
'Java' 카테고리의 다른 글
Java :: 객체지향 II (0) | 2024.07.07 |
---|---|
Java :: 객체지향 I (0) | 2024.07.06 |
Java :: 배열 (0) | 2024.06.24 |
Java :: 제어문 (0) | 2024.06.24 |
Java :: 변수와 형변환에 대하여 (0) | 2024.06.22 |