사칙 연산자(+
, -
, *
, /
)와 나머지 연산자(%
)로 구성.
수학의 사칙 연산과 같다. 피연산자가 정수형인 경우 0으로 나눌 수 없음. 부동소수형 값인 0.0f나 0.0d로 나누면 Infinity
가 반환됨.
📌 [주의] 피연산자의 타입에 따라 연산 결과가 예상과 달라질 수 있음.
int a = 7;
int b = 2;
System.out.println(a / b); // 3.5가 아닌 3이 출력된다.
위 예시처럼 int
끼리 나눗셈을 하면 결과값도 정수형인 int
이기 때문에 소수점 아래 값을 표기할 수 없어 버려짐. (반올림이 아님에 주의)
byte a = 17;
byte b = 27;
byte c = a + b; // 컴파일 에러 발생
위 예시에서 컴파일 에러가 발생한 이유는 a + b
값이 int
형인데 byte
형으로 선언한 변수 c
에 담으려고 했기 때문. 앞서 보았던대로 int
보다 작은 타입끼리 연산하면 자동적으로 피연산자들을 int
로 통일시키므로 결과값도 int
가 됨. 아래처럼 고쳐야함.
byte c = (byte)(a + b);
byte a = 20;
byte b = 30;
byte c = (byte)(a * b); // c의 값은 600이 아니라 88이 됨.
이번에는 결과값을 담을 변수 c
의 타입에 맞게 형변환을 시켰는데, 에러는 발생하지 않았지만 예상 밖의 결과 값이 나옴. 연산 결과가 byte
타입의 범위를 초과했기 때문에 int
로는 표현 가능했던 결과값이 byte
로 형변환될 때, 이진수 앞부분이 잘리고 88만 남겨짐.
연산 결과의 데이터 손실을 방지하기 위해 충분히 큰 범위의 타입을 사용하는 것이 권장됨.
이 때, 결과값의 타입을 넉넉하게 하는 것도 중요하지만, 피연산자 자체의 타입부터 넉넉한 것으로 설정하는게 좋음.
int a = 1_000_000;
int b = 2_000_000;
long c = a * b; // c = -1454759936
결과값이 담기는 변수 c
가 long
타입이니 문제 없을 것 같지만, 애초에 int
끼리 연산했으므로 결과값도 int
라서 예상 값인 2000000000000 이 int
범위 내에서 -1454759936 이 되어 반환됨.
long a = 1_000_000;
long b = 2_000_000;
long c = a * b; // c = 2000000000000
피연산자 타입도 long
으로 바꾸니 정상 출력됨.
문자(char
)도 결국 숫자인 유니코드 값으로 변환되어 저장되므로 문자 간의 연산은 숫자간의 연산과 동일한 원리가 적용됨.
char c1 = 'a';
char c2 = (char)(c1 + 1); // c2 = 'b'
c1
은 메모리상에서는 정수 97 ('a'의 유니코드)이므로, 1을 더하면 정수 98이 됨. 이 결과값을 다시 char
로 형변환하면 c2
에는 유니코드 98에 해당하는 문자 b
가 담김.
char c3 = 'a' + 1; // c3 = 'b'
c3
는 결과값을 char
로 형변환하지 않았는데도 컴파일 에러가 없음. 변수를 참조하지 않고 단순 리터럴끼리 연산하면 컴파일러가 미리 연산을 수행해 실제 런타임에는 연산이 수행되지 않고 이미 'b'라는 값이 c3
에 담긴 채로 실행된다.