3주 차 시작합니다!

연산이란?

주어진 정보를 통해 일정한 규칙에 따라 어떤 값이나 결과를 구하는 과정을 의미한다.

 

연산자란?

연산을 진행하는 동안 사용되는 기호를 말한다.

연산 대상의 수나 연산 방식에 따라서 명칭이 나뉘게 된다.

 

피연산자란?

연산될 대상을 말하며. 변수, 상수, 리터럴 등을 의미한다.

 

단항 연산자 (Unary Operator)

연산이 수행될 피연산자가 1개인 경우 해당 연산자를 단항 연산자라고 한다.

  • 전위 증감, 후위 증감 연산자
  • 단항으로써 사용하는 +, - 연산자 (부호 연산자)
  • 비트 반전 ~ 연산자

이항 연산자 (Binary Operator)

연산이 수행될 피연산자가 2개인 경우 해당 연산자를 이항 연산자라고 한다.

  • 산술 연산자
  • 비트 연산자
  • 관계 연산자
  • 대입 연산자
  • 논리 연산자

삼항 연산자 (Ternary Operator)

연산이 수행될 피연산자가 3개인 경우 해당 연산자를 삼항 연산자라고 한다.

  • 조건 연산자 ( 조건식? True의 경우 반환될 식 : False의 경우 일시 반환될 식 )

 

산술 연산자 (Arithmetic Operator)

산술 연산자는 수치 계산을 실시하는 연산자를 말한다.

  • A + B : 덧셈 연산자 (정수형, 실수형 리터럴뿐만 아니라 문자열 연산에도 사용 가능하다.)

      // 기본적인 사용법
          int numA = 10;
          int numB = 10;
    
          System.out.println(numA + numB); // 20
    
          int i = numA + numB;
        System.out.println(i);
    • 문자열 더하기 연산

      String strA = "hello";
      strA += " World";
      System.out.println(strA); // hello world
    • 값의 범위가 다른 Type 간의 연산 (공통)

      // long과 int를 더하는 경우에는?
      long longA = 10l;
      int numC = 10;
      
      System.out.println(longA+numC);
      
      // 실제로는 int 가 long으로 Type Conversion이 된 것을 알 수 있다. - 2주차 내용
      System.out.println(longA + (long)numC);
      
      // long과 double를 더하는 경우에는?
      long longA = 10;
      double doubleA = 10.0;
      
      System.out.println(longA+doubleA);
      
      // 실제로는 long 이 double로 Numeric Promotion이 된 것을 알 수 있다.
      System.out.println((double)longA + doubleA);
      
  • A - B : 뺄셈 연산자

      int numA = 10;
      int numB = 10;
    
      System.out.println(numA - numB); 
    
      int j = numA - numB; 
      System.out.println(j); // 0 출력
  • A / B : 나눗셈 연산자

    나눗셈 연산과 나머지 연산의 경우 피연산자로 0을 사용하면 Exception이 발생한다.

      int numA = 10;
      int numB = 10;
    
      System.out.println(numA / numB); 
    
      int k = numA / numB;
      System.out.println("k = " + k); // 1 출력 
  • A % B : 나머지 연산자

      int numA = 11;
      int numB = 2;
    
      System.out.println(numA % numB); 
    
      int l = numA % numB;
      System.out.println("l = " + l); // 1 출력
  • A * B : 곱하기 연산자

      int numA = 11;
      int numB = 2;
    
      System.out.println(numA * numB); 
    
      int h = numA % numB;
      System.out.println("h = " + h); // 22 출력
  • ++N, --N : 전위 증감 연산자

      int numC = 10;
      numC = --numC;
      System.out.println("numC = " + numC);
    
      int numD = 10;
      numD = ++numD;
      System.out.println("numD = " + numD);
    
      // Decompile
      // 전위 증감 연산자는 내부적으로 A = A +- 1; 의 형식을 취하는 것을 알 수 있다.
    
      int numC = 10;
      int numC = numC - 1;
      System.out.println("numC = " + numC);
    
      int numD = 10;
      int numD = numD + 1;
      System.out.println("numD = " + numD);
    
  • N++, N-- : 후위 증감 연산자

      int numE = 10;
      System.out.println(numE--);
      System.out.println("numE = " + numE);
    
      int numF = 10;
      System.out.println(numF--);
      System.out.println("numF = " + numF);
    
      // Decompile
      // 우선 임시변수를 만들어서 해당 값을 복사한뒤 전달하고 반영된 값을 그 이후에 제공한다.
    
      int numE = 10;
      byte var10001 = numE; // 임시변수에 값 복사
      int numE = numE - 1; // 변수 연산
      System.out.println(var10001); // 임시변수 출력
      System.out.println("numE = " + numE); // 연산된 변수를 제공
    
      int numF = 10;
      var10001 = numF; 
      int numF = numF - 1;
      System.out.println(var10001);
      System.out.println("numF = " + numF);
    • Wrapper와 Primitive 간의 산술 연산

    • Wrapper Class* (Integer, Long, Double)와 Primitive Type 연산 시에는 Wrapper Class 피연산자가 Unboxing 되어 연산이 진행되고 다시 Boxing이 되는 등에 성능상 오버헤드가 발생한다.

      int numB = 10;       
      
      Integer numA = numB; //autoboxing
      
      numA = numA + numB; //unboxing 후 연산 -> 결과 반영 전 다시 autoboxing 진행

      그리고 Operand Stack에서 관리되는 Primitive TypeHeap에서 참조되는 Wrapper Type은 값에 접근하는 시간에 차이가 존재한다. (일반적으로 Wrapper의 Access 시간이 길다.)

    • Primitive 도 일정 값 이상의 경우, Constant Pool이라는 Stack 외부에 존재하는 공간에 저장된다.*

    • JVM 설정 중에 Primitive 값의 크기에 대한 설정이 있다고 한다.

    • 즉 프로그램의 환경적인 부분에 따라서 수치나 연산 시간 등이 달라질 수 있다.

 

