연산자
목표
자바가 제공하는 다양한 연산자 학습.
목차
- 산술 연산자
- 비트 연산자
- 관계 연산자
- 논리 연산자
- instanceof
- assignment(=) operator
- 화살표(->) 연산자
- 3항 연산자
- 연산자 우선 순위
- (optional) Java 13. switch 연산자
int result = n1 + n2;
1) System.out.println("정수형 나눗셈 : " + 7/3);
2) System.out.println("실수형 나눗셈 : " + 7.0f/3.0f);
3) System.out.println("실수형 나눗셈 : " + (float)7/3);
1의 경우 두 피연산자가 정수일 때 나머지는 버려지고 몫인 2가 출력된다.
2의 경우 두 피연산자가 실수일 때 실수형 나눗셈이 적용되어 나머지 없이 '2.3333333'이 출력된다.
3의 경우는 실수형과 정수형의 나눗셈인데, 정수형이 실수형으로 자동 형 변환 되어 2와 같이 실수형 나눗셈이 적용된다.
(출처 : https://heurinbada.tistory.com/20)
n1 & n2 // n1과 n2를 비트 단위로 AND 연산
n1 | n2 // n1과 n2를 비트 단위로 OR 연산
n1 & n2 // n1과 n2를 비트 단위로 XOR 연산
~n // n의 모든 비트를 반전시켜서 얻은 결과를 반환
int n1 = 13;
int n2 = 7;
int n3 = n1 & n2;
위와 같이 동일한 위치에 있는 비트별로 연산하고, 그 결과를 하나의 정수로 반환한다.
피연산자는 반드시 정수이어야 한다.
언뜻 하트웨어 컨트롤을 떠올리기 쉽지만 일반적으로 응용 프로그램 개발에서도 매우 유용하게 사용된다.
비트 쉬프트 연산자
(출처 : https://unknownyun.blogspot.com/2018/08/blog-post_59.html)
- '>>'와 '>>>'의 차이점은 양수, 음수를 판단하는지의 여부이다.
- 왼쪽으로 비트 열을 n칸 이동시키면 2의 n제곱을 곱하고 오른쪽으로 이동시키면 2의 제곱 나눗셈 하는 결과가 된다.
- 비트를 이동시키는 연산은 곱셈과 나눗셈 보다 CPU에게 있어서 부담이 되지 않는다.
성능을 생각한다면 2의 배수 단위로 값을 증감할 때는 비트 연산자를 사용해 보도록 하자.
이 밖에도 비트 연산자의 활용 범위는 한정 지을 수 없을 만큼 다양하게 활용되고 있다고 한다.
그 활용에 대해서는 아래 링크를 달아 두겠다.
https://shoark7.github.io/programming/knowledge/some-useful-bit-tricks-and-usages
n1 < n2 // n1이 n2보다 작은가?
n1 > n2 // n1이 n2보다 큰가?
n1 <= n2 // n1이 n2보다 작거나 같은가?
n1 >= n2 // n1이 n2보다 크거나 같은가?
n1 == n2 // n1이 n2보다 같은가?
n1 != n2 // n1이 n2보다 다른가?
A && B // A와 B 모두 true이면 연산결과는 true (논리 AND)
A || B // A와 B 둘 중 하나라도 true이면 연산결과는 true(논리 OR)
!A // 연산결과는 A가 true이면 false, A가 false이면 true(논리 NOT)
이 연산에 대한 진리 표는 아래와 같다.
(출처 : https://m.blog.naver.com/PostView.nhn?blogId=brickbot&logNo=220508046954&proxyReferer=https:%2F%2Fwww.google.com%2F)
class SCE
{
public static void main(String[] args)
{
int num1, num2 = 0;
boolean result;
result = (num1+=777)<0 && (num2+=777)>0;
// num1은 777이고 num2는 0
result = (num1+=777)>0 && (num2+=777)>0;
// num1은 777+777이고, num2는 0
}
}
그러므로 논리 연산자를 사용할 때는 복잡한 로직의 피연산자는 오른편에 두고 간단한 로직의 피연산자는 왼편에 둠으로써 연산의 성능 향상을 시키자.
public class InstanceofExam {
public static void main(String[] args) {
A a = new A();
B b = new B();
//객체 a는 자기 자신의 객체이기 때문에 형변환 가능.
System.out.println(a instanceof A); //true 출력
//객체 b는 A의 자식객체이기 때문에 A로 형변환 가능.
System.out.println(b instanceof A); //true 출력
//객체 a는 B의 부모객체이기때문에 형변환 불가능.
System.out.println(a instanceof B); //false 출력
//객체 b는 자기 자신의 객체이기때문에 형변환 가능.
System.out.println(b instanceof B); //true 출력
}
}
class A{
}
class B extends A{
}
부모객체가 자식객체 타입의 형변환이 불가능한데,
이는 자식 클래스의 요소들이 부모 클래스의 요소를 포함하고 있기 때문이다.
즉 부모객체가 자식객체타입으로 형변환을 하려는 것은
좁은 범위가 넓은 범위를 커버하려는 것과 같다.
Runnable runnable = new Runnable(){ public void run(){ ... } } // 람다식 사용 x
Runnable runnable = () -> { ... } // 람다식 사용 o
함수 정의 형태를 띠고 있지만 런타임 시에 인터페이스의 익명 구현 객체로 생성된다. 위의 코드는 Runnable 변수에 대입되므로 람다식은 Runnable의 익명 구현 객체를 생성하게 된다
장점
코드를 간결하게 만들 수 있다. 코드의 의도가 명확히 드러나기때문에 가독성 향상.
함수를 만드는 과정없이 한번에 처리할 수 있기에 코딩하는 시간이 줄어든다.
병렬프로그래밍에 용이.
단점
람다를 사용하면서 만드는 무명함수는 재사용이 불가능.
이로 인해 비슷한 함수를 계속 중복생성할 가능성이 높아서
잘못 쓰면 전체적으로 코드가 지저분해질 수 있다.
디버깅이 다소 까다롭다
재귀로 만들경우에는 다소 부적합한면이 있다.
변수 = (조건문) ? (참일 때 반환 값) : (거짓일 때 반환 값);
int a = (5<4) ? 10 : 20;
(출처 : https://noritersand.github.io/java/java-%EC%97%B0%EC%82%B0%EC%9E%90-operator/)
int jdkClassic(int input) {
int result = 0;
switch(input){
case 1:
case 2:
result = 1;
break;
case 3:
case 4:
result = 2;
break;
default:
result = 3;
break;
}
return result;
}
result를 변수를 만들어야 하고 multiple case를 지원하지 않는다.
int jdk13(int input) {
return switch (input) {
case 1, 2:
yield 1;
case 3, 4:
yield 2;
default:
yield 3;
};
}
참고 서적 : 난 정말 JAVA를 공부한 적이 없어요 (ORANGE MEDIA 윤성우 저)