[whiteship] 자바 온라인 스터디 - 연산자

노력을 즐기는 사람·2020년 11월 25일
0
post-thumbnail

이번 주는 시간의 여유가 있으니 꼼꼼하게 공부해보자

Expressions and Operators

표현식 연산은 자바 인터프리터가 수행한다고 한다.

어쨌든 연산자 우선 순위를 살펴봅시다

연산자 종류와 우선순위 테이블

precedenceassociativityOperatorOperand type(s)Operation performed
16L.object, memberObject member access
[ ]array, intArray element access
( args )method, arglistMethod invocation
++, --variablePost-increment, post-decrement
15R++, --variablePre-increment, pre-decrement
+, -numberUnary plus, unary minus
~integerBitwise complement
!booleanBoolean NOT
14Rnewclass, arglistObject creation
( type )type, anyCast (type conversion)
13L*, /, %number, numberMultiplication, division, remainder
12L+, -number, numberAddition, subtraction
+string, anyString concatenation
11L<<integer, integerLeft shift
>>integer, integerRight shift with sign extension
>>>integer, integerRight shift with zero extention
10L<, <=number, numberLess than, less than or equal
>, >=number, numberGreater than, greater than or equal
instanceofreference, typeType comparison
9L==primitive, primitiveEqual (have identical values)
!=primitive, primitiveNot equal (have different values)
==reference, referenceEqual (refer to same object)
!=reference, referenceNot equal (refer to different objects)
8L&integer, integerBitwise AND
&boolean, booleanBoolean AND
7L^integer, integerBitwise XOR
^boolean, booleanBoolean XOR
6L|integer, integerBitwise OR
|boolean. booleanBoolean OR
5L&&boolean, booleanConditional AND
4L||boolean, booleanConditional OR
3R? :boolean, anyConditional (ternary) operator
2R=variable, anyAssignment
*=, /=, %=variable, anyAssignment with operation
+=, -=, <<=variable, anyAssignment with operation
>>=, >>>=variable, anyAssignment with operation
&=, ^=, |=variable, anyAssignment with operation
1R->arglist, method bodylambda expression
  • P가 높을수록 우선순위가 높다.
  • A의 L은 연산자의 왼쪽에 있는 녀석이 오른쪽에 작용한다는 말이다. 반대로 R은 오른쪽이 왼쪽으로....

필요하면 언제든지 다시와서 살펴보자

연산자 예제 살펴보기

책에 소개된 몇가지 예제를 살펴봅시다


((Integer) o).IntValue();

while ((line = in.readLine()) != null) {}

if ((flags & (PUBLIC | PROTECTED)) != 0) {}

대부분 우리가 교육과정에서 배웠던 수학의 연산순서와 동일하다.
괄호가 보이면 괄호안으로 쭉 들어가서 연산하면되고 왼쪽부터 가장 먼저 쓴 연산자부터 연산하면된다.

예제 바이트 코드 살펴보기

그렇다면 이런 연산은 어떨까

int a = 0, b = 1, c = 1, d = 1;
a = b += c = -~d;

일단 각 연산자들의 정보를 연산자 우선순위 테이블과 매칭시켜보자

  • = P:2, A:R
  • += P:2, A:R
  • ~ P:15, A:R
  • - P:15, A:R

정보를 토대로 먼저 내가 예상하는 연산 순서를 적어보자.

  1. ~-가 가장 높은 연산 순위를 가지고 있다.
  2. -를 먼저 썼으니 - 연산을 수행한다. -> ~d의 부호가 바뀜
  3. 다음 순서인 ~를 실행한다.
  4. 다음엔 가장 왼쪽에 있는 = 가 실행된다.
  5. =를 실행하려고 보니 오른쪽 또한 표현식이므로 표현식을 실행한다
  6. b += c = -~d 에서 +=가 실행된다.
  7. c = -~d에서 = 가 실행된다. -~d 는 아까 연산을 마쳤기 때문에 앞으로 쭉쭉 대입만 한다.

