연산자(Operator)

  • 연산자(Operator): 연산을 수행하는 기호 (+, -, *, / 등)

  • 피연산자(Operand) : 연산의 작업 대상(변수, 상수, 리터럴, 수식)



식(式)

  • 연산자와 피연산자를 조합하여 계산을 하는 바를 표현한 것
    • 2 * 4
  • 식을 계산하여 결과를 얻는 것을 ‘식을 평가(evaluation)한다’라고 함

  • 하나의 식을 평가(계산)하면, 단 하나의 결과를 얻음

    • 2 * 4 → 8
  • 작성한 식을 프로그램에 포함하려면, 식의 끝에 ‘;’를 붙여서 문장으로 만들어야 함

    • 2 * 4;
  • 대입연산자’=’를 사용하면 변수와 같이 값을 저장할 수 있는 공간에 결과를 저장할 수 있음

    • x = 2 * 4;
  • 식을 저장하지 않고 출력만 할 경우, println메서드의 괄호()안에 직접 식을 써도 무관

    • System.out.println(2*4); → 8 출력



연산자의 기능별 분류

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



피연산자의 개수에 의한 분류

종류 피연산자 개수
단항 연산자 (type) a, +a, !a 피연산자가 한개
이항 연산자 a-b, a<b, a&&b 피연산자가 두개
삼항 연산자 ?: a?b:c 피연산자가 세개



연산자의 우선순위와 결합규칙(진행방향)

  1. 산술 > 비교 > 논리 > 대입

  2. 단항(1) > 이항(2) > 삼항(3)

  3. 단항 연산자와 대입 연산자를 제외한 모든 연산자의 진행 방향은 왼쪽에서 오른쪽

종류 결합규칙 연산자 비고 우선순위
단항 연산자 ++ – + - ~ ! (type) + -는 부호연산자임 1
산술 연산자 * / %   2
산술 연산자 + - 계산 연산자임 3
산술 연산자 << >>   4
비교 연산자 < > <= >= instance of 5
비교 연산자 == !=   6
논리 연산자 &   7
논리 연산자 ^   8
논리 연산자 | 9  
논리 연산자 &&   10
논리 연산자 || 11  
삼항 연산자 ?:   12
대입 연산자 = += -= *= /= %=   13
대입 연산자 «= >>= &= ^= |=   14



일반 산술 변환

  • 연산 수행 직전에 발생하는 피연산자의 자동 형변환

    • 두 피연산자의 타입을 같게 일치(보다 큰 타입으로 일치)

    • 피연산자의 타입이 int보다 작은 타입이면 int로 변환

  • 증감 연산자는 일반 산술 변환에 의한 자동 형변환이 발생하지 않으며, 연산 결과의 타입은 피연산자와 같음



단항 연산자 : 증감 연산자 ++ –

  • 증가 연산자(++) : 피연산자의 값을 1 증가시킴

  • 감소 연산자(--) : 피연산자의 값을 1 감소시킴

  • 식에 두 번 이상 포함된 변수에 증감 연산자를 사용하는 것은 피해야 함

타입 설명 사용 예(i = 1)
전위형(pretfix) 값이 참조되기 전에 증가/감소 j = ++i; → j = 2, i = 2
j = –i; → j = 0, i = 0
후위형(postfix) 값이 참조된 후에 증가/감소 j = i++; → j = 1, i = 2;
j = –i; → j = 1, i = 0;



단항 연산자 : 부호 연산자 + -

  • - : 피연산자의 부호를 반대로 변경한 결과를 반환

    • -음수 → 양수

    • -양수 → 음수

  • + : 하는 일은 없고, 쓰이는 경우도 없는데 -랑 짝을 맞추기 위해 형식적으로 추가되어있음

    • 부호 연산자는 boolean형과 char형을 제외한 기본형에만 사용할 수 있음



산술 연산자 : 사칙 연산자 + - * /

  • + : 덧셈

  • - : 뺄셈

  • * : 곱셈

  • / : 나눗셈

    • 피연산자가 정수형인 경우, 나누는 수로 0을 사용할 수 없음
      0으로 나누면 에러 발생 (ArithmeticException)

    • 나누기 연산자의 두 피연산자가 모두 int타입인 경우, 연산 결과 역시 int타입 (소수점 아래는 버림)
      하나를 float 또는 double로 바꾸고 연산하면 연산 가능

    • 피연산자가 하나라도 실수형인 경우, 나누는 수로 0을 사용하면 Infinity(무한대) 출력

    • 피연산자가 유한수가 아닌 경우의 연산 결과

x y x / y x % y
유한수 ±0.0 ±Infinity NaN
유한수 ±Infinity ±0.0 x
±0.0 ±0.0 NaN NaN
±Infinity 유한수 ±Infinity NaN
±Infinity ±Infinity NaN NaN
  • 연산 순서대로 연산을 진행하니 중간에 overflow가 발생하지 않도록 형태에 신경쓰기

  • 상수 또는 리터럴 간의 연산은 실행 과정동안 변하는 값이 아니기 때문에, 컴파일 시에 컴파일러가 계산해서 그 결과로 대체함으로써 코드를 보다 효율적으로 만듦

    • 즉 풀어 쓰더라도 컴파일러에 의해 미리 계산되므로 실행시 성능 차이는 없음
