[Chapter 3] 연산자 (Operator)

slchoi·2022년 1월 5일
0

자바의 정석

목록 보기
8/19
post-thumbnail

'자바의 정석 3rd Edition'을 공부하며 정리한 내용입니다.

1. 연산자(Operator)


1.1 연산자와 피연산자

  • 연산자(operator): 연산을 수행하는 기호 (+,-.*,/ 등)
  • 피연산자(operand): 연산자의 작업 대상(변수, 상수, 리터럴, 수식)
  • 연산자는 피연산자로 연산을 수행하고 나면 항상 결과값을 반환
    • 대입 연산자는 우변의 값을 좌변에 저장하고 저장된 값을 연산결과로 반환

1.2 식과 대입연산자

  • 식: 연산자와 피연산자를 조합하여 계산하고자하는 바를 표현한 것
  • 식을 평가(evaluation)한다: 식을 계산하여 결과를 얻는 것
    • 하나의 식을 평가하면, 단 하나의 결과를 얻음
  • 대입 연산자 =를 사용해 변수와 같이 값을 저장할 수 있는 공간에 결과를 저장
    • 식의 평가결과를 다른 곳에 사용하지 않을 것이라면 변수에 저장하지 않고 println 메서드의 괄호 안에 직접 식을 써도 됨

1.3 연산자의 종류

종류연산자설명
산술 연산자+ - * / % << >>사칙 연산(+,-,*,/)과 나머지 연산(%)
비교 연산자> < >= <= == !=크고 작음과 같고 다름을 비교
대입 연산자=우변의 값을 좌변에 저장
기타(type) ?: instanceof형변환 연산자, 삼항 연산자, instanceof 연산자
  • 논리 연산자: &&, ||, !, &, |, ^, ~ (AND와 OR으로 조건을 연결)

1.4 연산자의 우선순위와 결합규칙

1. 주의해야 할 연산자 우선순위의 예와 설명

설명
x << 2 + 1쉬프트 연산자(<<)는 덧셈 연산자보다 우선순위가 낮음. 'x<<(2+1)'과 같은 식
data & 0xFF == 0비트 연산자(&)는 비교 연산자(==)보다 우선순위가 낮으므로 비교연산 후에 비트연산 수행. 'data & (0xFF == 0)'와 같은 식
  • 논리 연산자 중에서 AND를 의미하는 '&', '&&'가 OR를 의미하는 '|', '||'보다 우선순위가 높음. 수식에 AND와 OR가 함께 사용되는 경우 괄호를 사용해 우선순위를 명확히 하는 것이 좋음

2. 연산자의 결합규칙

  • 대부분 왼쪽에서 오른쪽의 순서로 연산을 수행
  • 단항 연산자와 대입 연산자만 반대로 오른쪽에서 왼쪽의 순서로 연산을 수행

3. 연산자 우선순위 정리

  1. 산순 > 비교 > 논리 > 대입. 대입은 제일 마지막에 수행됨
  2. 단항 > 이항 > 삼항. 단항 연산자의 우선순위가 이항 연산자보다 높음
  3. 단항 연산자와 대입 연산자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽

1.5 산술 변환(usual arithmetic conversion)

  • 이항 연산자는 두 피연산자의 타입이 일치해야 연산이 가능하므로 피연산자의 타입이 다를 경우 먼저 형변환 연산자로 타입을 일치시켜야 함
    • 두 피연산자의 타입 중 더 큰 타입으로 일치시킴
    • 작은 타입에서 큰 타입으로 형변환하는 경우 자동적으로 형변환되므로 형변환 연산자를 생략할 수 있음
  • 산술 변환: 연산 전에 피연산자 타입의 일치를 위해 자동 형변환되는 것. 이항 연산과 단항 연산에서 수행
    • 쉬프트 연산자, 증감 연산자에서는 산술 변환이 일어나지 않음
    • 규칙 1. 두 피연산자의 타입을 같게 일치시킴(큰 타입으로 일치)
    • 규칙 2. 피연산자의 타입이 int보다 작은 타입이면 int로 변환
  • 연산 결과의 타입은 피연산자의 타입과 일치

2. 단항 연산자


2.1 증감 연산자 ++ --

  • 증감 연산자: 피연산자에 저장된 값을 1 증가 또는 감소시킴
    • 피연산자로 정수, 실수 모두 가능하지만 상수는 값을 변경할 수 없으므로 불가능
  • 산술 변환에 의한 자동 형변환이 발생하지 않고, 연산결과의 타입은 피연산자의 타입과 같음
  • 일반적으로 단항 연산자는 피연산자의 왼쪽에 위치하지만 '++', '--'는 양쪽 모두 가능
