티스토리 뷰

Java

[Java] 3. 연산자

nohriter 2022. 8. 6. 14:52

자바의 연산자에 대해 알아보겠습니다.

 

 

 

 

산술 연산자


산술이란 우리가 흔히 말하는 더하기, 빼기, 곱하기, 나누기를 수행하는 연산 말한다.

자바에는 사칙연산과 나머지 연산을 포함한 총 다섯 가지의 산술 연산자가 있다.

몫을 구하는 연산자와, 나머지를 구하는 연산자가 따로 존재하는 것을 유의하자.

 

+, -, *, /, %

 

🚨 주의사항

정수형 일 때  0으로 나누거나 모듈 연산을 수행하게 되면 런타임에 오류가 발생하기 때문에 컴파일 타임에 확인할 수 없다.
또한, 자료형의 크기에 따른 오버플로우 문제도 조심해야 한다.
예를들어 중간 값을 계산하는 로직의 경우 int middleValue = (a + b) / 2 와 같이 구하고는 하는데 이는 overflow가 발생할 수 있다. 이 때 int middleValue = a + (b-a) / 2 와 같이 계산하면 overflow를 방지할 수 있다.


실수형은 연산 과정 자체에서 오차가 발생할 수 있다는 것을 인지해야 한다.
이는 특히 테스트 코드를 작성할 때, 어느 정도의 수준의 오차를 허용할 것인지 반드시 설정해주어야 하는 이유이다.
실수형은 0으로 나누거나, 모듈로 연산을 사용하면 위와 같이 Infinity, -Infinity, NaN 값을 가진다.
또한 Infinity와 NaN이 피연산자인 경우 또한 존재합니다.

 

 

비트 연산자


Java에서는 정수 타입에 대해서 비트와 비트 시프트 연산을 제공하는데 이를 비트 연산자 한다.

비트 연산이라는 의미는 정수 타입은 컴퓨터가 이해할 수 있도록 2진수 형식으로 표현되는데, 2진수 조각 하나하나를 비트라고 표현하고, 이 비트들마다 수행하는 연산을 의미한다.

 

&, |, ^

 

 

 & 는 비트 AND 연산자이며, 피연산자들의 비트 별로 AND 연산을 수행하게 된다.

 

 | 는 비트 OR 연산자이며, 피연산자들의 비트 별로 OR 연산을 수행하게 된다.

 

 ^ 는 비트 XOR 연산자이며, 피연산자들의 비트 별로 XOR 연산을 수행하는데, 둘이 다를 때만 1이 된다.

 

 ~ 는 비트를 반전시키는 단항 연산자이다. '0'은 '1'로, '1'은 '0'으로 반전시킨다. 만약 byte 타입의 리터럴 0이 있다면 이진수로는 00000000 으로 표현되는데, 이를 비트 반전시키면 11111111 이 됩니다. 따라서 이를 1의 보수를 만든다고도 표현한다.

// 부호비트도 고려해야 함.
byte a = 0b00001010; //  10  (0|0001010)
byte b = 0b00101000; //  40  (0|0101000)
byte c = -0b00001010; // -10 (1|1110110)

// ~ 연산자(1의 보수를 만들어줌)
System.out.println(~a); // 1|1110101 => -11 여기에 1을 더하면 -10이 됨(2의 보수)
System.out.println(~c); // 0|0001001 => 9   여기에 1을 더하면 10이 됨

// & 연산자
System.out.println(a & b); // 0|0001000 => 8
System.out.println(a & c); // 0|0000010 => 2
System.out.println(b & c); // 0|0100000 => 32

// | 연산자
System.out.println(a | b); // 0|0101010 => 42
System.out.println(a | c); // 1|1111110 => -2
System.out.println(b | c); // 1|1111110 => -2

// ^ 연산자(다른 경우에만 1)
System.out.println(a ^ b); // 0|0100010 => 34
System.out.println(a ^ c); // 1|1111100 => -4
System.out.println(b ^ c); // 1|1011110 => -34

 

 

관계 연산자


<, >, ≤, ≥, ==, ≠

관계 연산자는 피연산자가 같은지(==), 같지 않은지(!=), 큰지(>), 크거나 같은지(>=), 작은지(<), 작거나 같은지(<=) 비교하는 연산자이다. 주로 조건문과 반복문의 조건식에 사용되며, 연산결과는 오직  true 와  false  둘 중의 하나이다. 문자열의 동등성을 비교할 때에는 equals()메서드를 사용해야 한다.

 

int a = 7;
int b = 11;

System.out.println(a == b); // false
System.out.println(a != b); // true
System.out.println(a > b);  // false
System.out.println(a >= b); // false
System.out.println(a < b);  // true
System.out.println(a <= b); // true

 

 

논리 연산자


&&, ||, !

