Chapter 03 - 연산자

김태원·2023년 1월 9일
0

오버플로우와 언더플로우

오버플로우(overflow)란 타입이 허용하는 최대값을 벗어나는 것을 말한다.
반대로 언더플로우(underflow)는 타입이 허용하는 최소값을 벗어나는 것을 말한다.

정수 타입 연산에서 오버플로우 또는 언더플로우가 발생되면 에러가 발생할 것 같지만, 실제로는 그렇지 않고 해당 정수 타입의 최소값 또는 최대값으로 되돌아간다.

예를 들어 byte 타입일 경우 최대값 127에서 1을 더하면 128이 되어 오버플로우가 발생하여 연산 결과는 최소값인 -128이 된다. 그리고 다시 1을 더하면 -127이 된다.

byte value = 127;
value++;
System.out.println(value); // -128

마찬가지로 -128에서 1을 빼면 -129가 되어 언더플로우가 발생하는데, 연산 결과는 최대값인 127이 된다.
그리고 다시 1을 빼면 126이 된다.

byte value = -128;
value--;
System.out.println(value); // 127

오버 플로우가 발생하는 이유는 다음과 같다.

컴퓨터가 저장하는 모든 데이터는 결국 이진수로 저장되는데, 자료형의 크기를 넘어선 연산을 시도하면 다음 그림과 같은 이진수 연산이 이루어진다.
Integer Overflow

위 그림을 보면 자릿수의 한계로 인해 계산 결과가 100000000가 아닌 00000000이 되는 것을 확인할 수 있다.


NaN과 Infinity

자바는 NaN(Not A Number)과 Infinity(무한대)를 의미하는 자료형을 가지고 있다.

나눗셈(/) 또는 나머지(%) 연산에서 좌측 피연산자가 정수이고 우측 피연산자가 0일 경우 예외(ArithmeticException)이 발생한다.
무한대의 값을 정수로 표현할 수 없기 때문이다.

int x = 5;
int y = 0;
int result = 5 / 0; // 예외 발생

하지만 좌측 피연산자가 실수이거나 우측 피연산자가 0.0 또는 0.0f이면 예외가 발생하지 않고 연산 결과가 Infinity 또는 NaN이 된다.

5 / 0.0 => Infinity
5 % 0.0 => NaN

Infinity 또는 NaN 상태에서 계속 연산을 수행하면 어떤 연산을 하더라도 결과는 계속해서 Infinity와 NaN이 된다.

Infinity + 1 => Infinity
NaN + 1 => NaN

따라서 나눗셈(/) 또는 나머지(%) 연산의 결과가 Infinity 또는 NaN인지 확인하는 과정이 필요하다.

boolean value1 = Double.isInfinite(Infinity) // true
boolean value2 = Double.isInfinite(12.34) // false
boolean value3 = Double.isNaN(NaN) // true
boolean value4 = Double.isNaN(12.34) // false

왜 String의 비교 연산은 ==이 아니라 equals()를 써야 할까?

자바에서 문자열을 비교할 때에는 동등(==, !=) 연산자 대신 equals()와 !equals()를 사용한다. 왜일까?

String 변수를 생성할때는 리터럴을 이용한 방식과 new를 이용한 방식이 있다.
두 방식에는 큰 차이점이 있는데 리터럴을 사용하게 되면 string constant pool이라는 영역에 존재하게 되고
new를 사용하게 되면 Heap영역에 존재하게 된다.

Java String

String을 리터럴로 선언할 경우, 내부적으로 String의 intern() 메서드가 호출되게 되고
intern() 메서드는 주어진 문자열이 string constant pool에 존재하는지 검색하고,
있다면 그 주소값을 반환, 없다면 string constant pool에 넣고 새로운 주소값을 반환한다.

주소값 비교(==)와 값 비교(equals)

==연산자와 equals()메소드의 가장 큰 차이점은 다음과 같다.

==연산자는 비교하고자 하는 두개의 대상의 주소값을 비교하는데,
String클래스의 equals 메소드는 비교하고자 하는 두개의 대상의 값 자체를 비교한다는 점이다.

equals()는 모든 객체의 부모 클래스인 Object에 정의되어있는 메소드다.
String 클래스는 위와 같이 equals()를 오버라이드하여 인자로 전달된 String의 문자열을 비교한다.

자바의 기본 타입(byte, short, int, long, float, double, boolean)은 Call by Value 형태로,
기본적으로 대상에 주소값을 가지지 않는 형태로 사용된다.

하지만 String은 기본 타입이 아니라 클래스이며 클래스는 기본적으로 Call by Reference 형태로 생성 시 주소값이 부여된다.

그렇기에 String타입을 선언했을때는 같은 값을 부여하더라도 서로간의 주소값이 다른 것이다.


Call by value와 Call by reference

함수 호출 방법은 크게 두가지로 Call by value(값에 의한 호출)와 Call by reference(참조에 의한 호출)가 존재한다.

Call by value(값에 의한 호출)는 인자로 받은 값을 복사하여 처리를 한다.
Call by reference(참조에 의한 호출)는 인자로 받은 값의 주소를 참조하여 직접 값에 영향을 준다.

간단히 말해 값을 복사를 하여 처리를 하느냐, 아니면 직접 참조를 하느냐의 차이다.

Call by

Call by value(값에 의한 호출)

장점 : 복사하여 처리하기 때문에 안전하다. 원래의 값이 보존이 된다.
단점 : 복사를 하기 때문에 메모리가 사용량이 늘어난다.

Call by reference(참조에 의한 호출)

장점 : 복사하지 않고 직접 참조를 하기에 빠르다.
단점 : 직접 참조를 하기에 원래 값이 영향을 받는다.

Call by VS

C언어에서는 포인터를 이용해서 매개변수의 주소값을 넘겨 참조(Reference)할 수 있다.
JAVA에서는 포인터가 따로 없으며, 기본적인 매개변수는 Call by Value지만
예외적으로 배열과 클래스는 참조변수로 Call by Reference로 작동한다.

profile
개발이 재밌어서 하는 Junior Backend Developer

0개의 댓글