타입설명
전위형(prefix)값이 참조되기 전에 증가시킴. 변수의 값을 먼저 증가시킨 후에 읽어옴
후위형(postfix)값이 참조된 후에 증가시킴. 변수의 값을 먼저 읽어온 후에 값을 증가
  • '++i;', 'i++;'처럼 증감 연산자가 수식이나 메서드 호출에 포함되지 않고 독립적인 하나의 문장으로 쓰인 경우 전위형과 후위형 차이는 없음

2.2 부호 연산자 + -

  • 부호 연산자 '-': 피연산자의 부호를 반대로 변경한 결과를 반환
    • 피연산자가 음수면 양수, 양수면 음수가 연산의 결과가 됨
  • boolean형과 char형을 제외한 기본형에만 사용 가능

3. 산술 연산자


3.1 사칙 연산자 + - * /

1. 숫자에서의 사칙 연산

  • 곱셈, 나눗셈, 나머지 연산자가 덧셈, 뺄셈 연산자보다 우선순위가 높아 먼저 처리
  • 피연산자가 정수형인 경우, 나누는 수로 0을 사용할 수 없음
    • 0으로 나눌 경우 ArithmeticException 발생
    • 부동 소수점값인 0.0f, 0.0d로 나누는 것은 가능하지만 결과는 Infinity
  • 나누기 연산자의 두 피연산자가 모두 int 타입인 경우 연산결과에 소수점이 있을 경우 반올림하지 않고 버려짐
    • 올바른 연산결과를 얻기 위해서는 두 피연산자 중 한 쪽을 실수형으로 형변환해야 함
byte a = 10;
byte b = 20;
byte c = a + b; // byte c = (byte)(a+b);로 변경해주어야 에러 발생 X
System.out.println(c);
  • 위의 코드 같은 경우 컴파일하면 에러 발생
    • a와 b는 모두 int형보다 작은 byte형이기 때문에 +는 이 두 개의 피연산자들의 자료형을 int형으로 변환한 다음 연산 수행. 따라서 a+b의 결과값은 int형
    • 4byte 값을 1byte에 저장하려했기 때문에 에러 발생
    • 크기가 큰 자료형의 값을 작은 자료형의 변수에 저장하려면 명시적으로 형변환 연산자를 사용해서 변환해주어야 함
    • 수식에 변수가 들어가 있는 경우 컴파일러가 미리 계산할 수 없으므로 형변환을 해주어야 함

2. 문자에서의 사칙 연산

  • 문자는 실제로 해당 문자의 유니코드(부호없는 정수)로 바뀌어 저장되므로 문자간의 사칙연산은 정수간의 연산과 동일
  • 문자간의 뺄셈을 하는 경우가 대부분
    • 문자 '2'를 숫자로 변환하려면 문자 '0'dmf Qownaus ehla
    • 문자 '2'의 유니코드는 50이고 문자 '0'은 48이기 때문
char c2 = 'a' + 1;  // 출력값: b
  • 오류 발생 X
  • 이항 연산자는 int보다 작은 타입의 피연산자를 int로 자동 형변환한다고 했는데 char c2 = (char) ('a'+1)처럼 형변환해주지 않고도 문제가 없는 이유
    • 리터럴 간의 연산이기 때문
    • 상수 또는 리터럴 간의 연산은 실행 과정동안 변하는 값이 아니기 때문에 컴파일 시에 컴파일러가 계산해서 그 결과로 대체함으로써 코드를 효율적으로 만듦
    • 컴파일러가 미리 덧셈연산을 수행해 실생 시에는 덧셈 연산이 수행되지 않음

3. 소문자를 대문자로 변경하는 코드

  • 대문자와 소문자 간의 코드값 차이는 10진수로 32
char lowerCase = 'a';
char upperCase = (char) (lowerCase - 32);
  • 대문자를 소문자로 변환하려면 32를 더해주면 됨
  • char형과 int형 간의 뺄셈 결과는 int 형이므로 연산 후 char형으로 다시 형변환해야 함

4. 반올림하는 방법

  • 'int / int'를 수행하면 소수점을 버린 결과가 나옴
  • 반올림을 하고 싶다면 Math.round()를 사용
double pi = 3.141592;
Math.round(pi * 1000) => Math.round(3141.592) => 3142

