연산자
Operator
연산자 (Operator)
산술연산자
- Binary Operator(이항)
- 덧셈 (Addition,
+
)- 피연산자 중 하나가 문자열이면 다른 피연산자는 문자열로 변환되어 수행됨
System.out.println("Total: " + 3 + 4); // Total: 34 System.out.println("Total: " + (3 + 4)); // Total: 7
- 뺄셈 (Subtraction,
-
) - 곱셈 (Multiplication,
*
) - 나눗셈 (Division,
/
)- 결과는 integer, 값을 잃을 수도 있다.
- 0으로 나누게 되면
ArithmeticException
발생
7 / 3 // 2 7 / 3.0f // 2.33333f 7 / 0 // ArithmeticException!! 7 / 0.0 // positive infinity 0.0/0.0 // NaN
- 나머지 연산 (Modulo,
%
)- 한 숫자를 다른 숫자로 나눈 후 나눗셈의 나머지 또는 부호있는 나머지를 반환
7 % 3 // 1 4.3 % 2.1 // 0.1 4 % 0 // ArithmeticException !! 4 % 0.0 // NaN
비트연산자 (bitwise operator) & Shift Operators
- Binary Operator(이항)
- bit 단위로 논리 연산을 할 때 사용하는 연산자, 비트 단위로 전체 비트를 왼쪽이나 오른쪽으로 이동시킬 때도 사용함.
- floating-point, boolean, array, object 는 비트 연산자를 사용할 수 없음
- 결과는 int값이나 연산자 중 하나가 long이면 결과는 long 이다.
~
(비트NOT
연산)- 비트를 1이면 0으로, 0이면 1로 반전시킴
byte b = ~12; // ~00001100 => 11110011 ( or -13 decimal)
&
(비트AND
연산)- 대응되는 비트가 모두 1이면 1을 반환
10 & 7 // 00001010 & 00000111 => 00000010 (or 2)
|
(비트OR
연산)- 대응되는 비트 중에서 하나라도 1이면 1 반환
10 | 7 // 00001010 | 00000111 => 00001111 (or 15)
^
(비트XOR
연산)- 대응되는 비트가 서로 다르면 1 반환
10 ^ 7 // 00001010 ^ 00000111 => 00001101 (or 13)
-
<<
(Left Shift
연산)💡 x « n 은 x * 2^n 의 결과와 같다.
- 지정한 수만큼 비트들을 전부 왼쪽으로 이동시킴
10 << 1 // 0b00001010 << 1 = 00010100 = 20 = 10*2 7 << 3 // 0b00000111 << 3 = 00111000 = 56 = 7*8 -1 << 2 // 0xFFFFFFFF << 2 = 0xFFFFFFFC = -4 = -1*4 // 0xFFFF_FFFC == 0b1111_1111_1111_1111_1111_1111_1111_1100
-
>>
(Right Shift 연산)💡 x » n 은 x / 2^n 의 결과와 같다.
- 부호를 유지하면서 지정한 수만큼 비트를 전부 오른쪽으로 이동시킴
10 >> 1 // 00001010 >> 1 = 00000101 = 5 = 10/2 27 >> 3 // 00011011 >> 3 = 00000011 = 3 = 27/8 -50 >> 2 // 11001110 >> 2 = 11110011 = -13 != -50/4
관계연산자
- 개념
- 비교 연산자라고도 함
- 두개의 변수 또는 리터럴을 비교하는 데 사용되는 연산자
- 연산결과는
true
또는false
- 이항연산자이므로 비교하는 피연산자의 자료형이 서로 다를 경우에는 자료형의 범위가 큰 쪽으로 형변환하여 피연산자의 타입을 일치시킨 후 비교한다.
-
대소비교 연산자
> : 왼쪽 항이 크면 true, 아니면 false 반환 < : 왼쪽 항이 작으면 true, 아니면 false 반환 >= : 왼쪽 항이 오른쪽 항보다 크거나 같으면 true, 아니면 false 반환
-
등가비교 연산자
== : 두개 항의 값이 같으면 true, 아니면 false 반환 != : 두개 항이 다르면 true, 아니면 false 반환
논리연산자
- 조건문과 반복문에서 조건식 간의 결합에 사용
- 피연산자로
boolean
형 또는boolean
형 값을 결과로 하는 조건식만 허용
- 피연산자로
&&
가||
연산보다 우선순위가 높으므로 한 조건식에 같이 사용할 때는 괄호를 사용하여 우선순위를 명확히 해 줘야 한다.- 효율적인 연산을 함
||
연산의 경우 어느 한 쪽만true
여도 전체 연산결과가true
이므로 좌측의 피연산자가true
이면 우측의 피연산자 값은 검사하지 않는다.&&
연산의 경우 어느 한쪽만false
여도 전체 연산결과가false
이므로 좌측의 피연산자가false
이면 우측의 피연산자 값은 검사하지 않는다.- 연산 속도를 위해 단락회로평가(short circuit evaluation)를 고려 하여 코딩하는 것이 좋음.
&
또는|
을 사용한다면 양쪽 다 평가한다.
&& (논리곱) : 두 항이 모두 true인 경우에만 true, 아니면 false 반환
|| (논리합) : 두 항 중 하나의 항이 true이면 결과 값은 true, 아니면 flase 반환
- 논리부정연산자 (단항연산자)
! (부정) : true를 false로 바꾸고, false인 경우 true로 변경
instanceof Operator
참조변수가 참조하는 인스턴스의 실제 타입을 체크하는데 사용하는 연산자이다.
연산결과는 true
또는 false
이다. true
이면 해당 타입으로 형변환이 가능하다.
"string" instanceof String // True (all strings are instances of String)
"" instanceof Object // True (strings are also instances of Object)
null instanceof String // False (null is never an instance of anything)
Object o = new int[] {1,2,3};
o instanceof int[] // True (the array value is an int array)
o instanceof byte[] // False (the array value is not a byte array)
o instanceof Object // True (all arrays are instances of Object)
다형성을 통해 조상타입의 참조변수로 자손타입의 인스턴스를 가리킬 수 있게 되었기 때문에 참조변수의 타입과 참조변수가 가리키는 인스턴스의 타입이 항상 같지는 않는다.
따라서 참조변수가 가르키는 인스턴스 타입이 어떤 것인지 확인하기 위해서 instanceof
연산자를 사용하는 것
// Use instanceof to make sure that it is safe to cast an object
if (object instanceof Test) {
Test t = (Test) object
}
assignment operator
변수에 값 또는 수식의 연산결과를 저장(in 메모리
)하는데 사용한다. 왼쪽에는 반드시 변수가 위치해야 하며, 오른쪽에는 리터럴이나 변수 또는 수식이 올 수 있다.
연산 진행 방향이 오른쪽에서 왼쪽 (←) 이다. (right-associative)
a=b=c
⇒a=(b=c)
기본적인 assignment operator 는 =
이다. (equality operator 인 ==
와 혼동 주의)
또한 다른 연산자와 결합하여 op=
와 같은 방식으로 사용이 가능하다.
i = i + 3 => i += 3
i *= 10 + j => i = i * (10 + j)
...
+= -= *= /= %= // Arithmetic operators plus assignment
&= |= ^= // Bitwise operators plus assignment
<<= >>= >>>= // Shift operators plus assignment
x +=2
와x = x+2
는 거의 같다, 다만op=
형식을 쓸 때 왼쪽피연산자가 한번만 evaluated 된다.
Lambda expression (→)
람다 표현식은 실행 가능한 자바 코드의 anonymous collection 으로, 메서드와 비슷하지만 이름이 필요하지 않고 method body에서 바로 구현이 가능한 표현식이다.
TestSomething test = () -> System.out.println("Hello, Lamda");
자바는 객체지향 언어이기 때문에 위 람다식을 변수에 할당 가능하고, 메서드의 파라미터로 전달 및 리턴타입으로 리턴할 수 있다. (람다식 자체를 리턴 또한 가능하다.)
표현 방법
(인자 리스트) → { body }
인자리스트
- 인자 리스트는 없을 수도 있고, 한개 또는 여러개 일수도 있다.
- 인자의 타입은 생략 가능, 컴파일러가 infer(추론) 하지만 명시할 수도 있다.
(String str, Integer one)
1. 인자가 없는 경우: () -> { body } // 구현해야 할 추상메서드가 파라미터를 아무것도 받지 않을 때
2. 인자가 한개인 경우: (one) or one -> { body }
3. 인자가 여러개인 경우: (one, two) -> { body }
바디
- 화살표 오른쪽에 함수 내용을 구현한다.
- 코드가 한줄인 경우
{}
를 생략 가능하고,return
도 생략 가능하다. - 코드가 여러 줄인 경우
{}
를 사용하여 묶는다.
특징
💡
final
또는effective final
인 경우에만 로컬 변수 참조 가능 람다는 람다를 감싸는 별도의 scope 이 존재하여 쉐도잉을 하지 않는다.
-
로컬변수 참조 (local variable capture) 란
private void run() { final int baseNumber = 25; // 로컬클래스 class LocalClass { void printBaseNumber() { System.out.println(baseNumber); // 로컬변수 참조 } } // 익명클래스 Consumer<Integer> integerConsumer = (i) -> { System.out.println(baseNumber) // 로컬 변수 참조 }; }
-
람다의 로컬변수 참조
람다 표현식을 사용할 때 local variable capture를 하려면 그 변수가
final
이거나effective final
인 경우에만 참조할 수 있다.-
effective final
자바 8부터 지원하는 기능으로 사실상
final
인 변수이다.final
키워드를 사용하지 않은 변수를 익명클래스 구현체 또는 람다에서 참조할 수 있다.
private void run() { int baseNumber = 25; // effective final IntConsumer printInt = (i) -> { System.out.println(i + baseNumber); // 참조 가능 } // 컴파일 에러 발생 IntConsumer printNotFinal = (~~baseNumber~~) -> { // Variable 'baseNumber' System.out.println(baseNumber) // is already defined }; // in the scope }
위 에러가 발생하는 이유는 다음과 같다.
💡 람다는 run() 메소드와 scope이 같음 (쉐도잉이 적용 안됨)
→ 같은 scope에서는 동일한 변수명을 사용할 수 없음 (concurrency 문제 발생 할 수있음)
→ 컴파일에러 발생-
쉐도잉이란 ?
private void run() { // ① int baseNumber = 25; // 로컬클래스 class LocalClass { void printBaseNumber() { // ② int baseNumber = 12; System.out.println(baseNumber); // 12 출력 } } // 익명클래스 Consumer<Integer> integerConsumer = (i) -> { int baseNumber = 12; System.out.println(baseNumber) // 12 출력 }; }
위 코드에서 로컬클래스와 익명클래스는 run() 메소드 안에 있지만 run 메소드 scope과 다른 scope에 있어서 ①
baseNumber
가 ②baseNumber
로 가려져서 12가 출력이 된다. 이 것을 쉐도잉 이라 한다.
-
Conditional operator
삼항연산자는 세 개의 피연산자를 필요로 하기 때문에 삼항 연산자라 불린다.
if문
으로 바꿔 쓸 수 있으며 간단한 if문
을 삼항 연산자로 사용하면 코드를 보다 간단히 할 수 있다.
표현방법
-
조건식 ? 피연산자1 : 피연산자2
int max = (x > y) ? : x : y; String name = (name != null) ? name : "unknown"
조건식의 연산 결과가
true
이면 결과가피연산자1
,false
이면피연산자2
가 결과값이 된다.피연산자들에는 주로 값이 오지만 경우에 따라 연산식이 올 수도 있다.
-
if문 ↔ 삼항연산자
// if문 사용
if (x > 0 ) {
result = x;
} else {
result = -x;
}
// 삼항연산자 사용
result = (x > 0 ) ? x : -x;
연산 진행방향
3 * 4 * 5 // 연산 진행방향이 왼쪽에서 오른쪽인 경우 ① (3 * 4) ② 12 * 5
x = y = 3 // 연산 진행방향이 오른쪽에서 왼쪽인 경우 ① y = 3 ② x = y
- 산술 > 비교 > 논리 > 대입 (대입은 제일 마지막에 수행)
- 단항 (1) > 이항(2) > 삼항(3) (단항 연산자의 우선순위가 이항 연산자보다 높다)
- 단항 연산자와 대입 연산자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽이다.
1. -x + 3 // 단항 연산자가 이항 연산자보다 우선순위가 높음 -> x의 부호를 바꾼 다음 덧셈 수행
// - 는 뺄셈 연산자가 아니라 부호 연산자임.