Backend/Java

연산자 (3주차)

가은파파 2021. 1. 12. 14:29

학습할 것

  • 산술 연산자
  • 비트 연산자
  • 관계 연산자
  • 논리 연산자
  • instanceof
  • assignment(=) operator
  • 화살표(->) 연산자
  • 3항 연산자
  • 연산자 우선 순위
  • (optional) Java 13. switch 연산자

# 산술 연산자

산술 연산자인 연산자(+,-,*,/), 나머지 연산자(%), 쉬프트 연산자(<<,>>,<<<)는 모두 두개의 피연산자를 취하는 이항 연산자이다.

  • 이항 연산자는 피연산자의 크기가 4 byte 보다 작으면 4 byte(int형)로 변환한 다음에 연산을 수행한다는 점을 명심해야한다. 
byte, char, short -> int(자료형 변환) 
  • 피연산자들의 타입을 일치 시킴
  • 자료형의 표현범위가 큰쪽에 맞춰서 형변환(Casting)된 후 연산을 수행한다
  • 정수형간의 나눗셈에서 0으로 나누는 것은 금지(ArithmeticException 발생)
  10진수 값손실
byte > int 10 > 10 없음
int > byte 300 > 44 있음

기본형 타입별로 연산자 사용시 주의해야할 Case를 정리해봤다.

//Case 1
byte a = 10;
byte b = 20;
byte c = a+b; //error 발생

byte c = (byte)(a+b); //byte 형변환으로 해결

//Case 2
int x = 1000000;
int y = 2000000;
long z = a*b; //int형 a*b 끼리 계산되어 20억이 넘어감.

//아래와 같이 코딩하여야 함.
long x = 1000000;
long y = 2000000;
long z = a*b;

//Case 3
long a = 1000000 * 1000000; //int형 끼리 계산이 되므로 -727379968
long b = 1000000 * 1000000L; //리터럴 처리로 int * long -> long으로 해결됨.

//Case 4
char c1 = 'a1';
char c2 = c1+1;//error 발생.

char c2 = (char)(c1+1) // c1+1은 int형 끼리 계산이므로 char로 casting이 필요. 98출력됨.

//Case 5
int a = 86400;
int a = 60 * 60 * 12;


//Case 6 소수점 3째 자리까지만 구하는 방법
float pi = 3.141592f;
float shortPi = Math.round(pi * 1000) / 1000f; // 3.142

//**Case 7 mid 값 구하기
int start = 2_000_000_000;
int end = 2_100_000_000;

int mid = (start + end) / 2 ; //stackOverFlow errer

int mid = start + (end - start) / 2; // **첫번째 방법. start값에서 차이값의 절반만 +함.
int mid = start + end >>> 1;         
// 두번째 방법. 비트연산자 사용 우측으로 한칸 보내면 2로 나눈 효과를 냄.(음수는 불가능)

  • Case1. a+b가 int 기본형으로 casting되므로, byte로 casting시켜야 한다.
  • Case5. 리터럴 표현은 가독성, 유지보수에 좋음.

나머지 연산자(%)

  • 나누는 수 0은 불가능함. 0.0, 0.0f는 가능.
  • -10 % 8 = -2 / 10 % -8 = 2 / -10 % 8 = -2

쉬프트 연산자(<<,>>,<<<)

  • 정수형 변수에만 사용할 수 있는데, 피연산자의 각 자리(2진수로 표현했을 때)를 오른쪽 또는 왼쪽으로 이동한다고 해서 쉬프트 연산자라고 한다.
8>>0 8 8<<0 8 -8<<0 -8 -8>>>0 -8
8>>1 4 8<<1 16 -8<<1 -16 -8>>>1 2147483644
8>>2 2 8<<2 32 -8<<2 -32 -8>>>2 1073741822
  • >>>의 특징은 >>는 오른쪽으로 이동시에 1을 채우는 대신에 >>>는 0을 채운다.
  • 쉬프트 연산자를 쓰는 이유? 속도 때문. But 가독성도 중요하기 때문에 특수경우를 제외하고는 사용하지 않는다.

# 비트 연산자

  • & : And 연산자
  • | : OR 연산자
  • ^ : XOR 연산자. 서로 다른 값일 때만 1.

# 관계 연산자

  • 대소비교 < > <= >=
  • 등가비교 ==, !=
  • 기본형과 다르게 참조형은 참조하는 객체의 주소값을 비교하고 ==, !=만 사용가능하다. 예외로 String만 +가 가능하다.
  • 비교연산전에 피연산자의 타입을 맞춰서 자동형변환이 일어나지 않게 한다.
float f = 0.1f;
double d = 0.1;
double d2 = (double)f;

System.out.println(10.0 ==10.0f); //true
System.out.println(0.1 ==0.1f); //false

