3. 연산자

3.1 연산자

3.1.1 연산자와 피연산자

  • 연산자(Operator)
    • 연산을 수행하는 기호(+, -, *, /)
  • 피연산자(Operand)
    • 연산자의 작업 대상(변수, 상수, 리터럴, 수식)

3.1.2 식과 대입연산자

  • 식(Expression)
    • 연산자와 피연산자를 조합하여 계산하는 표현
  • 대입연산자
    • = Simple assignment operator
    • += Add AND assignment operator
    • -= Subtract AND assignment operator
    • *= Multiply AND assignment operator
    • /= Divide AND assignment operator
    • %= Modulus AND assignment operator
    • <<= , >>=, &=, ^=, |=

ex)

int a = 1;

a += 1; // a = a + 1
a /= 1; // a = a/1
a <<= 1; // a = a << 1

3.1.3 연산자의 종류

  • Arithmetic Operators
    • +, -, *, /, %, ++, --
  • Relational Operators
    • ==, !=, >, <, >=, <=
  • Bitwise Operators
    • &, |, ^, ~, <<, >>, >>>
  • Logical Operators
    • &&, ||, !
  • instance of Operator
    • instanceof
//instanceof 예
class Vehicle {}

public class Car extends Vehicle {
   public static void main(String args[]){
      Vehicle a = new Car();
      boolean result =  a instanceof Car;
      System.out.println( result ); //true
   }
}

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

Example

System.out.println("1 + 2 = " + 1 + 2);
System.out.println("1 + 2 = " + (1 + 2));

System.out.println(1 + 2 + "abc");
System.out.println("abc" + 1 + 2);

3.1.5 산술 변환

  • 일반 산술변환
    • 두 피연산자의 타입을 같게 일치(큰 타입으로)
    • 피연산자 타입이 int보다 작으면 int로 변환
  • 연산결과 타입
    • 피연산자의 타입과 일치
    • ex) 5/2 -> 2(정수형) //소수점 이하 버림
    • ex2) 5.0/2 -> 2.5(실수형) //2가 실수형으로 자동 형변환

3.2 단항 연산자

3.2.1 증감 연산자 ++ --

  • 전위형
    • j = ++i;
  • 후위형
    • j = i++;

Example

int x = 5;
int y = 10;
int z = ++x * y--;

3.2.2 부호 연산자 + -

  • 부호를 반대로 변경
    • i = -i;
    • 피연산자가 1개이니까 구별가능

3.3 산술 연산자

3.3.1 사칙 연산자 + - * /

  • 연산결과로 형변환이 일어날 수 있다.

    • 큰 타입 -> 작은 타입 : 데이터 손실이 일어날 수 있다.
      • ex) int -> long
    • 작은 타입 -> 큰 타입 : 데이터 손실이 일어나지 않는다.
      • ex) int -> byte
  • 문자

    • 연산시 정수로 형변환
int a = 65;
char b = 'A';

System.out.println((char)(a + 1));
System.out.println((char)(b + 1));

3.3.2 나머지 연산자 %

  • 피연산자들을 나누고 난 나머지 값
    • 피연산자로 정수만 허용
    • 부호는 무시
    • EX) 10%8 ==> 10%-8

3.4 비교 연산자

비교연산자는 2항 연산자로 비교연산자의 좌항과 우항을 비교하며 그 결과는 true 혹은 false 중 하나가 된다. 만약 좌항과 우항의 타입이 다르면 자료형의 범위가 큰 쪽으로 형변환된 후 비교한다

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

기본 자료형 중 boolean을 제외한 나머지 자료형의 값을 비교한다. 클래스와 같은 참조형은 비교할 수 없다.

  • > 왼쪽이 크면 true, 오른쪽이 크면 false
  • < 왼쪽이 크면 false, 오른쪽이 크면 true
  • >= 왼쪽이 크면 true, 오른쪽이 크면 false. 또 왼쪽과 오른쪽이 같으면 true
  • <= 왼쪽이 크면 false, 오른쪽이 크면 true. 또 왼쪽과 오른쪽이 같으면 true

그렇다면 어떤 비교 연산자를 사용해야 코드 가독성이 좋아질까? 이야기해보자

int value = 4;

boolean result1 = 3>value;
boolean result2 = 3<value;

boolean result3 = 3>value;
boolean result4 = value>3;

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

등가비교 연산자는 2항 연산자이다. 대소비교 연산자와 달리 크기를 비교하지않고 값이 같은지 혹은 다른지를 검사한다. 대소비교는 기본 자료형만 가능하나, 등가비교는 클래스와 같은 참조형의 비교도 가능하다. 기본 자료형은 변수에 저장된 값을 비교하고, 참조형은 객체의 주소값을 저장하고 있으므로 동일한 객체를 가리키는지를 비교하게 된다.

  • == 두 값이 같으면 true, 다르면 false
  • != 두 값이 같으면 false, 다르면 true

비교연산자의 피항의 타입이 다를 경우 자료형의 범위가 다를 경우 큰 쪽으로 형변환 후 비교연산을 수행하므로 아래 결과에 주의하기 바란다. 단 기본자료형과 참조형을 비교할 순 없다.

10 != new String("cody"); // compile error
10 == 10.0f; // true
0.1 == 0.1f; // false