3.2 나머지 연산자 %

  • 왼쪽의 피연산자를 오른쪽 피연산자로 나누고 난 나머지 값을 결과로 반환하는 연산자
  • 나눗셈에서처럼 오른쪽 피연산자로 0을 사용할 수 없지만 음수는 허용
    • 음수를 사용할 경우 부호는 무시되므로 결과는 음수의 절대값으로 나눈 나머지와 같음
  • 짝수, 홀수 또는 배수 검사 등에 주로 사용
  • 피연산자의 부호를 모두 무시하고 나머지 연산을 한 결과에 왼쪽 피연산자의 부호를 붙이면 됨

4. 비교 연산자


  • 조건문, 반복문의 조건식에 사용. 연산 결과는 true, false 둘 중 하나
  • 비교 연산자도 이항 연산자이므로 비교하는 피연산자의 타입이 다를 경우 자료형의 볌위가 큰 쪽으로 자동 형변환하여 피연산자의 타입을 일치시킨 후에 비교

4.1 대소비교 연산자 < > <= >=

  • 기본형 중에서 boolean을 제외한 나머지 자료형에 모두 사용 가능하지만 참조형에는 사용 불가

4.2 등가비교 연산자 == !=

1. 숫자 비교

  • 기본형과 참조형에 모두 사용 가능
    • 기본형의 경우 변수에 저장되어 있는 값이 같은지를 알 수 있음
    • 참조형의 경우 객체의 주소값을 저장하기 때문에 두 개의 피연산자(참조변수)가 같은 객체를 가리키고 있는지 확인 가능
  • 기본형과 참조형은 서로 형변환이 불가능하므로 등가비교 연산자로 기본형과 참조형을 비교할 수는 없음
  • float 타입과 double 타입의 값을 비교하려면 double 타입의 값을 float 타입으로 형변환한 다음 비교해야 올바른 결과를 얻을 수 있음

2. 문자열 비교

  • 두 문자열을 비교할 때는 비교 연산자 '==' 대신 equals()라는 메서드를 사용해야 함
    • 비교 연산자는 두 문자열이 완전히 같은 것인지를 비교할 뿐, 문자열의 내용이 같은지 비교하기 위해서는 equals()를 사용해야 함
    • 비교 연산자의 경우 내용이 같지만 다른 객체일 경우 fasle를 반환
    • equals()의 경우 객체는 다르지만 내용이 같다면 true를 반환
  • 대소문자를 구별하지 않고 비교하고 싶다면 equalsIgnoreCase()를 사용

5. 논리 연산자


  • 둘 이상의 조건을 AND나 OR으로 연결하여 하나의 식으로 표현할 수 있게 해줌

5.1 논리 연산자 - &&, ||, !

  • 논리 연산자는 피연산자로 boolean형 또는 boolean형 값을 결과로 하는 조건식만을 혀용
  • || (OR 결합): 피연산자 중 어느 한쪽만 true이면 true를 결과로 얻음
  • && (AND 결합): 피연산자 양쪽 모두 true이어야 true를 결과로 얻음
  • ! (논리 부정 연산자): 피연산자가 true이면 false를, false이면 true를 결과로 반환
    • 어떤 값에 논리 부정 연산자를 반복적으로 적용하면 참과 거짓이 차례로 반복됨
    • 토글 버튼(toggle button)을 논리적으로 구현 가능

효율적인 연산(short circuit evaluation)

  • OR 연산의 경우, 두 피연산자 중 어느 한 쪽만 참이어도 전체 연산결과가 참이므로 죄측 피연산자가 true이면 우측 피연산자의 값으 평가하지 않음
  • AND 연산의 경우, 어느 한쪽만 false이어도 전체 연산결과가 false이므로 좌측 피연산자가 false이면 우측 피연산자는 평가하지 않음
  • 같은 조건식이라도 피연산자의 위치에 따라 연산속도가 달라질 수 있음
    • OR 연산의 경우 연산결과가 참일 확률이 높은 피연산자를 연산자의 왼쪽에 놓아야 더 빠른 연산결과를 얻을 수 있음

5.2 비트 연산자 & | ^ ~ << >>

  • 피연산자를 비트단위로 논리 연산
  • 피연산자는 실수는 혀용하지 않고 정수(문자 포함)만 허용
  • toBinaryString(): 비트연산의 결과를 2진수로 출력