내 생각이 맞는지 저번에 온라인 스터디 때 본 것처럼 바이트 코드를 살펴봅시다.

IntelliJ의 View -> Show Bytecode 를 통해 바이트 코드를 쉽게 볼 수 있다 만세!

    // a: 1, b: 2, c: 3, d: 4에 ISTORE 되어 있다.
   L5
    LINENUMBER 9 L5
    ILOAD 2      -> b를 로드한다.
    ILOAD 4      -> d를 로드한다.
    ICONST_M1    -> int -1을 스택에 넣는다.
    IXOR         -> 두 값을 XOR한 값을 리턴한다.
    INEG         -> 값을 부정한다 (10으로 01)
    DUP          -> 스택 맨 위에 있는 값을 복사한다.
    ISTORE 3     -> c에 저장한다.
    IADD         -> 더한다.
    DUP          -> 복사한다. (메모리 주소를 주지 않고 값만 복사하네)
    ISTORE 2     -> b에 저장한다.
    ISTORE 1     -> 1에 저장한다.

Java bytecode instruction을 참고해서 바이트코드 명령어를 해석해보자.

뇌피셜과의 비교

  1. -~부터 시작하지 않는다.

    가장먼저 -~부터 시작될 줄 알았지만 a = b += c = -~d의 식에서 = 를 기준으로 왼쪽과 오른쪽으로 나누는 것 같다.
    그 후 오른쪽을 살펴보니 표현식이여서 순서대로 진행이 되는 것 같다.

  2. -~-부터 연산하지 않는다.

- 를 연산하려고 보니까 오른쪽이 아직 값이 결정되지 않아서 오른쪽 ~d부터 연산을 한건지 어쩐지 하여튼 ~부터 연산하더라.

- 연산은 INEG 이고 ~INCOST_M1IXOR이다. ICONST_M1은 32개의 1로 이루어진 2진수이고 IXOR 을 통해 비교하고자 하는 값과 ICONST_M1간의 XOR연산을 수행한다.
그래서 -1^d와 같은 바이트 코드로 변환된다.

바이트코드 까보기가 생각보다 재밌다. 컴퓨터 구조 때 배웠던 어셈블리 명령어들이 생각난다. 생각보니 어셈블리어의 특징이 기계어와 일대일 대응이라는데 바이트코드도 기계어로 번역된다고 하니 비슷한 녀석일 수도 있겠다는 생각이 든다.

이런 연산식도 있다.

int a = 0;
System.out.println(++a + ++a * ++a); // answer: 7

연산자의 우선순위가 ++ 이 15이고 +는 13, *가 12이므로 왼쪽부터 순서대로 1씩 증가 시킨 후 *, + 순으로 연산이 진행된다.

1 + 2 * 3 = 7

꺄르륵~ 재밌다 왠지 리뉴얼된 정처기 시험에서나 나올법한 문제..

비교 연산자

== 에 대해서 자세히 살펴봐야할 필요가 있다.

  • primitive type를 비교할 때 피연산자들의 값이 같은지를 확인한다. 이게 대부분 우리가 ==를 사용하면서 바라는 기능이다.
    그러니까 int a = 3, int b = 3일 때 ab의 값이 같은지 확인하고 싶을 때 ==를 사용한다는 말이다.
  • reference type을 비교할 때는 그들이 같은 objectarray를 참조하고 있는지 확인한다. 값을 비교하지 않는다. 그래서 String 을 비교할 때 .equals() 을 쓴다..
  • 형변환을 발생시킨다. 작은 녀석이 큰 녀석으로 변환된다.
    예를들어 short -> float 처럼 말이다..
  • NaN 타입들은 Float.isNan() , Double.isNan() 을 활용하여 비교하자.

