ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 3주차 과제: 연산자
    Programming/Java live study 2020. 12. 24. 02:10
     

    3주차 과제: 연산자 · Issue #3 · whiteship/live-study

    목표 자바가 제공하는 다양한 연산자를 학습하세요. 학습할 것 산술 연산자 비트 연산자 관계 연산자 논리 연산자 instanceof assignment(=) operator 화살표(->) 연산자 3항 연산자 연산자 우선 순위 (option

    github.com

    목표

    자바가 제공하는 다양한 연산자를 학습하세요.

    학습할 것

     - 산술 연산자

     - 비트 연산자

     - 관계 연산자

     - 논리 연산자

     - instanceof

     - assignment(=) operator

     - 화살표(->) 연산자

     - 3항 연산자

     - 연산자 우선 순위

     - (optional) Java 13. switch 연산자


    1. 산술 연산자

    참고 : blog.baesangwoo.dev/posts/java-livestudy-3week/

    1.1 이항 연산자

     산술 연산자는 덧셈, 뺄셈 등 산술 연산을 수행하는 연산자들이다. 산술 연산자는 필요한 피연산자가 두 개 이기 때문에 이항 연산자라고 부른다.

    기호 기능
    + 덧셈 10 + 10, a + b, 1 + a
    - 뺄셈 10 - 10, a - b, 1 - a
    * 곱셈 10 * 10, a * b, 10 * a
    / 나눗셈 10 / 10, a / b, 10 / a
    % 나머지 10 % 10, a % b, 10 % a

     아래는 연산자를 사용한 간단한 코드이다.

     

    int a = 20;
    int b = 30;
    
    System.out.println(a + b); // 50
    System.out.println(a - b); // -10
    System.out.println(a * b); // 600
    System.out.println(a / b); // 0
    System.out.println(a % b); // 20

     

     + 연산자의 경우 문자열을 연결하는데에도 사용할 수 있다.

     

    String word1 = "hello";
    String word2 = "world";
    
    System.out.println(word1 + " " + word2); // hello world

     

     / 연산자의 경우 피연산자들이 정수인지 실수인지에 따라 결과가 나뉘게 된다. 정수 끼리 나누게 되면 결과도 정수이고, 피연산자에 하나라도 실수가 있다면 결과도 소수점으로 나온다.

     

    System.out.println(6 / 2); // 3
    System.out.println(5 / 3.0); // 1.6666666666666667

     

     또한 0으로 나누는 행위는 런타임에 ArithmeticException을 던진다.

     

    System.out.println(5 / 0);

     

    0 으로 나누면 ArithmeticException을 던진다.

     % 연산자의 경우 반환된 결과는 항상 첫 번째 피연산자의 부호와 동일하다.

     

    System.out.println(10 % 3); // 1
    System.out.println(-10 % 3); // -1
    System.out.println(10 % -3); // 1
    System.out.println(-10 % -3); // -1

    1.2 단항 연산자

      Java에서 - 의 경우에는 부호 연산자로도 사용된다. 부호 연산자는 필요한 피연산자가 하나 이기 때문에 단항 연산자이다.  

     

    System.out.println(-1); // -1
    System.out.println(-10 * -5); // 50

     

     또 다른 단항 연산자로는 ++, -- 가 있다. ++, --의 경우 상수에 직접적으로 값을 지정할 수 없고, 변수에만 사용이 가능하다. 

     

    기호 기능 사용 설명
    ++ 전위 증가 ++op 값을 1 증가 시킨 후 값을 반환
    ++ 후위 증가 op++ 값을 1 증가 시키기 전에 반환하고 증가
    -- 전위 감소 --op 값을 1 감소 시킨 후 값을 반환
    -- 후위 감소 op-- 값을 1 감소 시키기 전에 번환하고 감소

     

    int a = 20;
    
    // System.out.println(--20); 컴파일 에러
    System.out.println(--a); // 즉시 값을 내린 후 출력 19 
    System.out.println(++a); // 즉시 값을 더한 후 출력 20
    System.out.println(a--); // 20 출력 후 값을 내림 --
    System.out.println(a++); // 19 출력 후 값을 올림 ++
    System.out.println(a); // 20

     

     마지막으로 ! 연산자가 있다. !은 논리 연산자로 해당 값의 반대되는 값을 반환한다.

     

    System.out.print(!false); // true를 return

    2. 비트 연산자

    참고 :

    blog.baesangwoo.dev/posts/java-livestudy-3week/

    coding-factory.tistory.com/521

     

      비트 연산자는 데이터를 비트 단위로 연산한다. 0과 1로 표현이 가능한 정수 타입만 비트 연산이 가능하다. 비트 연산자의 종류에는 비트 이동 연산자와 비트 논리 연산자가 있다. 단항 연산자인 ~ 보수 연산만을 제외하고 피연산자가 두개 필요하기 때문에 이항 연산자이다. 비트 연산자와 비트 이동 연산자는 개별 비트를 조작하는 저수준의 연산자이다.

    2.1 비트 이동 연산자

    연산자 설명
    a << b 정수 a의 각 비트를 b만큼 왼쪽으로 이동한다. 새로 생긴 빈자리는 0으로 채운다.
    a >> b 정수 a의 각 비트를 b만큼 오른쪽으로 이동한다. 새로 생긴 빈자리는 a의 최상위 비트 값으로 채운다.
    a >>> b 정수 a의 각 비트를 b만큼 오른쪽으로 이동한다. 새로 생긴 빈자리를 0으로 채운다.

     

    10 << 3
    
    00000000 00000000 00000000 00001010         10
    00000000 00000000 00000000 01010000         80
    
    System.out.println(10 << 3); // 80

     

    10 >> 3
    
    00000000 00000000 00000000 00001010         10
    00000000 00000000 00000000 00000001 -> 010   1
    
    System.out.println(10 >> 3); // 1

     

    -10 >> 3
    
    11111111 11111111 11111111 11110101        -10
    11111111 11111111 11111111 11111110 -> 101  -2
    
    System.out.println(-10 >> 3); // -2

     

    -10 >>> 3
    
    11111111 11111111 11111111 11110101        -10
    00011111 11111111 11111111 11111110 -> 101 536870910
    
    System.out.println(-10 >>> 3); // 536870910

    2.2 비트 논리 연산자

    연산자 논리 설명
    & AND 두 비트 모두 1인 경우 연산결과는 1이다
    | OR 두 비트 중에 하나라도 1인 경우 연산결과는 1이다
    ^ XOR 두 비트 중 하나는 1이고 나머지 하나가 0인 경우 연산결과는 1이다
    ~ NOT 1은 0, 0은 1 비트가 반전된다.

     

    AND 
    0b00001111 & 0b00001010 => 0b00001010 

     

    OR
    0b00001111 & 0b11111010 => 0b11111111 

     

    XOR
    0b01010101 ^ 0b10101010 => 0b11111111

     

    NOT
    ~0b00001111 => 0b11110000

    3. 관계 연산자

    참고 :

    blog.baesangwoo.dev/posts/java-livestudy-3week/

    coding-factory.tistory.com/536 

     

     두 항의 관계를 true 혹은 false와 같은 boolean 타입으로 반환한다. 기본형 타입인 피연산자에서 연산이 가능하다. 두 개의 피연산자를 사용하기 때문에 이항 연산자이다.

    연산자 설명
    > 왼쪽이 크면 true, 아니면 false를 반환한다
    < 오른쪽이 크면 true, 아니면 false를 반환한다
    >= 왼쪽이 크거나 같으면 true, 아니면 false를 반환한다
    <= 오른쪽이 크거나 같으면 true, 아니면 false를 반환한다
    == 두 개의 항이 같으면 true, 다르면 false를 반환한다
    != 두 개의 항이 다르면 true, 같으면 false를 반환한다

     

    System.out.println(10 > 20); // false
    System.out.println(10 < 20); // true
    System.out.println(10 >= 10); // true
    System.out.println(10 <= 10); // true
    System.out.println(10 == 10); // true
    System.out.println(10 != 10); // false

     

     또한 == 과 != 의 경우 두 피연산자의 타입이 다르면 크기가 작은 피연산자 타입이 크기가 큰 피연산자 타입으로 변환되어 비교된다.

     

    int i = 10;
    double d = 10.0;
    
    System.out.println(i == d); // true
    System.out.println(i != d); // false

     

     참조형 변수의 경우 해당 값을 비교하는 것이 아닌, 참조하는 주소의 값을 비교한다.

     

    int[] arr1 = {1, 2, 3};
    int[] arr2 = {1, 2, 3};
    
    System.out.println(arr1 == arr2); // false

     

     String 클래스의 경우 변수를 생성하는 방식이 두가지가 있다. 

      - 리터럴을 이용한 방식

      - new 연산자를 이용한 방식

     

     String word = "Hello"; 처럼 리터럴로 생성할 경우 String constance pool 영역에 생성되고, String word = new String("Hello");의 경우 Heap 영역에 생성된다. 

     

     String을 리터럴로 생성한 경우, 내부적으로 String의 intern() 메소드가 호출하게 된다. intern() 메소드는 주어진 문자열이 String constant pool에 존재하는지 검색하고 있으면 해당 주소값을 반환하고 없으면 String contant pool에 넣고 새로운 주소값을 반환한다.

     

    String word1 = "hello";
    String word2 = new String("hello");
    
    System.out.println(word1 == word2); // false

     

     그렇기 때문에 문자열 자체를 비교하기 위해서는 equals 메소드를 사용해야 한다.

     

    String word1 = "hello";
    String word2 = new String("hello");
    
    System.out.println(word1.equals(word2)); // true

    4. 논리 연산자

    참고 :

    blog.baesangwoo.dev/posts/java-livestudy-3week/

    velog.io/@foeverna/Java-%EC%97%B0%EC%82%B0%EC%9E%90-%EA%B4%80%EA%B3%84-%EB%85%BC%EB%A6%AC-%EC%A1%B0%EA%B1%B4-%EB%B9%84%ED%8A%B8-%EC%97%B0%EC%82%B0%EC%9E%90

     

      두 항을 논리 연산하여 true 혹은 false와 같은 boolean 타입으로 반환한다. 관계 연산자와 함께 사용 하는 경우가 많다. 단항 연산자인 ! 연산자를 제외하고 모두 이항 연산자이다.

    연산자 설명
    && 두 항이 모두 true이면 true를 반환한다
    || 두 항중 하나만 true여도 true를 반환한다
    ! true이면 false, false이면 true를 반환한다.

     

    더 알아보기 short circuit evaluation

      short circuit evaluation은 논리 연산을 사용할 때 고려해야 할 사항이다. 두개의 항 중에 앞의 항에서 반환값이 정해지는 경우 뒤의 항이 true인지 false인지 연산하지 않는 것을 뜻한다.

     

    int a = 10;
    int b = 15;
    
    System.out.println((++a > 0) || (++b > 0)); // true?
    System.out.println("a : " + a); // 11?
    System.out.println("b : " + b); // 16?

     

      출력값으로 밑의 값을 예상할 가능성이 크다.

     

    true
    a : 11
    b : 16

     

      하지만 예상한 값이 아닌 밑의 값이 나오게 된다.

     

    true
    a : 11
    b : 15

     

      위의 코드를 보면 a > 0 관계 연산을 진행하면 true 를 반환한다. 논리합 OR 이기 때문에 무조건 true 를 반환하게 된다. ++b > 0 연산은 진행되지 않았고 ++b 는 실행되지 않는다. 이것이 short circuit evaluation 이다.


    5. instanceof

    참고 : improver.tistory.com/140

     

      객체의 타입을 확인하는데 사용한다. reference 변수가 instanceof로 형변환이 가능한 타입인지 확인한다. 형변환이 가능하면 true를 반환하고 그렇지 않다면 false를 반환한다. 해당 객체가 해당 클래스의 인스턴스인지도 확인 가능하다. 자식 타입 객체의 경우 부모 클래스로 형변환이 가능하므로 true를 반환한다. 하지만 부모 타입 객체가 자식 타입으로 형변환은 자연스럽지 못하기 때문에 false를 반환한다. 두 개의 피연산자를 필요로 하기 때문에 이항 연산자 이다.

     

    Animal animal = new animal(); // 부모 타입
    Lion lion = new Lion(); // 자식 타입
    
    // 객체 instanceof 클래스
    
    System.out.println(animal instanceof Animal); // true
    System.out.println(lion instanceof Animal); // true
    System.out.println(animal instanceof Lion); // false
    System.out.println(lion instanceof Lion); // true
    

    6. assignment(=) operator

    참고 : tcpschool.com/java/java_operator_assignment

     

      변수에 값을 대입할 때 사용한다. 다른 연산자와 함께 사용하여 복합 대입 연산자로 사용할 수 있다. 두 개의 피연산자가 필요하기 때문에 이항 연산자이다.

    연산자 설명
    = 왼쪽의 피연산자에 오른쪽의 피연산자를 대입한다
    += 왼쪽의 피연산자에 오른쪽의 피연산자를 더한 후, 그 결괏값을 왼쪽의 피연산자에 대입한다
    -= 왼쪽의 피연산자에서 오른쪽의 피연산자를 뺀 후, 그 결괏값을 왼쪽의 피연산자에 대입한다
    *= 왼쪽의 피연산자에 오른쪽의 피연산자를 곱한 후, 그 결괏값을 왼쪽의 피연산자에 대입한다
    /= 왼쪽의 피연산자를 오른쪽의 피연산자로 나눈 후, 그 결괏값을 왼쪽의 피연산자에 대입한다
    %= 왼쪽의 피연산자를 오른쪽의 피연산자로 나눈 후, 그 나머지를 왼쪽의 피연산자에 대입한다
    &= 왼쪽의 피연산자를 오른쪽의 피연산자와 비트 AND 연산한 후, 그 결괏값을 왼쪽의 피연산자에 대입한다
    |= 왼쪽의 피연산자를 오른쪽의 피연산자와 비트 OR 연산한 후, 그 결괏값을 왼쪽의 피연산자에 대입한다
    ^= 왼쪽의 피연산자를 오른쪽의 피연산자와 비트 XOR 연산한 후, 그 결괏값을 왼쪽의 피연산자에 대입한다
    <<= 왼쪽의 피연산자를 오른쪽의 피연산자만큼 왼쪽 시프트한 후, 그 결괏값을 왼쪽의 피연산자에 대입한다
    >>= 왼쪽의 피연산자를 오른쪽의 피연산자만큼 부호를 유지하며 오른쪽 시프트한 후, 그 결괏값을 왼쪽의 피연산자에 대입한다
    >>>= 왼쪽의 피연산자를 오른쪽의 피연산자만큼 부호에 상관없이 오른쪽 시프트한 후, 그 결괏값을 왼쪽의 피연산자에 대입한다

     

    int num = 10;
    
    num += 1; // num = num + 1
    num -= 1; // num = num - 1
    num *= 2; // num = num * 2
    num /= 2; // num = num / 2
    num %= 2; // num = num % 2
    
    num &= 2; // num = num & 2
    num |= 2; // num = num | 2
    num ^= 3; // num = num ^ 3
    
    num <<= 3; // num = num << 3
    num >>= 3; // num = num >> 3
    num >>>= 3; // num = num >>> 3

    7. 화살표(->) 연산자

    참고 : skyoo2003.github.io/post/2016/11/09/java8-lambda-expression

     

      람다 표현식이라고 부른다. 기존의 Java에 함수형 프로그래밍을 위하여 Java 8 부터 추가 되었다. 함수형 프로그래밍은 함수의 입력에 의존하여 출력을 만드는 구조로 외부에서 상태를 변경하는 것을 지양하는 패러다임으로 부작용 발생을 최소화 하는 방법론이다.

     

      Java에는 함수의 개념이 없다. 그렇기 때문에 기존의 Java 언어 체계에서는 함수형 언어를 언어 차원에서 지원하지 못한다.  Java 8 에서 함수형 인터페이스(단 하나의 메소드만 선언된 인터페이스)라는 개념을 도입하였다. 함수형 인터페이스의 형우, 람다식으로 표현이 가능할 수 있도록 제공했다.

     

     기본적인 람다식 구조이다.

     

    (매개변수) -> {함수 로직}

     

    Runnable runnable = new Runnable() {
    
        @Override
        public void run() {
            System.out.println("run method 실행");      
        }
    };

     

     쓰레드를 만들기 위한 runnable 인터페이스를 활용하여 Annoanonymous inner class를 작성하였다. 밑의 코드는 람다식으로 변경한 것이다.

     

     Runnable runnable = () -> System.out.println("run method 실행");

     

    ide에서 쉽게 변경할 수 있다.

     

    람다식은

     - 단순한 구문일 경우 중괄호가 생략 가능하다.

     - return이 없을 수도 있다.

     - 매개변수에는 타입을 명시하지 않아도 된다. (타입 추론이 가능하다.)

     - 람다식 문법은 컴파일러가 익명 클래스로 변환한다.


    8. 3항 연산자

      3항 연산자는 말그대로 피연산자가 3개인 연산자 이다. 단순히 코드를 간결하게 만들어 준다.

     

    int result = 0;
    if (a > b) {
        result = a;
    } else {
        result = b;
    }

     

    int result = 0;
    result = (a > b) ? a : b;

    9. 연산자 우선 순위

    참고 : toma0912.tistory.com/66

     

      연산자에는 우선순위가 존재한다. 기본적인 연산 진행방향은 왼쪽에서 오른쪽으로 수행되며 단향 연산자와 대입 연산자의 경우 오른쪽에서 왼쪽으로 수행된다. 추가적인 연산 우선순위는 밑에 표를 참고한다.

    우선순위 연산자 설명
    1 (), [] 괄호, 대괄호
    2 !, ~, ++, -- 부정, 증감 연산자
    3 *, /, % 곱셈, 나눗셈 연산자
    4 +, - 덧셈, 뺄셈 연산자
    5 <<, >>, >>> 비트단위의 쉬프트 연산자
    6 <. <=, >, >= 관계 연산자
    7 ==, !=  
    8 & 비트 연산자
    9 ^  
    10 |  
    11 && 논리 연산자
    12 || 논리 연산자
    13 ? : 조건 연산자
    14 =, +=, -=, *=, /=, %=, <<=, >>=, &=, ^=, ~= 대입 연산자

     


    10. (optional) Java 13. switch 연산자

    참고 : openjdk.java.net/jeps/354

     

      switch문은 break를 사용하지 않으면 다음 case로 이어지기 때문에 각 case마다 break를 명시해야 했다. switch 연산자를 사용하면 -> 연산자를 사용하여 break 없이 한 개의 표현식은 한 개의 case로 인식하게 된다. 추가적으로 콤마를 사용하여 case에 여러 값을 입력할 수 있다.

     

    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);
        }
    );   

     

      switch 연산자를 사용해서 값으로 반환할 수 있다.

     

    Day day = Day.WEDNESDAY;    
    
    int number = switch (day) {
                    case MONDAY, FRIDAY, SUNDAY -> 6;
                    case TUESDAY                -> 7;
                    case THURSDAY, SATURDAY     -> 8;
                    case WEDNESDAY              -> 9;
                    default -> throw new IllegalStateException("Invalid day: " + day);
                };  

     

     break 대신 yield를 사용한다. switch case의 값을 반환한다.

     

    Day day = Day.WEDNESDAY;    
    
    int number = switch (day) {
                    case MONDAY, FRIDAY, SUNDAY -> 6;
                    case TUESDAY                -> 7;
                    case THURSDAY, SATURDAY     -> 8;
                    case WEDNESDAY              -> 9;
                    default -> yield 0;
                };  

     

      switch의 경우 철저해야 한다. 가능한 모든 값에 대해 일치하는 switch가 있어야 한다. 이것은 defualt절이 필요하다는 것을 의미한다. 없을 경우 컴파일 에러가 발생한다. 그러나 enum의 경우 모든 case를 입력하면 컴파일러가 default를 자동적으로 삽입하므로 생략 가능하다.


    References.

    blog.baesangwoo.dev/posts/java-livestudy-3week/

    coding-factory.tistory.com/521

    velog.io/@foeverna/Java-%EC%97%B0%EC%82%B0%EC%9E%90-%EA%B4%80%EA%B3%84-%EB%85%BC%EB%A6%AC-%EC%A1%B0%EA%B1%B4-%EB%B9%84%ED%8A%B8-%EC%97%B0%EC%82%B0%EC%9E%90

    improver.tistory.com/140

    tcpschool.com/java/java_operator_assignment

    skyoo2003.github.io/post/2016/11/09/java8-lambda-expression

    toma0912.tistory.com/66

    openjdk.java.net/jeps/354

    댓글

Designed by Tistory.