System.out.println(f); //0.1
System.out.println(d); //0.1
System.out.println(d2); //0.10000000149011612


System.out.println(d==f); //false
System.out.println(d==d2); //false
System.out.println(d2==f); //true

 

# 논리 연산자

  • 우선순위 : && > || 
  • 효율적 연산 : 같은 조건식이라도 피연산자의 위치에 따라서 연산속도가 달라질 수 있는것.
  • &&연산자가 false일 확률이 높은 피연산자를 연산자의 좌측에 놓아야 빠른 연산결과를 얻을 수 있다.
class Main {
    public static void main(String[] args) {
        char x = 'j';

        if( (x>'a' && x<='z') || (x>'A' && x<='Z')){ // 소문자를 넣는 확률이 커서 좌측에 두었다.
                                      //그래서 조건통과시 오른쪽 조건은 체크하지 않는다.
            System.out.println("유효한 문자입니다.");
        }else{
            System.out.println("유효하지 않은 문자입니다.");
        }
        
        int i =0;
        int j =0;
        * 퀴즈1
        if( i++ == 1 || j++ == 1 ){ // 좌측 검사해서 충족했으므로 오른쪽은 검사하지 않는다.
        	break;
        } //i = 1, j =0
        if( i++ == 1 | j++ == 1 ){ // 좌측 충족하더라도 오른쪽도 검사한다.
        	break;
        } //i = 1, j =1
        
    }
}

# instanceof

인스턴스의 실제타입을 알아보기 위해 사용. 주로 조건문에 이용

# assignment(=) operator (대입 연산자)

int a = 5;

오른쪽에 리터럴 or 변수

왼쪽변수에 5를 assign한다.

final Max = 3; //상수가 됨.

바이트코드내용으로 보면 2번의 작업이라는 것을 확인 가능.

# 화살표(->) 연산자

//메소드
int min(int x, int y) {

    return x < y ? x : y;

}

//람다 표현식
(x, y) -> x < y ? x : y;

위의 예제처럼 메소드를 람다 표현식으로 표현하면, 클래스를 작성하고 객체를 생성하지 않아도 메소드를 사용할 수 있습니다.

 

그런데 자바에서는 클래스의 선언과 동시에 객체를 생성하므로, 단 하나의 객체만을 생성할 수 있는 클래스를 익명 클래스라고 합니다.

따라서 자바에서 람다 표현식은 익명 클래스와 같다고 할 수 있습니다.

 

이러한 람다 표현식은 메소드의 매개변수로 전달될 수도 있으며, 메소드의 결괏값으로 반환될 수도 있습니다.

따라서 람다 표현식을 사용하면, 기존의 불필요한 코드를 줄여주고, 작성된 코드의 가독성을 높여줍니다.

Java SE 8부터는 이러한 람다 표현식을 사용하여 자바에서도 함수형 프로그래밍을 할 수 있게 되었습니다.

# 3항 연산자

result = (x >0) ? x : -x ;

간단하게 표현이 가능하다.

# 연산자 우선 순위

단항연산자 산술연산자 비교연산자 논리연산자 삼항연산자 대입연산자 순의 우선순위를 가지고 있다.

연산자별 우선순위

# (optional) Java 13. switch 연산자

switch 문법은 조건에 따라 분기해야 할 내용이 많아질 경우, 가독성을 포함하여 실행 속도를 향상 시키기 위해 있는 문법.

이번에 java 13에서 달라진 switch 문법이 더 바뀌었고, java 12에서 처음 추가되었다.

		//Java 12 이전
		int num = 1;
		int rtn = 0;
		switch(num){
			case 1:
				rtn = 1;
				System.out.println("1들어옴");
				break;
			case 2:
				rtn = 2;
				System.out.println("2들어옴");
				break;
		}
		System.out.println("return : [ " + rtn + " ]");

		//Java 12
		rtn = switch (num) {
			case 1 -> 1;
			case 2 -> 2;
			default -> throw new IllegalArgumentException("Unexpected value: " + num);

		};

		//Java 13
		rtn = switch (num) {
			case 1 : yield 3;
			default : throw new IllegalArgumentException("Unexpected value: " + num); 
		};

# 퀴즈 2

numbers라는 int형 배열이 있다.

해당 배열에 들어있는 숫자들은 오직 한 숫자를 제외하고는 모두 두번씩 들어있다. 오직 한 번만 등장하는 숫자를 찾는 코드를 작성하라.

더보기

XOR 연산 이용 ^

5 ^ 0 = 5

5 ^ 5 = 0

101

101

---

000

5

 

int solution(int[] numbers){

  int result = 0;

  for (int number : numbers){

   result ^= number;

  }

return result;

}