비트 연산자?

  • A & B : AND 연산 ( A와 B의 비트에 대해서 AND 연산을 수행한다.)

      //양쪽의 비트가 1이면 1, 0이면 0, 0 과 1 인 경우에는 0을 나타낸다.
    
      int num = 15; // 1111
      int nums = 6; // 0110
                    // 0110 : AND 연산
    
      int i = num & nums;
      System.out.println("i = " + i); // 6
  • A | B : OR 연산 ( A와 B의 비트에 대해서 OR 연산을 수행한다.)

      //하나의 비트가 1이면 1, 양쪽의 비트가 0이면 0이다.
    
      int num = 15; // 1111
      int nums = 6; // 0110
                    // 1111 : OR 연산
    
      int i = num | nums;
      System.out.println("i = " + i); // 15
  • A ^ B : XOR 연산 ( A와 B의 비트에 대해서 XOR 연산을 수행한다.)

      //하나의 비트가 1이면 1, 양쪽의 비트가 0이면 0, 양쪽의 비트가 1이면 0이다.
    
      int num = 15; // 1111
      int nums = 6; // 0110
                    // 1001 : XOR 연산
    
      int i = num ^ nums;
      System.out.println("i = " + i); // 9
  • A << N : 시프트 연산자 ( A의 비트를 좌측으로 N만큼 이동한다.)

      //비트를 좌측으로 N만큼 이동하고 우측에서 N만큼의 영역을 0으로 채우게된다.
    
      int num = 15; // 1111
      int nums = 2;
    
      int i = num << nums; // 111100 = 좌측으로 비트를 2칸씩 이동.
      System.out.println("i = " + i);
  • A >> N : 시프트 연산자 ( A의 비트를 우측으로 N만큼 이동한다.)

      //비트를 우측으로 N만큼 이동하고 좌측에서 N만큼의 영역을 0으로 채우게된다.
    
      int num = 15; // 1111
      int nums = 2;
    
      int i = num >> nums; // 0011 = 우측으로 비트를 2칸씩 이동.
      System.out.println("i = " + i);
  • A >>> N : 부호가 없는 시프트 연산자 ( A의 2진수를 우측으로 비트를 N만큼 이동한다.)

      int num = -15; // 11111111111111111111111111110001
      int nums = 2;
    
      // 부호를 신경쓰지 않고 모든 비트 값들을 오른쪽으로 이동시킨 후에 왼쪽의 빈 공간은 모두 
      // 0 으로 채운다.
    
      int i = num >>> nums; 
      System.out.println("i = " + i); // 1073741820 = 00111111111111111111111111111100
    
      int j = -15 >> nums;         
      System.out.println("j = " + j); // -4 : 11111111111111111111111111111100 
  • ~A : 반전 연산자 ( A 내부의 비트 값을 반전시킨다.)

      int num = -15; // 11111111111111111111111111110001
    
      System.out.println(~num);   // 14 : 00000000000000000000000000001110

 

