산술 연산자(Arithmetic Operators)
- 간단한 수학 연산을 하기 위해 사용하는 연산자
- 기본 숫자 및 그에 대응하는 boxed type에서만 작동
- +, -, *, /, %가 존재한다
- '/'는 몫, '%'는 나머지를 의미한다
@Test
@DisplayName("산술 연산")
void op() throws Exception{
int a = 6;
int b = 4;
System.out.println("a/b = " + a / b); //몫
System.out.println("a%b = " + a % b); //나머지
}
비트 연산자(Bitwise Operators)
- 비트 연산자에는 Bitwise Logical Operators, Bitwise Shift Operators로 나눌 수 있다
Bitwise Logical Operators
- AND, OR, XOR, Complement Operator 가 있다
Complement 연산
b는 현재 0000 0110 이고, ~b는 1111 1001
최고 비트가 1이므로 b의 보수는 음수를 의미한다
따라서 음수인 수는 그 수의 보수를 구하여 다시 10진수로 변환해야 한다
따라서 1111 1001의 2의 보수는 0000 0110 + 1 이므로 0000 0111
즉, -7이 된다~
Bitwise Shift Operators
- Signed Left/Right Shift, Unsigned Right Shift가 있다
Signed Left Shift
- 변수 << [이동 횟수]
- 이동 횟수만큼 비트를 왼쪽으로 이동
- 오른쪽의 빈 공간은 0으로 채워짐
- 즉, 왼쪽으로 n번 움직이는 것은 2^n을 곱한 것과 같다
@Test
@DisplayName("비트 leftshift")
void shift() throws Exception{
int x = 12;
System.out.println("12를 2진수 = " + Integer.toBinaryString(x)); //1100
int leftShift = x << 2;
System.out.println("leftShift = " + leftShift); //48
x = -12;
leftShift = x << 2;
System.out.println("leftShift = " + leftShift); //-48
}
Signed Right Shift
- left shift와 형식은 비슷하지만 방향만 다르다
- 이동횟수만큼 비트를 오른쪽으로 이동
- 변수가 음수일 때(가장 왼쪽 비트 1), 빈 공간은 1로 채워짐
- 변수가 양수일 때(가장 왼쪽 비트 0), 빈 공간은 0으로 채워짐
@Test
@DisplayName("비트 right_shift")
void right_shift() throws Exception{
int x = 12;
System.out.println("12를 2진수 = " + Integer.toBinaryString(x)); //1100
int right_shift = x >> 2;
System.out.println("right_shift = " + right_shift); //3
x = -12;
right_shift = x >> 2;
System.out.println("right_shift = " + right_shift); //-3
}
Unsigned Right Shift
- Signed Right Shift와 매우 유사
- 다른 점은 숫자를 이동하고 난 뒤, 양수 음수 상관없이 0으로 채워짐
- 따라서 결과는 항상 양의 정수가 나옴
@Test
@DisplayName("비트 unsigned_right_shift")
void unsigned_right_shift() throws Exception{
int x = 12;
System.out.println("12를 2진수 = " + Integer.toBinaryString(x)); //1100
int unsigned_right_shift = x >>> 2;
System.out.println("unsigned_right_shift = " + unsigned_right_shift); //3
x = -12;
unsigned_right_shift = x >>> 2;
System.out.println("unsigned_right_shift = " + unsigned_right_shift); //1073741821
}
관계 연산자(Relational Operators)
- 관계 연산자는 "비교 연산자(Comparison operators)"라고도 한다
- ==, !=, >, >=, <, <= 가 있지만 우리는 ==을 유심히 봐보자
Java에서 비교를 할 시, ==와 equals 중에 어떤 것을 사용해야 할까?라는 의문이 들 수 있다
나는 대부분 값을 비교할 시 ==을 사용하고, 객체를 비교할 시 무조건 equals를 사용한다
각 숫자에 대응되는 wrapper class들(interget ...)도 equals를 사용한다
- Integer를 비교할 때 ==을 사용 시 -128~127까지는 올바르게 비교 값이 나오지만 그 이후에는 예상치 못한 결괏값이 나올 수 있다
- 해당 부분은 "IntegerCache"를 참고하면 알 수 있다
@Test
@DisplayName("Integer 비교")
void relational() throws Exception{
Integer a = 129;
Integer b = 129;
Integer c = 124;
Integer d = 124;
System.out.println(a == b); //false
System.out.println(c == d); //true
System.out.println(a.equals(b)); //true
System.out.println(c.equals(d)); //true
}
논리 연산자(Logical Operators)
- java에서 제공되는 논리 연산자에는 AND, OR 연산자가 있다
- &&와 || 로 표현
||(OR) 연산자 특징
- 만약 이 앞의 조건이 참이라면 뒤의 연산을 계산하지 않고 true를 반환한다
@Test
@DisplayName("|| 특징")
void logical() throws Exception{
int a = 4;
int b = 5;
if (a >= 4 || ++b > 5) {
System.out.println("b = " + b); //b의 값은 5
}
if (a >= 5 || ++b > 5) {
System.out.println("b = " + b); //b의 값은 6
}
}
instance of
참조 변수 instanceof 클래스
참조 변수가 "참조"하고 있는 "인스턴스"의 실제 타입을 알아보기 위해 사용한다
따라서 형 변환이 가능한지 검사하는 기능 또한 가지고 있다
static class A{}
static class B extends A{}
static class C extends A{}
@Test
@DisplayName("instance of")
void instance_of() throws Exception{
A a = new A();
System.out.println(a instanceof B); //f
System.out.println(a instanceof C); //f
a = new B();
System.out.println(a instanceof A); //t
System.out.println(a instanceof B); //t
System.out.println(a instanceof C); //f
}
처음에 참조 변수 a는 A 클래스를 참조하고 있다
그래서 A 클래스는 B 클래스로 형 변환을 할 수 있을까에 대한 답은 NO다 C 또한 마찬가지
두 번째에서는 참조 변수 a는 B 클래스를 참조하고 있다
B 클래스는 A 클래스로 형 변환할 수 있을까에 대한 답은 YES다
B와 C는 아무런 관계가 없으므로 당연히 NO다
assignment operator(=)
= 연산은 primitive type과 reference type에 따라 의미가 달라진다
primitive에서 int a = 3; 을 하게 되면 우리는 a의 값에 3을 저장한다
reference type에서는 주소 값을 할당하는 것이다
/**
* a = study.querydsl.QuerydslBasicTest$A@3f78a5ed
* b = study.querydsl.QuerydslBasicTest$A@630e5010
* a = study.querydsl.QuerydslBasicTest$A@630e5010
* b = study.querydsl.QuerydslBasicTest$A@630e5010
*/
@Test
@DisplayName("reference type에서 = 연산")
void assignment() throws Exception{
A a = new A();
A b = new A();
System.out.println("a = " + a);
System.out.println("b = " + b);
a = b;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
처음에는 a와 b가 주소값을 가지고 있었다
a = b의 연산을 하고 나서 a와 b 모두 b의 주소값을 참조하고 있게 된다
이 뜻은 a의 주소 값에 b의 주소 값을 저장한다 즉, a는 b가 참조하고 있는 주소 값을 참조한다 라는 뜻이다
reference type의 assignmnet operator는 주소값을 할당해주는 것을 알 수 있다
Compound Assignments
+=, -=, *=, /=, %= 을 compound assignment 라고 한다
a += b 의미는 a = a + b라는 뜻이다
나머지 연산들도 모두 같은 말이다
화살표 연산자(->)
java 8에서 람다의 도입에 따라 화살표 연산자도 등장하게 됐다
이는 람다식을 배우게 되면 사용하게 되는 연산자이다
3항 연산자(Ternary Operator)
- if-else 문을 줄여주기 위해 사용되는 연산자
- 조건 ? true일 경우 행동 : false일 경우 행동
@Test
@DisplayName("|| 특징")
void logical() throws Exception{
int a = 4;
String result1 = a >= 4 ? "참" : "거짓";
String result2 = a > 4 ? "참" : "거짓";
System.out.println("result1 = " + result1); //참
System.out.println("result2 = " + result2); //거짓
}
연산자 우선순위
unary(단항) 연산자와 assignment 연산자는 <- 방향으로 진행, 나머지 연산들은 -> 방향으로 진행된다
산술 -> 비교 -> 논리 -> 대입 순서로 알 수 있다~
Java 13 switch 연산자
Java 12 전 switch 연산
@Test
void post12_switch() throws Exception{
int a = 1;
switch (a) {
case 1:
System.out.println("1");
break;
case 2:
System.out.println("2");
break;
default:
System.out.println("3");
break;
}
}
Java 12 이후 " - > " 사용
switch expression으로 사용할 수 있게 됐다
즉, 단일 값으로 평가되어 명령문에서도 사용할 수 있다
@Test
void switch_12() throws Exception {
int a = 1;
switch (a) {
case 1 -> System.out.println("1");
case 2 -> System.out.println("2");
default -> System.out.println("many");
}
}
여기에 이어서 인자로 사용할 수도 있게 됐다
@Test
void switch_12() throws Exception {
int a = 1;
System.out.println(
switch (a) {
case 1 -> 1;
case 2 -> 2;
default -> 3;
}
);
}
Java 13 yield
Java 13에서 인자로 사용할 수 있기 때문에 변수에도 할당할 수 있게 "yield"를 사용했다
switch statement는 break의 target, switch의 expression은 yield의 타겟이 될 수 있다
@Test
void switch_13() throws Exception {
int num = 1;
int numLetters = switch (num) {
case 2 -> {
System.out.println(7);
yield 7;
}
case 7 -> {
System.out.println(8);
yield 8;
}
case 1 -> {
System.out.println(1);
yield 0;
}
default -> throw new IllegalStateException("Invalid num: " + num);
};
System.out.println("numLetters = " + numLetters);
/**
* 1
* numLetters = 0
*/
}
현재 num 값은 1이므로 case 1에 걸리게 되고 1을 출력하게 된다.
여기서 yield 0을 하게 되면 0을 반환하고, numLetters에는 0이 저장된다
따라서 numLetters를 출력하면 0이 출력된다~
REFERENCES
'Java' 카테고리의 다른 글
정적 메서드는 왜 오버라이딩 되지 않을까? (0) | 2022.08.29 |
---|---|
throw vs throws (0) | 2022.05.17 |
Primitive Type, Reference Type, Literal (0) | 2022.05.03 |
Java volatile keyword (0) | 2022.04.18 |
final 키워드 (0) | 2022.03.05 |