비트 연산자 종류

  1. | (OR 연산자): 피연산자 중 한 쪽의 값이 1이면, 1을 결과로 얻고 그 외에는 0을 얻음
    • 특정 비트의 값을 변경할 대 사용
  2. & (AND 연산자): 피연산자 양 쪽이 모두 1이어야 1을 결과로 얻고 그 외에는 0을 얻음
    • 특정 비트의 값을 뽑아낼 때 사용
  3. ^ (XOR 연산자): 피연산자의 값이 서로 다를 때만 1을 얻고 그 외에는 0을 얻음
    • 같은 값으로 두고 XOR 연산을 수행하면 원래의 값으로 돌아오는 특징이 있어 간단한 암호화에 사용
  4. ~(비트 전환 연산자): 피연산자를 2진수로 표현했을 때, 0은 1로, 1은 0으로 바꿈
    • 비트 전환되고 나면, 부호있는 타입의 피연산자는 부호가 반대로 변경됨 => 피연산자의 1의 보수를 얻을 수 있음
    • 양의 정수 p가 있을 때, p에 대한 음의 정수를 얻으려면 '~p+1'를 계산
    • 음의 정수 p가 있을 때, p에 대한 양의 정수를 얻으려면 '~(p-1)'를 계산
    • 부호 연산자 -를 사용하면 간단하게 변환할 수 있음
    • ~~p: 변수 p에 두 번의 비트 전환 연산을 적용. 원래 값과 같지만 연산 결과의 타입은 byte가 아니라 int
  5. 쉬프트 연산자 << >>: 피연산자의 각 자리를 오른쪽(>>) 또는 왼쪽(<<)으로 이동시키는 연산자
    • 자리 이동으로 저장범위를 벗어난 값들은 버려지고 빈자리는 0으로 채움
    • '<<' 연산자의 경우, 피연산자의 부호에 상관없이 각 자리를 왼쪽으로 이동시키며 빈칸을 0으로 채우면 됨
      • x << nx2nx * 2^n의 결과와 같음
    • '>>' 연산자의 경우, 오른쪽으로 이동시키기 때문에 부호있는 정수는 부호를 유지하기 위해 왼쪽 피연산자가 음수인 경우 빈자리를 1로 채움. 양수일 경우 0으로 채움
      • x >> nx/2nx / 2^n의 결과와 같음
    • 좌측 피연산자는 산술변환이 적용되어 int보다 작은 타입은 int타입으로 자동 변환되고 연산결과도 int타입이 됨
    • 피연산자의 타입을 일치시킬 필요가 없기 때문에 우측 피연산자에는 산술변환이 적용되지 않음
    • 쉬프트 연산자를 사용하는 것이 산술 연산자를 사용하는 것보다 더 빠름. 하지만 코드의 가독성이 떨어지기 때문에 곱셈 또는 나눗셈 연산자를 주로 사용하고, 빠른 실행속도가 요구되어지는 곳만 쉬프트 연산자를 사용하는 것이 좋음

6. 그 외의 연산자


6.1 조건 연산자 ? :

  • 조건식, 식1, 식2 세 개의 피연산자를 필요로 하는 삼항 연산자이며, 삼항 연산자는 조건 연산자 하나
조건식 ?1 :2
  • 조건식이 true이면 식1이, false면 식2가 연산 결과가 됨
  • 조건 연산자는 조건문인 if문으로 바꿔쓸 수 있으며, if문 대신 조건 연산자를 사용하면 코드를 간단히 할 수 있음
  • 조건 연산자를 중첩해서 사용하면 셋 이상 중의 하나를 결과로 얻을 수 있음
    result = x > 0 ? 1 : (x==0 ? 0 : -1)
    • x의 값이 양수면 1, 0이면 0, 음수면 -1의 결과가 나옴
  • 조건 연산자의 식1과 식2, 두 피연산자의 타입이 다른 경우 산술 변환이 발생

6.2 대입 연산자 = op=

  • 변수와 같은 저장공간에 값 또는 수식의 연산결과를 저장하는데 사용
    • 오른쪽 피연산자의 값을 왼쪽 피연산자에 저장하고 저장된 값을 연산 결과로 반환
  • 연산자들 중 가장 낮은 우선순위를 가지기 때문에 식에서 제일 나중에 수행됨

1. lvalue와 rvalue

  • 대입 연산자의 왼쪽 피연산자를 lvalue(left value), 오른쪽 피연산자를 rvalue(right value)라고 함
  • rvalue는 변수뿐만 아니라 식이나 상수 등이 모두 가능하지만 lvalue는 변수처럼 값을 변경할 수 있는 것이어야 함
    • 리터럴이나 상수같이 값 변경이 불가능한 것은 lvalue가 될 수 없음

2. 복합 대입 연산자

  • 다른 연산자(op)와 결합해 'op='와 같은 방식으로 사용 가능
  • 결합된 두 연산자는 반드시 공백없이 붙여 써야 함

Chapter 3 끝!!!

profile
예비 백엔드 개발자

0개의 댓글