자바에서 String을 생성할 때 리터럴로 생성하는 것과 new로 생성하는 것은 큰 차이가 있다고한다.
리터럴로 생성할 때는 string constatnt pool 이라는 영역에 존재하고 new로 생성하면 Heap 영역에 존재한다고 한다.
리터럴로 생성했을 때 String.intern() 메소드가 호출되고 메소드는 생성하려는 문자열이 string constant pool에 존재하는지 체크하고 만약 존재한다면 그 주소값을 반환하고 존재하지 않다면 string constant pool에 저장한 후 새로운 주소값을 반환한다고 한다.
출처 - 코딩팩토리님 블로그bb

예제코드..

        String a = "ab";
        String b = new String("ab");
        System.out.println(a==b); // false
        System.out.println(a.equals(b)); // true

비트 연산자

비트 연산자를 언제 쓸까? 임베디드 개발자들은 자주 쓸까?
취업해서 안드로이드를 개발하거나 백엔드를 개발할 때 퍼포먼스를 극한으로 끌어 올려야한다면 비트 연산자를 사용할 날이 올까? 싶긴 하다. 흠..

코딩 테스트 볼 때 뭔가 유용하게 쓸 수 있을 것 같긴하다.

그냥 이진수로 장난질 하는 연산자이다.

  • ~: 0을 1로 1을 0으로 바꾼다. ex) 0001 -> 1110

  • &: AND 연산자이다. 자리수에 맞게 둘다 1일때만 1이다.

  • |: OR 연산자이다. 하나라도 1이면 1이다.

  • ^: XOR 연산자이다. 두 놈이 달라야 1이다. 0001 ^ 1111 -> 1110

  • <<: 비트의 자릿 수를 입력만큼 왼쪽으로 옮긴다. 옮기는 과정에서 밀려난 비트들은 버려진다. 십진수로 따지면 2^자릿수 만큼을 곱해주는 것과 같다.
    ex) 0101 << 1 = 1010 // 10 * 2

  • >>: 위와 동일하다.. 비트가 오른쪽으로 옮겨지고 십진수로는 나눗셈이다. 옮기는 과정에서 밀려난 비트들은 버려지고 왼쪽에 비어있는 비트들은 가장 최상위 비트와같은 녀석으로 채워진다.
    ex1) 00011011 >> 3 -> 00000011
    ex2) 11001110 >> 2 -> 111100111

  • >>>: 위에 있는 녀석들과 같은데 왼쪽에 빈 비트들은 무조건 0으로만 채워진다.

assignment operator

왼쪽과 오른쪽으로 구분이 된다.

  • 왼쪽: 저장하는 장소
  • 오른쪽: 저장하려는 것들, 표현식

앞으로 프로그래머로 살아가기로 마음을 먹었다면 =같다라고 인식하지 말고 할당하다(assignment) 라고 읽자

산술 연산자 외에도 비트연산자들또한 &=, <<=와 같이 사용할 수 있다.

instanceof

instanceof의 왼쪽과 오른쪽에는 objectarray가 와야한다. 그그리고 서로를 비교한다. 그런데 같은 type인지를 비교한다.
==와 다른점은 값이 아닌 type을 비교한다는 점이다.
null은 어떤 것과도 같은 instance가 아니다.

3항 연산자

(1 == 1)? 1 : 2 의 문법을 가지고 있다. ? 왼쪽의 식이 참이면 :의 왼쪽에 있는 값을 리턴하고 그렇지 않으면 오른쪽의 값을 리턴한다.

우테코 프리코스에 참여할 때는 삼항 연산자를 쓰지 말라고 하던데 다른 쪽은 어떤지 모르겠다.
코딩테스트를 공부할 때나 혼자 개발을 할 때는 가끔 사용하긴했다.

화살표 연산자

lambda expression 이라고 부른다. 람다는 stream api를 활용할 때나 콜백 함수를 넘겨줄때 자주 썼었는데 책을 읽어보니 지금 굳이 다룰만한 내용은 없어 보인다.

profile
노력하는 자는 즐기는 자를 이길 수 없다

0개의 댓글