[STUDY HALLE] 3주차 - 연산자

ByWindow·2021년 1월 10일
0

Java_Live_Study

목록 보기
3/4
post-thumbnail

목표

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

학습할 것

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

연산자(Operator)

  • 연산을 수행하는 기호

  • 그 기능에 따라 여러 가지의 연산자가 있다

  • 피연산자(Operand) : 연산의 대상으로, 연산자가 연산을 수행하기 위해 반드시 필요한 것(e.g. 변수, 상수, 리터럴, 수식)

    x + 5
    위와 같은 식이 있을 때

    • 연산자 : +
    • 피연산자 : x, 5
  • 대부분의 연산자는 2개의 피연산자를 필요로 하지만, 1 또는 3개를 필요로 하는 것도 있다

  • 연산자는 피연산자로 연산을 수행하고 나면 항상 결과값을 반환한다

연산자의 종류


산술 연산자

  • +(덧셈), -(뺄셈), /(나눗셈), *(곱셈), %(나머지)
  • '+' 연산에서 두 피연산자가 byte형일 때 연산자 '+'는 두 피연산자의 자료형을 int형으로 변환한 다음 연산을 진행한다
    다음과 같은 코드에서는 에러가 나타난다

    따라서 명시적인 형변환이 필요하다
public class Test {
       public static void main(String[] args) {
       byte a = 5;
       byte b = 10;
       byte c = (byte)(a * b);
       System.out.println(c);
   }
}
  • '/' 연산자의 피연산자가 int타입인 경우, 연산결과 역시 int타입이므로
    소수점 이하는 버리고 정수만 남겨지게 된다
    (e.g.) 10 / 3 = 3
  • '/' 연산자의 피연산자 중 하나가 실수형인 경우, 다른 한쪽도 형변환되어 실수의 값을 얻는다
    (e.g.) 10 / 3.0f → 10.0f / 3.0f → 3.3333333
  • 즉, 두 피연산자의 타입이 일치하지 않을 때는 더 넓은 타입으로 일치시킨 후에 연산을 수행한다
  • 피연산자가 정수형인 경우, 나누는 수로 0을 사용할 수 없다
  • 부동소수점 값인 0.0f, 0.0d로 나누는 것은 가능하지만, 결과값은 Infinity다
  • 이렇게 산술 연산자에서는 형변환(casting)을 유념해야 된다
public static void main(String[] args) {
       int a = 1000000;
       int b = 3000000;
       long c = a * b;
       System.out.println(c); //출력값 : 2112827392
       c = (long)a * b;
       System.out.println(c); //출력값 : 3000000000000
}

↳ (int타입 * int타입)의 연산결과는 int타입이기 때문에 long형으로 자동 형변환되어도 값은 변하지 않는다. 따라서 a 또는 b를 long형으로 형변환 시켜주어야 한다.

public static void main(String[] args) {
             char a = 'A';
             char b = 'b';
             System.out.println(a-b); //출력값 : -33

             char nine = '9';
            char one = '1';
             System.out.println(nine-one); //출력값 : 8
}

↳ 사칙연산의 피연산자로 문자도 가능하다. 해당 문자의 유니코드로 바뀌어 계산된다.

  • 나머지 연산자 (%)
    왼쪽의 피연산자를 오른쪽 피연산자로 나누고 난 나머지 값을 결과로 반환한다
    나누는 수로 0을 사용할 수 없고, 피연산자로 정수만 허용된다
    짝수, 홀수, 배수 검사 등에 주로 사용된다