관계 연산자?

  • A == B : 동일성 비교 ( A와 B가 같다면?)

      int numA = 10;
      int numB = 10;
    
      boolean isSame = numA == numB; // boolean 값을 반환.
    
      if (isSame){
          System.out.println(isSame); // true
      }
    
      혹은
    
      if (numA == numB){
          System.out.println(isSame); // true
      }
  • A != B : 동일하지 않은 경우 ( A와 B가 같지 않다면?)

      int numA = 10;
      int numB = 9;
    
      boolean isSame = numA != numB;
    
      if (isSame){
          System.out.println(isSame); //true
      }
    
      또는
    
      if (numA != numB){
          System.out.println(isSame); //true
      }
    
      또는
    
      boolean isSame = numA == numB;
    
      if (!isSame){
          System.out.println(isSame); //false 
      }
  • A > B : 대소 관계 비교 ( A가 B보다 크다면?)

      int numA = 10;
      int numB = 9;
    
      boolean isSame = numA > numB;
    
      if (isSame){
         System.out.println(isSame); //true
      }
    
      if (numA > numB){
         System.out.println(isSame); //true
      }
  • A >= B : 대소관계 비교 ( A가 B보다 크거나 같다면?)

      int numA = 10;
      int numB = 10;
    
      boolean isSame = numA >= numB;
    
      if (isSame){
         System.out.println(isSame); //true
      }
    
      if (numA >= numB){
         System.out.println(isSame); //true
      }
  • A < B : 대소관계 비교 ( A가 B보다 작다면?)

      int numA = 9;
      int numB = 10;
    
      boolean isSame = numA < numB;
    
      if (isSame){ 
         System.out.println(isSame); //true
      }
    
      if (numA < numB){
         System.out.println(isSame); //true
      }
  • A <= B : 대소관계 비교 ( A가 B보다 작거나 같다면?)

      int numA = 10;
      int numB = 10;
    
      boolean isSame = numA <= numB;
    
      if (isSame){
         System.out.println(isSame); //true
      }
    
      if (numA <= numB){
         System.out.println(isSame); //true
      }

 