참조형인 String 타입의 문자열을 비교할 때 주의할 점이 있다.

public static boolean foo(String s) {
    s=="sample";
}

foo("sample"); //true
foo(new String("sample")); //false

foo 메소드 호출하며 인자로 "sample"을 전달한 것과 new String("sample")을 전달한 결과가 다르다. "sample"과 같이 String 타입의 값을 만드는 리터럴을 사용해 만들어진 객체는 컴파일러에 의해 동일한 객체를 가리키고 있기 때문이다. "sample"=="sample"은 값을 비교한게 아니라 컴파일러에 의해 만들어진 동일한 객체를 비교한 것임을 명심하자. 원리는 알았으니 문자열의 비교는 ==가 아닌 equals를 사용하자.

3.5 논리 연산자

3.5.1 논리 연산자 && || !

논리연산자로 &&(AND), ||(OR) 연산자가 있다. 논리 연산자는 이항 연산자로 피연산자는 모두 boolean 타입이어야 한다. 논리연산자와 함께 많이 사용되는 연산자로 논리 부정 연산자 !(NOT)이 있다. 논리 부정 연산자는 일항 연산자로 피연산자는 boolean 타입이어야 한다.

  • && AND 연산자. 피연산자 모두 true 일때만 true
true false
true true false
false false false
  • || OR 연산자. 피연산자 중 하나만 true 여도 true
true false
true true true
false true false
  • ! NOT 연산자. 피연산자의 값이 true이면 결과는 false, false이면 true
true false
! false true
  • 기타 &&, || 를 사용해 논리 연산을 수행할 때 효율적인 연산을 하도록 하자.
      예를 들어 && 연산자 좌항과 우항에 각 기 계산식이 들어온 경우 && 연산자의 좌항이 false이면 우항은 계산식이 실행되지 않는다. 마찬가지로 || 연산자는 좌항이 true이면 우항이 계산되지 않으므로 **연산량이 적은 계산을 좌항에 배치하자**
    

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

비트연산자는 피연산자를 "비트" 단위로 논리 연산하다. 피연산자로는 정수(문자 포함)만 사용할 수 있다.

  • &

    • AND 비트 연산자
    • 피연산자 양쪽 모두 1이어야 1이다.
  • |

    • OR 비트 연산자
    • 피연산자 중 한쪽이 1이면 1이다.
  • ^

    • XOR(Exclusive OR) 비트 연산자
    • 피연산자의 비트가 같지 않으면 1이다.
  • ~

    • 비트 전환 연산자
    • 피연산자의 비트를 반전 시킨다. 0이면 1, 1이면 0
  • <<, >>

    • 왼쪽/오른쪽 쉬프트 연산자
    • 비트의 자리를 왼쪽 혹은 오른쪽으로 이동 시킨다. 범위를 벗어난 값은 버려지고, 빈자리는 0으로 채워진다.
      int result = 32>>2; //8 : 00001000
      int result = 8<<2; //32 : 00100000
      
  • 기타

    • 안드로이드에서 | 비트 연산자를 쓰는 경우가 종종있다. 비트 연산의 특성 상 특정 비트의 값을 변경할 때 사용된다.
      intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION|Intent.FLAG_ACTIVITY_CLEAR_TOP);
      
    • 비트연산자를 쓰는 이유가 빠르기 때문에 쓴다 이야기하는건 옳지 않다.2, 4, *8, /2, /4, /8과 같은 산술 연산의 경우 컴파일러가 좌측 쉬프트, 우측 쉬프트 연산으로 대체하는 경우가 있어 속도적인 이점이 전혀 없을 수도 있다. 오히려 "가독성"을 해칠 수 있기 때문에 빠르다는 이유로 쉬프트 연산을 고집하진 말자.

3.6 그 외의 연산자

3.6.1 조건 연산자 ? : (Ternary 3중' Conditional Operator)

조건 연산자는 3항 연산자로, 조건연산자와 함께 조건식, 조건식이 참 일 때의 값, 조건식이 거짓일 때의 값을 사용한다.

int max = 0;
if( x>y ) {
    max = x;
} else {
    max = y;
}

int max = x>y?x:y;

3.6.2 대입 연산자 =

  • 대입 연산자는 2항 연산자로, 연산자 우측의 값을 좌측에 저장한다. 대입 연산자의 좌측 피연산자를 l-value(left value), 우측의 피연산자를 r-value(right value)라 한다.
  • r-value는 상수, 리터럴, 변수를 사용할 수 있는 반면, l-value는 우측 피연산자의 값이 저장되야 하므로 변경 가능한 변수만 사용할 수 있다.

  • 복합 대입 연산자 (Compound Assignment Operator) 2항 연산자로, 좌항과 우항을 대입연산자 앞에 작성한 연산(산술연산, 비트연산)을 수행한 후 그 결과를 좌항에 대입한다. 대입 연산자의 특성 상 좌항은 변수만 사용될 수 있다.

    x += y; // x = x+y
    x -= y; // x = x-y
    x *= y; // x = x*y
    x /= y; // x = x/y
    x %= y; // x = x%y
    x <<= y; // x = x<<y
    x \>>= y; // x = x>>y
    x &= y; // x = x&y
    x ^= y; // x = x^y
    x |= y; // x = x!y