비트 연산자

  • 피연산자를 이진수로 표현했을 때의 각 자리에서 규칙에 따라 연산한다
  • 피연산자로 실수는 허용되지 않고, 정수 혹은 문자만 허용된다
  • | (OR 연산자) : 피연산자 중 한 쪽의 값이 1이면 결과는 1, 그 외에는 0
  • & (AND 연산자) : 피연산자 양 쪽이 모두 1이면 결과는 1, 그 외에는 0
  • ^ (XOR 연산자) : 피연산자의 값이 서로 다르면 결과는 1. 그 외에는 0
  • ~ (비트 전환 연산자) : 피연산자를 2진수로 표현했을 때 0은 1로, 1은 0으로 전환한다
  • << (왼쪽 쉬프트 연산자) : 좌측 피연산자를 2진술로 표현했을 때 각 자리를 왼쪽으로 우측 피연산자만큼 이동시킨다
  • >> (오른쪽 쉬프트 연산자) : 좌측 피연산자를 2진술로 표현했을 때 각 자리를 오른쪽으로 우측 피연산자만큼 이동시킨다

public static void main(String[] args) {
              int x = 0xA5;
              int y = 0xC;
             
              System.out.println("x = " + x + "\t\t\tint타입");
              System.out.println("y = " + y + "\t\t\tint타입");
              System.out.println("x = " + Integer.toBinaryString(x) + "\t32bit 2진수");
              System.out.println("y = " + Integer.toBinaryString(y) + "\t\t32bit 2진수");

              System.out.println(Integer.toHexString(x) + " | " + Integer.toHexString(y) + " = " + Integer.toBinaryString(x|y) + " = " + (x|y));
              System.out.println(Integer.toHexString(x) + " & " + Integer.toHexString(y) + " = " + Integer.toBinaryString(x&y) + " = " + (x&y));
              System.out.println(Integer.toHexString(x) + " ^ " + Integer.toHexString(y) + " = " + Integer.toBinaryString(x^y) + " = " + (x^y));
              System.out.println(Integer.toHexString(x) + " ^ " + Integer.toHexString(y) + " ^ " + Integer.toHexString(y) + " = " + Integer.toBinaryString(x^y^y) + " = " + (x^y^y));
              System.out.println("~" + Integer.toHexString(x) + " = " + Integer.toBinaryString(~x) + " = " + (~x));
              System.out.println(Integer.toHexString(x) + " >> 2 = " + Integer.toBinaryString(x>>2) + " = " + (x>>2));
              System.out.println(Integer.toHexString(x) + " << 2 = " + Integer.toBinaryString(x<<2) + " = " + (x<<2));
}```

위 코드에 대한 출력값

관계 연산자

  • 비교 연산자라고도 한다
  • 주로 조건문과 반복문의 조건식에 사용되며, 연산결과는 오직 true 아니면 false 둘 중 하나다
  • 이항 연산자이므로 피연산자의 타입이 서로 다를 경우, 자료형의 범위가 넓은 쪽으로 자동 형변환하여 피연산자의 타입을 일치시킨 후에 비교한다
    • 대소 비교
    관계 연산자연산결과
    >좌변 값이 크면 true, 아니면 false
    <우변 값이 크면 true, 아니면 false
    >=좌변 값이 크거나 같으면 true, 아니면 false
    <=우변 값이 크거나 같으면 true, 아니면 false
    • 등가 비교
    관계 연산자연산결과
    ==두 값이 같으면 true, 아니면 false
    !=두 값이 다르면 true, 아니면 false
    - 두 값의 타입이 달라도 된다 (e.g. 10 == 10.0f 는 true이다)
    - String타입의 문자열을 비교할 때는 관계연산자'==' 대신 equals()라는 메서드를 사용한다

논리 연산자

  • 둘 이상의 조건을 'AND'나 'OR'으로 연결하여 하나의 식으로 표현한다

    || (OR결합): 피연산자 중 어느 한 쪽만 true이면 true
    && (AND결합) : 피연산자 양쪽 모두 true이면 true
    ! (논리 부정 연산자) : 피연산자가 true이면 false, false이면 true

  • Short Circuit Evaluation

    • 논리 연산자는 효율적인 연산을 한다
    • OR연산의 경우, 두 개의 피연산자 중 하나만 true이면 연산결과가 true이므로 좌측 피연산자가 true일 때 우측 피연산자의 값은 평가하지 않는다
    • AND연산의 경우, 두 개의 피연산자 중 하나만 false이면 연산결과가 false이므로 좌측 피연산자가 false일 때 우측 피연산자의 값은 평가하지 않는다
    • 따라서, OR연산의 경우는 true일 확률이 높은 조건식을 좌측 피연산자에, AND연산자의 경우는 false일 확률이 높은 조건식을 좌측 피연산자에 놓음으로써 연산속도를 높일 수 있다

instanceof 연산자

  • 객체 타입을 확인하는 연산자이다
  • 형변환 가능여부를 확인하며 true / false 로 결과를 반환한다
  • 상속 관계에서 부모객체인지 자식객체인지 확인하는데 사용된다
  • 좌측 피연산자(객체)가 우측 피연산자(클래스)에 속할 때 true를 반환한다
  • 값이 null인 참조변수에 대해 instanceof 연산을 수행하면 false를 반환한다
class Parent{} // 부모 객체

class Child extends Parent {} // 부모 객체를 상속받는 자식 객체

public class Test {
      public static void main(String[] args) {
            Parent p = new Parent();
            Child c = new Child();

            System.out.println(p instanceof Parent); // true
            System.out.println(p instanceof Child); // false
            System.out.println(c instanceof Child); // true
            System.out.println(c instanceof Parent); // true
      }
}

assignment (=) 연산자

  • 저장공간(e.g. 변수)에 값 또는 수식의 연산결과를 저장할 때 사용
  • 우측 피연산자(rvalue)의 값(또는 평가값)을 좌측 피연산자(lvalue)에 저장하고 저장된 값을 연산결과로 반환
  • 대입연산자는 연산자들 중에서 가장 낮은 우선순위를 가지고 있어서 식에서 제일 나중에 수행된다
int x;
System.out.println(x = 3); // 출력값 : 3 (x에 3이 저장된다)
System.out.println(x + 3); // 출력값 : 6

복합 대입 연산자 (op=)

  • 대입연산자(=)는 다른 연산자(operator)과 결합하여 사용될 수 있다

화살표 연산자 (->)

람다식이란?

화살표 연산자를 알아보기 전에 람다식에 대한 개념부터 알아야 한다
Java에 두 번의 큰 변화가 있다면 JDK1.5에서의 generics, JDK1.8에서의 lambda expression일 것이다
람다식(lambda experssion)은 메서드를 하나의 식으로 표현하여 함수를 간략하면서 명확한 식으로 나타낼 수 있다
메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어지므로, 람다식을 익명함수(anonymous function)이라고도 한다
모든 메서드는 클래스에 포함되어야 하므로 클래스도 새로 만들어야 하고 객체도 생성해야만 메서드를 호출할 수 있는데, 람다식은 이 모든 과정없이 오직 람다식 자체만으로도 메서드의 역할을 대신할 수 있다

함수(function)와 메서드(method)의 차이점
객체지향개념에서는 함수를 객체의 행위나 동작을 의미하는 메서드라는 용어로 사용한다
메서드는 함수와 같은 의미이지만, 특정 클래스에 반드시 속해야 한다는 제약이 있기 때문에 기존의 함수와 같은 의미의 다른 용어를 선택해서 사용한 것이다
그러나 이제 람다식을 통해 메서드가 하나의 독립적인 기능을 하기 때문에 함수라는 용어를 사용하게 된 것이다

람다식에 대해서는 다음에 인터페이스와 람다식에 대해 공부할 때 더 자세히 알아보고,
화살표 연산자가 어떻게 사용되는지만 훓고 넘어가자

람다식 작성하기

보통 우리는 메서드를 정의할 때 다음과 같이 작성한다

반환타입 메서드이름(매개변수 선언){
	본문
}

람다식은 메서드에서 이름과 반환타입을 제거하고 매개변수 선언부와 몸통{} 사이에 화살표(->)를 추가하여 작성한다

(매개변수 선언) -> { 본문 }

또한, 반환값이 있는 메서드의 경우에는 return문 대신 식(expression)으로 표현할 수 있다
식의 연산결과가 자동으로 반환값이 된다(식의 끝에는 ';'을 붙이지 않는다)
람다식에 선언된 매개변수의 타입은 추론이 가능한 경우 생략될 수 있으며, 대부분 생략가능하다
필요로 하는 매개변수가 하나라면 매개변수 선언부의 괄호를 생략할 수 있지만, 매개변수의 타입이 있으면 생략할 수 없다
메서드의 본문이 하나의 문장일 때는 괄호{ }를 생략할 수 있고, 이 때는 문장의 끝에 ';'을 붙이면 안된다
람다식에서 괄호{ } 안의 문장이 return문일 경우에는 괄호{ }를 생략할 수 없다

다음은 메서드를 람다식으로 표현한 몇 가지 예이다

/*method*/
int min(int x, int y){
	return a < b ? a : b;
}
/*lambda*/
(int a, int b) -> { return a < b ? a : b; };
(int a, int b) -> a < b ? a : b
(a, b) -> a < b ? a : b

/*method*/
void printNameID(String name, int ID){
	System.out.println("name : " + name + "\tID : " + ID);
}
/*lambda*/
(String name, int ID) -> {System.out.println("name : " + name + "\tID : " + ID);}
(naem, ID) -> {System.out.println("name : " + name + "\tID : " + ID);}
(naem, ID) -> System.out.println("name : " + name + "\tID : " + ID)

3항 연산자 ( ? : )

조건식, 식1, 식2 모두 세 개의 피연산자를 필요로 해서 3항 연산자(조건 연산자)이다

(조건식) ? 식1 : 식2

위와 같은 구조이며, 조건식이 true일 때 식1을 수행하고 조건식이 false일 때 식2를 수행한다
3항 연산자를 중첩해서 사용하면 셋 이상 중의 하나를 결과로 얻을 수 있다

/* a가 양수면 1, 0이면 0, 음수면 -1을 result에 저장하는 문장*/
int result = (a > 0) ? 1 : (a == 0 ? 0 : -1);

3항 연산자의 결합규칙은 오른쪽에서 왼쪽이다
그 이유로 위의 코드에서 괄호( )는 필요가 없지만 가독성을 위해 사용했다
만약 식1과 식2, 두 피연산자의 타입이 다른 경우에는 이항 연산자에서처럼 자동 형변환이 일어난다

연산자 우선순위와 결합규칙

식에 사용된 연산자가 둘 이상인 경우에 연산자의 우선순위에 의해서 연산순서가 결정된다
또한, 하나의 식에 같은 우선순위의 연산자들이 여러 개 있는 경우, 우선순위가 같다고 해서 아무거나 먼저 처리하는 것이 아니고 나름대로의 규칙을 가지고 있는데 그 규칙을 연산자의 결합규칙이라고 한다
말로서 정리하자면 다음과 같다

  • 산술 > 관계 > 논리 > 대입 순이다. 대입은 제일 마지막에 수행됨
  • 단항 > 이항 > 삼항 순이다
  • 단항 연산자, 삼항 연산자, 대입 연산자를 제외한 모든 연산의 진행방향(결합규칙)은 왼쪽에서 오른쪽이다

    (참고)
    단항 연산자에 있는 '+'와 '-'는 부호 연산자이다
    괄호는 연산자가 아니라 연산자의 우선순위를 임의로 지정할 때 사용하는 기호이다

Java 13. switch 연산자

switch는 if와 비슷한 조건식 문법으로 조건에 따라 분기해야 할 내용이 많아질 경우 사용하는 문법으로 알고 있었다
하지만 Java13에서 switch는 문법이 아니라 operator(expression)으로도 사용된다
switch도 다른 연산자처럼 데이터를 처리하고 그 결과가 존재한다는 것이다
Java 12Java 13에서 switch연산자가 어떻게 작동하는지 설명되어 있는 참고자료이다

profile
step by step...my devlog

0개의 댓글