논리 연산자는 피연산자로  boolean  값을 받는다. 이는 평가의 결과가 될 수도 있으며, 보통 식이 전달되는 경우가 많다.

 

 &&(And,그리고)  연산자는 두 연산결과가 참인지 판단한다. 만약 둘 중 하나라도 거짓이라면 거짓을 반환하고, 만약, 왼쪽의 피연산자가 거짓이라면 오른쪽의 피연산자는 평가하지 않는다

 

 ||(Or, 또는)  연산자는 두 연산결과 중 한 쪽만 참이어도 참을 반환한다. 만약, 왼쪽의 피연산자가 참이라면 오른쪽의 피연산자는 평가하지 않는다.

 

 !  산자는 피연산자를 하나만 받는 단항 연산자이며, true 를 false로, false를 true로 반대 논리로 변경한다. 식의 평가 방향은 오른쪽에서 왼쪽이다.

 

위에서 말한 평가하지 않는 것을 short circuit evaluation이라고 하며, 식의 평가를 수행하지 않기 때문에 좀 더 빠른 결과를 얻을 수 있다.

더보기

효율적인 연산(short circuit evaluation)

논리 연산자의 또 다른 특징은 효율적인 연산을 한다는 것인데, OR연산인 || 의 경우, 두 피연산자 중 어느 한쪽만 참이여도 참이기에 좌측에 우선 평가 될 피연산자에 참이 될 가능성이 높은 피연산자를 위치하면 좌측의 피연산자가 참일 경우 우측의 피연산자 수행을 하지않고 진행하게 된다. AND연산인&& 도 마찮가지로 어느 한 쪽만 거짓이여도 결과는 거짓이 되기 때문에 false를 반환할 확률이 높은 평가식을 좌측 피연산자위치에 놓을 경우 더 빠른 연산결과를 얻을 수 있다.

 

 

instanceof


instanceof 연산자는 타입 비교 연산자이다. 이를 이용해서 객체가 클래스의 인스턴스, 하위 클래스의 인스턴스 또는 특정 인터페이스를 구현하는 클래스의 인스턴스인지 참조변수의 실제 타입을 확인할 수 있다.

 

 

 

 

class Parent {
	...
}
class Child extends Parent {
	...
}

Parent parent = new Child();
System.out.println(parent instanceof Child);	//true
System.out.println(parent instanceof Parent);	//true
System.out.println(parent instanceof Object);	//true

 

 

assignment(=) operator


대입 연산자는 변수에 값을 대입할 때 사용하는 이항 연산자이며, 피연자들의 결합 방향은 오른쪽에서 왼쪽이다.

또한, 자바에서는 대입 연산자를 다른 연산자와 결합하여 만든 다양한 복합 대입 연산자를 제공한다.

 

 

 

화살표(→) 연산자


화살표 연산자는 Java8에서 추가된 람다 표현식의 한 부분이며, (파라미터) -> { Body } 형태로 작성한다.

람다 표현식은 함수형 프로그래밍에서 가장 중요한 부분 중 하나이며, 이름 도입함으로써 자바는 함수형 언어로도 기능하게 되었다.

이는 일부 익명 클래스를 간단히 표현할 수 있는 방법이라고도 볼 수 있으며, 또한 @FunctionalInterface 어노테이션을 붙인 인터페이스의 구현체로도 사용할 수 있다. 람다식은 메서드가 매개변수로 전달되는 것이 가능하고, 결과로 반환되는 것도 가능한다. 이를 함수가 일급시민이다라고 표현한다.

 

// 메소드
int min(int x, int y) {
    retrun x < y ? x : y;
}
// 람다 표현식
(x, y) -> x < y ? x : y;

 

💡 3항 연산자

?: 는 논리 연산자의 일종으로 피연산자를 세 개를 받는 유일한 연산자여서 삼항 연산자로 많이 불린다.
이는 if - else 문과 비슷하게 보이며, 한 줄로 간단하게 표현할 수 있다.
조건 ? 참일 때  : 거짓일  때 의 표현으로 사용한다.

 

 

연산자 우선 순위


 

 

switch 연산자


Java의 새로운 Switch 표현 방법은 Java 12에서 처음 소개 되었고, Java 13, 14 버전을 거치면서 사소한 변경만 몇 차례 있었다.

switch expression은 자바 14 버전부터 사용할 수있다.

 

[ 변경 전 ]

  • C, C++에서 사용하는 형태의 Switch 형식을 따른다.
  • 불필요한 반복코드 존재한다.
  • 다수의 case와 break가 존재한다.
  • 개발자의 실수로 break를 빼먹을 경우 다음 분기로 넘어가게 됨.

[ 변경 후 ]

  • Switch 내에서 라벨이 일치하는 경우, case -> A와 같은 형식으로 표현이 가능하다.
  • 단일 수행 또는 블록 수행이 가능하다.
  • Switch 블록 내에서 계산된 값을 반환하는 yeild라는 키워드가 생겼다.
  • 여러 조건을 쉼표로 구분하여 한 라인으로 처리할 수 있음.

 

 

[ 기존의 Switch 표현 ]

enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY; }
enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY; }

static void  test(Day day){
        switch (day) {
            case MONDAY:
            case FRIDAY:
            case SUNDAY:
                System.out.println(6);
                break;
            case TUESDAY:
                System.out.println(7);
                break;
            case THURSDAY:
            case SATURDAY:
                System.out.println(8);
                break;
            case WEDNESDAY:
                System.out.println(9);
                break;
    }
}

MONDAY, FRIDAY SUNDAY는 동일한 처리를 하기 위해 연속적으로 위치해야하고 미관상으로도 좋지 않다. 또 전체적으로 case, break, System.out.prinln()이 반복된다는 것을 알 수 있다.

 

[ 개선 된 switch ]

static void  test(Day day){
        switch (day) {
             case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
             case TUESDAY                -> System.out.println(7);
             case THURSDAY, SATURDAY     -> System.out.println(8);
             case WEDNESDAY              -> System.out.println(9);
    }
}

조건에 해당하는 라벨을 ,로 구분하여 한 줄에 쓸 수 있고, -> 화살표를 통해 처리할 코드를 작성할 수 있다. 새로운 Switch 표현을 사용해 이전 보다는 간결해 졌지만 여전히 System.out.println()이 반복되는 것을 알 수 있다. 이런 경우에는 다음과 같은 표현도 가능하다.

 

public void test(Day day) {
        System.out.println(
                switch (day) {
                    case MONDAY, FRIDAY, SUNDAY -> 6;
                    case TUESDAY                -> 7;
                    case THURSDAY, SATURDAY     -> 8;
                    case WEDNESDAY              -> 9;
        	});
	}

 

System.out.println() 인자에 Switch를 사용할 수 있다. 기존 Switch는 반환값이 없었는데, 새로운 Switch는 반환값을 갖는다. 따라서 다음과 같은 표현이 가능하다.

public void test(Day day) {
        int cnt = switch(day) {
            case MONDAY, FRIDAY, SUNDAY -> 6;
            case TUESDAY                -> 7;
            case THURSDAY, SATURDAY     -> 8;
            case WEDNESDAY              -> 9;
        };
    }

 

[ Switch의 반환 yield ]

-> 표현 오른쪽에 오는 처리는 꼭 단일 수행이 아니어도 된다. 이때는 {} 블록을 만들고 그 안에 수행할 코드를 작성하면된다. 또 Switch 표현에서 사용되는 yield라는 키워드가 생겼다. 새로운 Switch 표현이 처음 나온 Java 12에서는 break value;와 같은 문법으로 해당 기능을 지원했으나 현재 Java 14에서는 yield로 변경되었다. yield는 쉽게 말게 함수의 return과 비슷하다고 할 수 있다. yield는 해당 Switch 블록에 특정 값을 Switch의 결과값으로 반환하는 것이다.

public void test(Day day) {
        int cnt = switch (day) {
            case MONDAY -> 0;
            case TUESDAY -> 1; 
            case WEDNESDAY -> {
                int k = day.toString().length();
                int result = k+5;
		yield result;
                //break result; <----- java 12 Switch Expression
            }
            default -> 0;
        };
}

 

yield 키워드는 항상 Switch 블록 내부에서만 사용된다.

public void test(Day day) {
        int cnt = switch (day) {
            case MONDAY -> 0;
            case TUESDAY -> yield 1; // error! yield는 block 안에서만 유효다
            case WEDNESDAY -> { yield 2;} // ok
            default -> 0;
        }
}

 

💡 default

Switch의 반환값이 따로 필요하지 않은 경우나, case가 Switch로 들어오는 모든 인자를 커버하는 경우에는 default 항목을 따로 넣어주지 않아도 된다. 하지만 그렇지 않은 경우에는 default -> { your code; }를 꼭 작성해야 한다. 위 두 가지 경우가 아니면 에러가 발생한다.
💡 yield 키워드는 변수명으로 사용이 가능하다.

Java에서 예약어는 변수명으로 사용할 수 없다. 하지만 yield는 신기하게도 변수명으로 사용할 수 있다.

 

 

 

 

 

참고

 

백기선님 온라인 스터디 3주차 - 연산자

3주차!

velog.io

 

[JAVA] 연산자 (화살표, 삼항, switch...)

산술 연산자 +, -, *, /, % 비트 연산자 &, |, ^ 관계 연산자 <, >, ≤, ≥, ==, ≠ 논리 연산자 &&, || instanceof instanceof 란 참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용하는 연..

doozi0316.tistory.com

 

JAVA 스터디 5 - switch expression (자바 12부터 추가됨)

자바 12부터 새로운 switch문이 추가 되었다 . 기존 switch 제어문의 불편함 1. 변수은 값이 다르지만 공통된 명령을 수행해야 할때 case를 추가해서 똑같은 코드를 또 적어야한다 . 2. case 내에 break;를

ugo04.tistory.com

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/07   »
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
글 보관함