// 컴파일 전 .java 파일
char c2 = 'a' + 1;
int sec = 60 * 60 * 24;

// 컴파일 후 .class 파일
char 2 = 'b';
int sec = 86400;



산술 연산자 : 나머지 연산자 %

  • 피연산자로 정수만 허용

    • 음수도 허용하지만, 부호는 무시하므로 절댓값으로 나눈 것과 같음
  • 주로 짝수, 홀수, 또는 배수 검사 등에 사용

  • 나눗셈처럼 나누는 수(오른쪽 피연산자)로 0을 사용할 수 없음

  • 왼쪽의 피연산자를 오른쪽 피연산자로 나누고 남은 값



비교 연산자

  • 두 피연산자를 비교하는데 사용

  • 조건문과 반복문의 조건식에 주로 사용

  • 연산결과는 오직 true or false

  • 이항 연산자이므로 비교하는 피연산자의 타입이 서로 다를 경우, 자료형의 범위 가 큰 쪽으로 자동 형변환하여 피연산자의 타입을 일치시킨 후 비교

  • (주의) float과 double 비교

    • 10.0 == 10.0f → true / 0.1 == 0.1f → false

    • 이유 : 정수형과 달리 실수형은 근사값으로 저장되므로 오차가 발생할 수 있음

    • 10.0f → 오차없이 저장할 수 있는 값, 0.1f → 2진수로 변환하는 과정에서 오차가 발생

    • 10.0f → 0.10000000149011612로 저장

    • 0.1f → 0.00000000000000001로 저장

    • 성공적으로 비교하려면 : 자동형변환 타입인 float → double로 자동변환되서 비교 (X)

    • 성공적으로 비교하려면 : double을 float으로 (수동)형변환 후 비교 (O)

  • 대소비교

비교 연산자 연산결과
> 좌변 값이 크면 true 아니면 false
< 좌변 값이 작으면 true 아니면 false
>= 좌변 값이 크거나 같으면 true 아니면 false
<= 좌변 값이 작거나 같으면 true 아니면 false
  • 등가비교
비교 연산자 연산결과
== 두 값이 같으면 true 아니면 false
!= 두 값이 다르면 true 아니면 false
  • 문자열비교
비교 연산자 연산결과
대상문자열.equals(비교문자열) 대상문자열과 비교문자열이 같으면 true 다르면 false
대상문자열.equalsIgnoreCase(비교문자열) 대상문자열과 비교문자열이 대소문자 구분없이
같으면 true 다르면 false
  • 왜 문자열은 ‘==’를 쓰지 않고 .equals를 쓰는가

    • String은 클래스임(참조형 변수) : 값에 값이 들어가지 않고, 주소값이 들어감
      ==는 주소값끼리 비교를 해버려서 실패

    • 주소에 들어있는 값을 비교하려면 .equals()를 사용해야 함

    • String은 클래스지만 new를 사용하지 않고 선언할 수 있도록 허용해 둔 것

String str = new String("abc");
String str = "abc"; // 위와 동일(간략 표현)



논리 연산자 : 논리 연산자 && || !

  • || (or 결합) : 피연산자 중 어느 한 쪽만 true이면 true를 결과로 얻음

  • && (and 결합) : 피연산자 양쪽 모두 true이어야 true를 결과로 얻음

x y x || y x && y
true true true true
true false true false
false true true false
false false false false
  • 특징 : 효율적인 연산(short circuit evaluation)

    • OR 연산 : 좌항이 true이면 우항은 보지 않고 true

    • AND 연산 : 좌항이 false이면 우항은 보지 않고 false

    • 위 부분을 고려하여 프로그래밍을 하면 더 빠른 연산 결과를 얻을 수 있음

  • ! 논리 부정 연산자

    • true → false / false → true 반환

    • 단항연산자의 순서는 ← 이렇게 되므로 !!a 일때, a에 가까운 !가 먼저 처리됨



논리 연산자 : 비트 연산자 & | ^ ~ << >>

  • 비트 연산자는 피연산자를 비트단위로 논리 연산

  • 피연산자를 이진수로 표현했을 때 각 자리의 규칙을 규칙에 따라 표와 같이 연산하며, 피연산자로 실수는 허용하지 않고, 정수(문자 포함)만 허용

  • 피연산자의 타입을 일치시키는 ‘산술 변환’이 일어날 수 있음

  • 규칙

    • | (or 연산자) : 피연산자 중 한 쪽의 값이 1이면 1을 결과로 얻음, 그 외에는 0

    • & (and 연산자) : 피연산자의 양 쪽이 모두 1이어야만 1을 결과로 얻음, 그 외에는 0

    • ^ (xor 연산자) : 피연산자의 값이 서로 다를 때만 1을 결과로 얻음, 그 외에는 0

