이번 주는 시간의 여유가 있으니 꼼꼼하게 공부해보자
표현식 연산은 자바 인터프리터가 수행한다고 한다.
어쨌든 연산자 우선 순위를 살펴봅시다
precedence | associativity | Operator | Operand type(s) | Operation performed |
---|---|---|---|---|
16 | L | . | object, member | Object member access |
[ ] | array, int | Array element access | ||
( args ) | method, arglist | Method invocation | ||
++, -- | variable | Post-increment, post-decrement | ||
15 | R | ++, -- | variable | Pre-increment, pre-decrement |
+, - | number | Unary plus, unary minus | ||
~ | integer | Bitwise complement | ||
! | boolean | Boolean NOT | ||
14 | R | new | class, arglist | Object creation |
( type ) | type, any | Cast (type conversion) | ||
13 | L | *, /, % | number, number | Multiplication, division, remainder |
12 | L | +, - | number, number | Addition, subtraction |
+ | string, any | String concatenation | ||
11 | L | << | integer, integer | Left shift |
>> | integer, integer | Right shift with sign extension | ||
>>> | integer, integer | Right shift with zero extention | ||
10 | L | <, <= | number, number | Less than, less than or equal |
>, >= | number, number | Greater than, greater than or equal | ||
instanceof | reference, type | Type comparison | ||
9 | L | == | primitive, primitive | Equal (have identical values) |
!= | primitive, primitive | Not equal (have different values) | ||
== | reference, reference | Equal (refer to same object) | ||
!= | reference, reference | Not equal (refer to different objects) | ||
8 | L | & | integer, integer | Bitwise AND |
& | boolean, boolean | Boolean AND | ||
7 | L | ^ | integer, integer | Bitwise XOR |
^ | boolean, boolean | Boolean XOR | ||
6 | L | | | integer, integer | Bitwise OR |
| | boolean. boolean | Boolean OR | ||
5 | L | && | boolean, boolean | Conditional AND |
4 | L | || | boolean, boolean | Conditional OR |
3 | R | ? : | boolean, any | Conditional (ternary) operator |
2 | R | = | variable, any | Assignment |
*=, /=, %= | variable, any | Assignment with operation | ||
+=, -=, <<= | variable, any | Assignment with operation | ||
>>=, >>>= | variable, any | Assignment with operation | ||
&=, ^=, |= | variable, any | Assignment with operation | ||
1 | R | -> | arglist, method body | lambda expression |
필요하면 언제든지 다시와서 살펴보자
책에 소개된 몇가지 예제를 살펴봅시다
((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정보를 토대로 먼저 내가 예상하는 연산 순서를 적어보자.
~
와 -
가 가장 높은 연산 순위를 가지고 있다. -
를 먼저 썼으니 -
연산을 수행한다. -> ~d
의 부호가 바뀜~
를 실행한다. =
가 실행된다. =
를 실행하려고 보니 오른쪽 또한 표현식이므로 표현식을 실행한다b += c = -~d
에서 +=
가 실행된다.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 -> 값을 부정한다 (1은 0으로 0은 1로)
DUP -> 스택 맨 위에 있는 값을 복사한다.
ISTORE 3 -> c에 저장한다.
IADD -> 더한다.
DUP -> 복사한다. (메모리 주소를 주지 않고 값만 복사하네)
ISTORE 2 -> b에 저장한다.
ISTORE 1 -> 1에 저장한다.
Java bytecode instruction을 참고해서 바이트코드 명령어를 해석해보자.
뇌피셜과의 비교
-~
부터 시작하지 않는다.
가장먼저
-~
부터 시작될 줄 알았지만a = b += c = -~d
의 식에서=
를 기준으로 왼쪽과 오른쪽으로 나누는 것 같다.
그 후 오른쪽을 살펴보니 표현식이여서 순서대로 진행이 되는 것 같다.
-~
중 -
부터 연산하지 않는다.
-
를 연산하려고 보니까 오른쪽이 아직 값이 결정되지 않아서 오른쪽~d
부터 연산을 한건지 어쩐지 하여튼~
부터 연산하더라.
-
연산은INEG
이고~
는INCOST_M1
와IXOR
이다.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
일 때 a
와 b
의 값이 같은지 확인하고 싶을 때 ==
를 사용한다는 말이다.reference type
을 비교할 때는 그들이 같은 object
나 array
를 참조하고 있는지 확인한다. 값을 비교하지 않는다. 그래서 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) 라고 읽자
산술 연산자 외에도 비트연산자들또한 &=
, <<=
와 같이 사용할 수 있다.
instanceof
의 왼쪽과 오른쪽에는 object
나 array
가 와야한다. 그그리고 서로를 비교한다. 그런데 같은 type
인지를 비교한다.
==
와 다른점은 값이 아닌 type
을 비교한다는 점이다.
null
은 어떤 것과도 같은 instance
가 아니다.
(1 == 1)? 1 : 2
의 문법을 가지고 있다. ?
왼쪽의 식이 참이면 :
의 왼쪽에 있는 값을 리턴하고 그렇지 않으면 오른쪽의 값을 리턴한다.
우테코 프리코스에 참여할 때는 삼항 연산자를 쓰지 말라고 하던데 다른 쪽은 어떤지 모르겠다.
코딩테스트를 공부할 때나 혼자 개발을 할 때는 가끔 사용하긴했다.
lambda expression 이라고 부른다. 람다는 stream api를 활용할 때나 콜백 함수를 넘겨줄때 자주 썼었는데 책을 읽어보니 지금 굳이 다룰만한 내용은 없어 보인다.