논리 연산자?

  • 조건식 1 && 조건식2 : 조건식1과 조건식 2를 모두 만족한다면?

      int numA = 10;
      int numB = 10;
      int numC = 10;
      int numD = 10;
    
      boolean bool = numA == numB && numC < numD; //false
    
      if (bool){
         System.out.println(bool);
      }else {
           **System.out.println(bool);
      }
    
      if (numA == numB && numC == numD){
         System.out.println(numA == numB && numC == numD); //true
      }
  • 조건식1 || 조건식2 : 조건식1 혹은 조건식2를 만족한다면?

      int numA = 10;
      int numB = 10;
      int numC = 10;
      int numD = 10;
    
      boolean bool = numA == numB || numC < numD; //true
    
      if (bool){
         System.out.println(bool); 
      }else {
         System.out.println(bool);
      }
    
      if (numA == numB || numC == numD){
         System.out.println(numA == numB && numC == numD); //true
      }
  • ! : 논리 반전 연산자 : != 같지않다면?, !< 작지않다면?

      int num = 10;
      String conn = null;
    
      boolean bool = num != 9;
    
      if (bool){
         System.out.println(bool);
      }else {
         System.out.println(bool);
      }
    
      if (conn != null){
         System.out.println("true");
      }else {
         System.out.println("false");
      }
  • JDK Assertion (JDK 1.4+)

    이전에 작성된 코드에서 사용된 assert라는 변수들과의 하위 호환성을 위해 기본적으로 비활성화되어 있다.

    ```java
    assert service != null; // true -> 아무 일도 발생하지 않는다.
                                                // false -> AssertionError (Unchecked Exception) 발생
    
    혹은
    
    assert service != null : "Service is Null"; // false 시 추가적으로 메세지를 제공한다.

 

instanceof

  • A instanceof B : (공변) 타입 검증 연산자, A가 B의 타입 혹은 하위 구현체인지 판단한다.
    • Reference Type 만 사용 가능하다.
public static void main(String[] args) {
    InstanceofExample example = new InstanceofExample();
    example.run();
}

public void run() {
    SomeClass someClass = new SomeClass();
    SomeClasz someClasz = new SomeClasz();
    SomeClassChild someClassChild = new SomeClassChild();

    typeCheck(someClass);      // is SomeClass
    typeCheck(someClasz);      // is SomeClasz
    typeCheck(someClassChild); // is SomeClass
}

public void typeCheck(Object obj){
    if (obj instanceof SomeClass){
        System.out.println("is SomeClass");
    }else if (obj instanceof SomeClasz){
        System.out.println("is SomeClasz");
    }
}

class SomeClass {

}
class SomeClassChild extends SomeClass{

}
class SomeClasz {

}

 

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

Primitive 변수에 값을 할당하기 위해 사용하거나 Reference의 참조 값을 할당하기 위해 사용한다.

  • 그 외에도 이항 연산자와 결합하여 사용할 수 있다.
int num = 4;
int variable;

variable = num; // variable = 4;
System.out.println("= 연산 : " + variable); // = 연산 : 4

variable += num; // variable = 4 + 4
System.out.println("+= 연산 : " + variable); // += 연산 : 8

variable -= num; // variable = 8 - 4
System.out.println("-= 연산 : " + variable); // -= 연산 : 4

variable *= num; // variable = 4 * 4
System.out.println("*= 연산 : " + variable); // *= 연산 : 16

variable /= num; // variable = 16 / 4
System.out.println("/= 연산 : " + variable); // /= 연산 : 4

variable %= num; // variable = 4 % 4 
System.out.println("%= 연산 : " + variable); // %= 연산 : 0

variable = 10;
variable &= 2; // variable = 10 & 2
System.out.println("&= 연산 : " + variable); // &= 연산 : 2

variable = 10;
variable |= 2; // variable = 10 | 2
System.out.println("|= 연산 : " + variable); // |= 연산 : 10

variable = 10;
variable ^= 2; // variable = 10 ^ 2
System.out.println("^= 연산 : " + variable); // ^= 연산 : 8

variable = 10;
variable <<= 2; // variable = 10 << 2
System.out.println("<<= 연산 : " + variable); // <<= 연산 : 40

variable = 10;
variable >>= 2; // variable = 10 >> 2
System.out.println(">>= 연산 : " + variable); // >>= 연산 : 2

variable = 10;
variable >>>= 2; // variable = 10 >>> 2
System.out.println(">>>= 연산 : " + variable); // >>>= 연산 : 2

 

Arrow operator? ( (parameter list) -> lambda body )

Java 8에 추가된 lambda 식을 지원하기 위해 사용되는 연산자이다.

// 이러했던 구문을
Runnable runnable = new Runnable() {
   @Override
   public void run() {
       System.out.println("Hello!");
   }
};

// 이렇게 축약할 수 있다.

// 하나의 식, 로직을 가지고 있다면 { } block 과 return도 생략 가능하다.
Runnable runnable = () -> System.out.println("Hello!");

// 그렇지 않다면 이런식으로 작성해야한다.
Runnable runnable = () -> {
    System.out.println("Hello!");
    helloLogic();
**};**

 

3항 연산자? ( Ternary Operator : 변수 = (조건)? true 인 경우 : false 인 경우)

삼항 연산자는 피연산자 3개를 사용하며, if-then-else 명령문의 축약형이라고 할 수 있다.

int num = 10;
String result;

if(num == 10){
        result = "true";
        System.out.println("result = " + result);
}else {
        result = "false;
        System.out.println("result = " + result);
}

result = (num == 10) ? "true" : "false";
System.out.println("result = " + result);

 

연산자 우선순위?

수식 내에 여러 연산자가 함께 등장할 때, 어느 연산자가 먼저 처리될 것인가를 결정합니다.

( ) 등으로 우선순위, 결합 방향에 따른 연산자의 처리 순서를 변경할 수도 있습니다.

 

연산자의 결합 규칙?

수식 내에 우선순위가 같은 연산자가 둘 이상 있을 때, 먼저 어느 연산을 수행할 것인가를 결정합니다.

int numA = 5;
int numB = 2;
int numC = 2;

// +, - 의 우선 순위가 동일하다.
// 결합 방향에 따라서 왼쪽부터 오른쪽으로 진행하게 된다.
int res = numA + numB - numC;

 

Java 13의 switch 연산자?

기존 switch 문법으로 만든 요일 반환식 (윤년을 앞선 로직에서 체크했다고 가정.)

public static String monthCheck(int num){
        int days = 0;
        switch (num) {
            case 1 :
            case 3 :
            case 5 :
            case 7 :
            case 8 :
            case 10 :
            case 12 :
                days = 31;
                break;
            case 4 :
            case 6 :
            case 9 :
            case 11 :
                days = 30;
                break;
            case 2 :
                days = 28;
                break;
            default:
                days = -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

Java 12에서 switch는

  • case 라벨을 쉼표로 구분하여 사용하게끔 문법을 지원하게 되었다.
  • break을 통해 값을 반환할 수 있게끔 되었다.
public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 :
                break 31;
            case 4, 6, 9, 11 :
                break 30;
            case 2 :
                break 28;
            default:
                break -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

혹은 이와 같이 람다 식을 사용할 수 있게 되었다.

public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11 -> 30;
            case 2 -> 28;
            default -> -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

Java 13에서 switch는

  • 기존에 사용하던 break이라는 키워드를 yield로 대체(확장) 하게 되었다.
public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 :
                yield 31;
            case 4, 6, 9, 11 :
                yield 30;
            case 2 :
                yield 28;
            default:
                yield -1;
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

또는

public static String monthCheck(int num){
        int days = switch (num) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11 -> 30;
            case 2 -> 28;
            default -> {
                                System.out.println("잘못 입력했습니다.");
                                yield -1;
                        }
        };
        return "입력하신 달은 "+days+"일 입니다.";
    }

이렇게 표현할 수 있다.

 

Java 12 에서는 해당 기능을 미리보기 기능으로 제공하였고, (활성화 필요)

Java 13 에서는 해당 식을 확장하였으며,

Java 14 에서는 해당 기능을 표준으로 제공하게 되었다.

 

 

참고 자료

+ Recent posts