x y x | y x & y x ^ y
1 1 1 1 0
1 0 1 0 1
0 1 1 0 1
0 0 0 0 0
ex) 1010 ex) 1001 ex) 1011 ex) 1000 ex) 0111
  • 비트 전환 연산자 ~ : 2진수 기준 0 → 1로 1 → 0으로 바꿈

    • 논리 부정 연산자 !와 유사
x ~x
1 0
0 1
ex) 1010 ex) 0101
  • 쉬프트 연산자 << >>

    • 피연산자의 각 자리(2진수로 표현했을때)를 ‘오른쪽(>>)’ 또는 ‘왼쪽(<<)’으로 이동(shift)한다고 해서 ‘쉬프트 연산자(shift operator)

    • 좌측 피연산자를 우측 피연산자만큼 쉬프트

    • 곱셈/나눗셈 연산자를 사용하지 않고 굳이 쉬프트 연산자를 사용하는 이유?
      쉬프트 연산자가 곱셈/나눗셈 연산자보다 빠르기 때문(실행 속도)
      가독성이 더 중요하다면 곱셈/나눗셈 연산자로 사용

    • << (좌측 쉬프트)

  좌측쉬프트  
사용법 a << b  
의미 2진수로 표현했을 때>ba를 b만큼 좌측으로 shift
비는건 0으로 채우기
 
예제 8 << 2
→ 00001000
→ 00
0001000__
[왼쪽으로 2칸 보내버림]
→ 0001000__
[삐져나간 값 버림]
→ 000100000
[비는건 0으로 채우기]
= 32
비고 a « b는 a * 2^n의 결과와 같음
(8 « 2 = 8 * 2^2 = 8 * 4 = 32)
 
  • >> (우측 쉬프트), (단 a >= 0)
  우측쉬프트, a>=0
사용법 a >> b (단 a >= 0)
의미 2진수로 표현했을 때
a를 b만큼 우측으로 shift
비는건 0으로 채우기
예제 8 >> 2
→ 00001000
→ __000010|00
[오른쪽으로 2칸 보내버림]
→ __000010
[삐져나간 값 버림]
→00000010
[비는건 0으로 채우기]
= 2
비고 a >> b는 a / 2^n의 결과와 같음
(8 >> 2 = 8 / 2^2 = 8 / 4 = 2)
  • >> (우측 쉬프트), (단 a < 0)
  우측쉬프트, a<0  
사용법 a >> b (단 a<0)  
의미 2진수로 표현했을 때
a를 b만큼 우측으로 shift
비는건 음수니까 1로 채우기
 
예제 -8 >> 2
→ 11111000
→ __111110
00
[오른쪽으로 2칸 보내버림]
→ __111110
[삐져나간 값 버림]
→11111110
[비는건 1으로 채우기]
= -2
비고 a >> b는 a / 2^n의 결과와 같음
(-8 >> 2 = -8 / 2^2 = -8 / 4 = -2)
 



그 외의 연산자 : 조건 연산자 ?:

  • 조건식 ? 식1 : 식2

    • 조건식이 참(true)면 식1, 거짓(false)면 식2
  • 가독성을 높이기 위해 조건식을 괄호( )로 둘러싸는 경우가 많지만 필수는 아님

  • 가독성이 떨어지기 때문에 꼭 필요한 경우에 한 번 정도만 중첩하는 것이 좋음

  • 조건 연산자의 식1과 식2의 피연산자 타입이 다른 경우, 이항연산자처럼 산술 변환이 일어남

  • if문의 다음과 같음

// 조건 연산자
result = (x > y) ? x : y;

// if문
if (x > y)
    result = x; // x > y가 true일 때
else
    result = y; // x > y가 false일 때



그 외의 연산자 : 대입 연산자 = op=

  • 대입 연산자 (=)

    • lvalue(left value) : 대입 연산자의 왼쪽 피연산자, 값을 변경할 수 있는 것

    • rvalue(right value) : 대입 연산자의 오른쪽 피연산자, 변수, 상수, 식, 리터럴 등 모두 가능

    • 왼쪽 피연산자의 값을 오른쪽 피연산자에 저장

    • 우선순위가 가장 나중이고, 결합방향이 ← 임

    • y = x = 3 : x = 3부터 진행 후 y = x 진행

  • 복합 대입 연산자 (op =)

    • 대입 연산자는 다른 연산자(op)와 결합하여 “op=”와 같은 방식으로 사용될 수 있음

    • 결합된 두 연산자는 반드시 공백 없이 붙여써야 함

op= =
i += 3; i = i + 3;
i -= 3; i = i - 3;
i *= 3; i = i * 3;
i /= 3; i = i / 3;
i %= 3; i = i % 3;
i <<= 3; i = i << 3;
i >>= 3; i = i >> 3;
i &= 3; i = i & 3;
i ^= 3; i = i ^ 3;
i |= 3; i = i | 3;
(주의) i *= 10 + j (주의) i = i * (10+j);
(틀림 - i = i * 10 + j);