티스토리 뷰
산술 연산자
사칙 연산 (+,-,*,/)
타입에 유의하자
int a = 1_000_000; int b = 2_000_000; long c = a * b; System.out.println("c = " + c); //c = -1454759936
a*b 를 수행하는 과정에서 이미 overflow가 발생하기 때문에 예상과 달리 음수값이 나온다.
int a = 1_000_000;
int b = 2_000_000;
long c = (long)a * b;
System.out.println("c = " + c); //c = 2000000000000
해결방법은, 한 쪽에 casting을 걸어서 연산 과정 중에 자동 형변환이 이루어지도록 하면 된다.
리터럴 연산은 연산 중간에 형변환되지 않는다.
char c1 = 'a'; char c2 = c1+1; //컴파일 에러 char c3 = 'a'+1;
char c3 = 'a'+1;
는 왜 컴파일 에러가 뜨지 않을까?
리터럴 연산의 경우, 컴파일러가 미리 계산을 완료하기 때문에 실행할 때 덧셈 연산이 수행되지 않는다. 따라서 연산 도중에 자동형변환이 일어날 일이 없기 때문에 자동형변환에 따른 타입 불일치 에러가 발생하지 않는 것이다.
비트 연산자
and( & ), or ( | ), xor( ^ )
피연산자로 정수만 사용 가능
정수를 이진수로 바꿔서 각각의 자리에 있는 숫자끼리 비트 연산을 수행
비트연산
x y x|y x&y x^y 1 1 1 1 0 1 0 1 0 1 0 1 1 0 1 0 0 0 0 0
int x = 0xAB; //10101011
int y = 0xF; //00001111
long z = 0xF; //00001111 (마지막 8비트만 표기)
System.out.println(Integer.toBinaryString(x | y)); //10101111
System.out.println(Integer.toBinaryString(x & y)); //1011
System.out.println(Integer.toBinaryString(x ^ y)); //10100100
System.out.println(Integer.toBinaryString(~(x ^ y))); //11111111111111111111111101011011
System.out.println(Long.toBinaryString(~(x ^ z)));
//1111111111111111111111111111111111111111111111111111111101011011
System.out.println(Long.toBinaryString(~(x ^ z)));
의 결과를 보면, 비트 연산도 산술 연산과 마찬가지로 연산 과정 중간에 자동형변환이 일어나는 것을 확인할 수 있다.
not ( ~ )
- 피연산자를 이진수로 바꿔서 0은 1로, 1은 0으로 바꾼다.
- 피연산자의 타입이 int보다 작으면(byte, short) int로 자동형변환 후 연산한다. 즉, 연산결과는 항상 32자리의 2진수이다.
byte p = 10; //1010
System.out.println(Integer.toBinaryString(p)); //1010
System.out.println(Integer.toBinaryString(~p)); //11111111111111111111111111110101
System.out.println(Integer.toBinaryString(~p + 1)); //11111111111111111111111111110110
System.out.println(Integer.toBinaryString(~~p)); //1010
shift ( >> , << )
- 2진수로 변환한, 피연산자의 각 자리에 있는 수를 오른쪽 혹은 왼쪽으로 이동시킴
- '<<' 연산자
- 왼쪽으로 이동시킨 후 빈 자리를 0으로 채움
x << n
==x * 2^n
- '>>'
- 오른쪽으로 이동
- 양수일 경우, 빈 자리를 0으로 채움
- 음수일 경우, 빈 자리를 1로 채움
x >> n
==x / 2^n
- 오른쪽으로 이동
int n = 8;
System.out.println(Integer.toBinaryString(n)); //1000
System.out.println(Integer.toBinaryString(n >> 1)); //0100
System.out.println(Integer.toBinaryString(n >> 2)); //0010
System.out.println(Integer.toBinaryString(n >> 3)); //0001
System.out.println(Integer.toBinaryString(n >> 4)); //0000
System.out.println(Integer.toBinaryString(n >> 5)); //0000
//1000 --> 우측 피연산자가 bit수 이상일 경우, (우측 피연산자 % bit수) 만큼 자릿수를 이동
System.out.println(Integer.toBinaryString(n >> 32));
System.out.println(Integer.toBinaryString(n >> 33)); //0100
관계 연산자 (비교 연산자)
두 피연산자를 비교하는 데 사용되는 연산자.
- 두 피연산자의 타입이 서로 다를 경우에는 자료형 범위가 큰 쪽으로 자동형변환 발생
- 대소비교 연산자 ('>', '<', '>=', "<=")
- boolean을 제외한 Primitive type 사용 가능
- Reference type 사용 불가
- 등가비교 연산자 ('==', '!=')
- Primitive type, Reference type 모두 사용 가능
- Primitive type : 변수에 저장되어 있는 값이 같은지 비교
- Reference type : 두 참조 변수가 같은 객체를 가리키고 있는지 비교
- Primitive type, Reference type 모두 사용 가능
System.out.println(10==10.0f); //true (10 -> 10.0f 형변환)
System.out.println(0.1==0.1f); //false
System.out.println(0.1==0.1f);
은 왜 false로 출력될까?
그 이유는 실수형이 저장될 때 근사값으로 저장되어 오차가 발생할 수 있기 때문이다.
그렇다면 float와 double을 올바르게 비교하려면 어떻게 해야 할까?
- Double -> float 타입으로 형변환
- 어느 정도 오차는 감수하고, 앞에서 몇 자리만 잘라서 비교
문자열 비교
- '==' 비교 : 문자열이 같은 객체인지 비교 -> 문자열이 같더라도 다른 객체라면 비교 결과값이 false로 나올 수 있음.
equals()
메소드 : 문자열 내용이 같은지 비교
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1.equals("abc")); //true
System.out.println(str2.equals("abc")); //true
System.out.println(str1 == "abc"); //true
System.out.println(str2 == "abc"); //false -> 다른 객체이기 때문
instanceof
- Reference type이 참조하고 있는 instance의 실제 타입을 알아보기 위해 사용 (주로 조건문)
참조변수 instanceof 타입(클래스명)
- 참조변수의 타입이 아니라, 참조변수가 가리키고 있는 실제 객체의 타입에 의해 결과값이 결정된다.
- LSP(리스코프 치환 원칙) 를 위반하는 코드에서 주로 나타나는 연산자
-> instanceof 가 자주 등장한다면, 리팩토링의 대상이 아닌지 점검해보는 것이 좋다. - instanceof 결과가 참이라는 것은 참조변수가 검사한 타입으로 변환될 수 있음을 의미한다.
public class InstanceofTest {
public static void main(String[] args) {
FireEngine fe = new FireEngine();
if (fe instanceof FireEngine) {
System.out.println("FireEngine");
}
if (fe instanceof Car) {
System.out.println("Car");
}
if (fe instanceof Object) {
System.out.println("Object");
}
}
static class Car{}
static class FireEngine extends Car{}
}
FireEngine
Car
Object
위 조건문이 모두 참이므로, FireEngine은 Car, Object로 변환가능하다. 바꿔 말하면, Car 참조변수와 Object 참조변수가 FireEngine instance를 담을 수 있다.
논리 연산자
둘 이상의 조건을 연결해서 하나의 식으로 표현할 수 있게하는 연산자. 피연산자로는 boolean 혹은 조건문이 올 수 있다.
- && (And) 연산자 : 두 피연산자가 모두 true일 경우에만 true 반환
- || (Or) 연산자 : 두 피연산자 중 어느 한 쪽이 true이면 true 반환
- !(논리 부정) 연산자 : 하나의 피연산자의 boolean 값을 toggle
short circuit evaluation (효율적인 연산)
- 만약 하나의 피연산자 만으로 논리 연산의 결과값을 도출할 수 있는 경우, 나머지 피연산자를 확인하지 않고 논리 연산을 종료
- short circuit evaluation 특징을 잘 활용하면 연산 속도 향상 가능
- 가령 '||' 연산의 경우, 하나의 조건문만 참이어도 최종 결과값이 참이 되기 때문에, 두 개의 조건문 중 참이 될 가능성이 높은 조건문을 왼쪽에 배치해서 연산 속도를 빠르게할 수 있다.
- '&' , '|' 연산자는 short circuit evaluation 을 하지 않고, 두 조건문을 모두 체크한다.
int a = 5;
int b = 0;
System.out.println(a != 0 || ++b!=0); //true
System.out.println(b); //0 (위에서 ++b 연산이 생략된 것을 알 수 있음)
assignment(=) operator
변수와 같은 저장 공간에 값 또는 수식의 연산결과를 저장하는 연산자
- 오른쪽 피연산자(rvalue)의 값을 왼쪽 피연산자(lvalue)에 저장
- 연산 결과로는 저장된 값을 반환한다.
- 연산 진행 방향 :
오른쪽 -> 왼쪽
x = y = 3
- y = 3 저장
y = 3
연산 결과로 3 반환- 연산의 결과 값인 3이 x에 저장
- lvalue는 값을 변경할 수 있는 변수만 올 수 있다.
- 즉, 리터럴이나 final이 붙은 변수는 lvalue가 될 수 없다.
int a;
System.out.println(a = 5); //5
final int b = 3;
b = 5; // 컴파일 에러
화살표(->) 연산자
메서드로 전달할 수 있는 익명함수를 단순화한 것.
람다 파라미터 -> 람다 바디
동적 파라미터를 이용할 때 익명 클래스 등 판에 박힌 코드를 작성할 필요가 없어진다. 즉, 간결하게 코드를 작성할 수 있다.
//익명 클래스 Comparator<Apple> byWeight = new Comparator<Apple>() { @Override public int compare(Apple a1, Apple a2) { return a1.getWeight().compareTo(a2.getWeight()); } }; //람다식 Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
익명 클래스란, local class와 비슷한 개념으로, 클래스 선언과 인스턴스화를 동시에 할 수 있는 문법이다.
동적 파라미터 : 파라미터를 받은 메서드 내부에서 다양한 동작을 수행할 수 있도록, 기능이 확정되지 않은 코드블록을 파라미터로 넘긴다. 이렇게 어떤 코드블록이 오는지에 따라 다양한 역할을 수행할 수 있는 파라미터를 동적 파라미터라고 한다.
더 자세한 내용은 15주차 : 람다식
을 공부할 때 다루도록 하자.
3항 연산자
3개의 피연산자를 필요로 하는 연산자
조건식 ? 식1 : 식2
- 조건식이 참일 경우, 식1의 연산 결과가 최종 결과값이 된다.
- 조건식이 거짓일 경우, 식2의 연산 결과가 최종 결과값이 된다.
int x = -5;
int absX = (x >= 0) ? x : -x;
System.out.println("absX = " + absX); // absX = 5
연산자 우선 순위
종류 | 연산순서 | 연산자 | 우선순위 |
---|---|---|---|
단항 연산자 | <--- | ++ -- + - ~ ! (type) | 높음 |
산술 연산자 | * / % | ||
+ - | |||
<< >> | |||
비교 연산자 | < > <= >= instanceof | ||
== != | |||
논리 연산자 | & | ||
^ | |||
| | |||
&& | |||
|| | |||
삼항 연산자 | ?: | ||
대입 연산자 | <--- | = += -= ... | 낮음 |
(optional) Java 13. switch 연산자
기존 switch statement
public enum Day { SUNDAY, MONDAY, TUESDAY,
WEDNESDAY, THURSDAY, FRIDAY, SATURDAY; }
// ...
int numLetters = 0;
Day day = Day.WEDNESDAY;
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
numLetters = 6;
break;
case TUESDAY:
numLetters = 7;
break;
case THURSDAY:
case SATURDAY:
numLetters = 8;
break;
case WEDNESDAY:
numLetters = 9;
break;
default:
throw new IllegalStateException("Invalid day: " + day);
}
System.out.println(numLetters);
// https://docs.oracle.com/en/java/javase/13/language/switch-expressions.html
문제점
break
누락으로 인한 human fault 발생 가능성- 값을 return할 수 없음
- 장황한 코드
Java 12, 새로운 switch expression
Day day = Day.WEDNESDAY;
System.out.println(
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> throw new IllegalStateException("Invalid day: " + day);
}
);
// https://docs.oracle.com/en/java/javase/13/language/switch-expressions.html
바뀐 점
- '->' 연산자 사용,
break
사용하지 않음. - 여러 옵션을 한 줄에 표현 가능
- Expression 이기 때문에 결과값을 바로 사용할 수 있음
Java 13, yield 추가
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> {
System.out.println(6);
yield 6;
}
case TUESDAY -> {
System.out.println(7);
yield 7;
}
case THURSDAY, SATURDAY -> {
System.out.println(8);
yield 8;
}
case WEDNESDAY -> {
System.out.println(9);
yield 9;
}
default -> {
throw new IllegalStateException("Invalid day: " + day);
}
};
// https://docs.oracle.com/en/java/javase/13/language/switch-expressions.html
바뀐 점
yield
예약어를 통해 case 별, switch expression의 결과값을 반환 가능
Java 13에서도 switch 내부에서 ' : '(semi colon) 을 사용할 수 있다. 하지만 이 경우, break 를 명시해야 한다.
따라서, break를 사용할 필요가 없는 ' -> ' 을 쓰도록 하자.
백기선님 리뷰 영상
int mid = start + (end - start) / 2;
: overflow 방지 가능int mid = (start + end) >>> 1;
: unsigned shift- xor 을 이용해서 array 중 한 번만 등장하는 element 찾기
References
- https://docs.oracle.com/en/java/javase/13/language/switch-expressions.html
- 자바의 정석 (도우출판)
- 모던 자바 인 액션 (한빛미디어)
'JAVA' 카테고리의 다른 글
[자바 스터디] 6주차 : 상속 (0) | 2022.01.30 |
---|---|
[자바 스터디] 5주차 : 클래스, 패키지 (0) | 2022.01.21 |
[자바 스터디] 4주차 : 제어문 (0) | 2022.01.15 |
[자바 스터디] 2주차 : 자바 데이터 타입, 변수 그리고 배열 (0) | 2022.01.01 |
[자바 스터디] 1주차 : JVM이란 무엇이며 JAVA 코드는 어떻게 실행되는가 (0) | 2021.12.26 |
- Total
- Today
- Yesterday
- 토비
- 데코레이터패턴
- 백기선
- OOP
- 프록시패턴
- SOLID
- 서비스추상화
- 코딩테스트
- 객체지향
- 카카오
- BOJ
- 김영한
- 템플릿콜백
- 디자인패턴
- ec2
- 프록시
- gracefulshutdown
- 자바스터디
- 토비의스프링
- 자바
- AOP
- 코테
- provider
- 메서드레퍼런스
- 스프링
- java
- 프로그래머스
- 예외처리
- 토비의봄TV
- c++
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |