Java/라이브스터디

연산자

목표

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

학습목표

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

 


연산자 우선순위

 

연산자 표현
접미사 expr++ expr--
단항 ++expr --expr +expr -expr ~ !
곱셈/나눗셈/나머지 * / %
더하기 / 빼기 + -
시프트 << >> >>>
관계 < > <= >= instanceof
평등 == !=
비트 AND &
비트 XOR ^
비트 OR |
논리적 AND &&
논리적 OR ||
삼항 ? :
대입 =, +=, -=, *=, /=, %=, &=, ^=, |=, <<=, >>=, >>>=

 

 

대입, 산술, 그리고 단항 연산자

대입연산자

int a = 10;

좌항의 변수에 우항의 결과 값을 대입하는 연산자입니다.
기본적으로 =를 기준으로 앞에 다른 연산자가 붙는식으로 다양한 파생이 존재합니다.

 

a += 10;
// a = a + 10

a -= 10;
// a = a - 10;

a *= 10;
// a = a * 10;

a /= 10;
// a = a / 10;
// a를 10으로 나눈 몫 반환

a %= 10;
// a = a % 10;
// 10으로 나누고 남은 나머지 반환

a &= 10;
// a = a & 10;
// a 를 4 라고 가정하였을 떄, a와 10을 비트로 나타내면
// a : 0100
// 10 : 1010
// 두개를 AND 연산을 하면 0000 => 0 이 됩니다.
// 비트가 둘 다 1이면 1 반환

a ^= 10;
// a = a ^ 10;
// a : 0100
// 10 : 1010
// XOR => 1110 : 14
// 비트가 다르면 1을 반환

a |= 10;
// a = a | 10;
// a : 0100
// 10 : 1010
// OR => 1110 : 14
// 비트가 둘 중 하나만 1이어도 1 반환

a <<= 10;
// a = a << 10;
// a : 0100
// 비트를 왼쪽으로 10번 미는 연산입니다. ( 뒷부분은 0으로 채워진다 )
// 01 0000 0000 0000 : 4096

a >>= 10;
// a = a >> 10;
// a : 0100
// 비트를 오른쪽으로 10번 미는 연산입니다.
// 오른쪽으로 3회째 미는순간부터 0이되고 왼쪽 비트는 음수는 1, 양수는 0으로 채워집니다.

a >>>= 10;
// a = a >>> 10;
// a : 0100
// 비트를 오른쪽으로 10번 미는 연산입니다.
// >> 연산과는 다르게 왼쪽의 비트가 0로 채워집니다.

 

산술연산자

사칙연산인 +, -, *, /과 % 연산을 의미하는 단어입니다.

각각에 대하여 더하기, 빼기, 곱하기, 나누기 몫, 나머지를 의미합니다.

int result = 10;
System.out.println(result + 5);
System.out.println(result - 5);
System.out.println(result * 5);
System.out.println(result / 5);
System.out.println(result % 5);

===== 출력결과 =====
15
5
50
2
0

 

단항연산자

하나의 항을 가지고 연산하는 연산자를 의미합니다.

기본적으로 부호를 나타내는 +, - 연산자와 증감을 나타내는 ++, --연산자, 그리고 boolean타입에서 쓰이는 !연산자가 존재합니다.

int result = +10;
System.out.println("부호 연산자 : " + result);

result = -10;
System.out.println("부호 연산자 : " + result);

System.out.println("증감 연산자(접미사) : " + result++);
System.out.println("증감 연산자 : " + result);

System.out.println("증감 연산자(접미사) : " + result--);
System.out.println("증감 연산자 : " + result);

System.out.println("증감 연산자(접두사) : " + ++result);
System.out.println("증감 연산자 : " + result);

System.out.println("증감 연산자(접두사) : " + --result);
System.out.println("증감 연산자 : " + result);

boolean bool = false;
System.out.println(!bool);

===== 출력 결과 =====
부호 연산자 : 10
부호 연산자 : -10
증감 연산자(접미사) : -10
증감 연산자 : -9
증감 연산자(접미사) : -9
증감 연산자 : -10
증감 연산자(접두사) : -9
증감 연산자 : -9
증감 연산자(접두사) : -10
증감 연산자 : -10
true

주의해야할 점으로는 증감연산자가 뒤에 붙을경우, 해당 줄의 처리가 모두 종료된 다음 실행됩니다.
반대로 앞에 붙은경우, 증감연산자를 먼저 실행한 후에 해당 줄의 처리가 실행됩니다.

 

비트연산자

  • >> : 오른쪽으로 미는 연산자
  • << : 왼쪽으로 미는 연산자
  • >>> : 오른쪽으로 밀되, 좌측이 무조건 0으로 채워지는 연산자

비트연산자의 종류는 3가지가 존재하며, 시프트 연산자라고도 부릅니다.

byte number = Byte.MAX_VALUE;
System.out.println("MAX값 오른쪽으로 5회 밀기 " + (number >> 5));

number = Byte.MIN_VALUE;
System.out.println("MIN값 오른쪽으로 5회 밀기 " + (number >> 5));

number = Byte.MAX_VALUE;
System.out.println("MAX값 왼쪽으로 5회 밀기 " + (number << 5));

number = Byte.MIN_VALUE;
System.out.println("MIN값 왼쪽으로 5회 밀기 " + (number << 5));

number = Byte.MAX_VALUE;
System.out.println("MAX값 오른쪽으로 5회 밀기 " + (number >>> 5));

number = Byte.MIN_VALUE;
System.out.println("MIN값 오른쪽으로 5회 밀기 " + (number >>> 5));

===== 출력 결과 =====
MAX값 오른쪽으로 5회 밀기 3
MIN값 오른쪽으로 5회 밀기 -4
MAX값 왼쪽으로 5회 밀기 4064
MIN값 왼쪽으로 5회 밀기 -4096
MAX값 오른쪽으로 5회 밀기 3
MIN값 오른쪽으로 5회 밀기 134217724

 

 

0111 1111 : Byte MAX 값
=> 우측으로 5회 밀기
0000 0011 : 3

1000 0000 : Byte MIN 값
=> 우측으로 5회 밀기
1111 1100 : -4

0111 1111 : Byte MAX 값
=> 좌측으로 5회 밀기
0000 1111 1110 0000 : 4064
> 비트를 왼쪽으로 밈으로써 값이 커지게되어 자동으로 int 할당바이트인 8 비트까지 늘어났다.

1000 0000 : Byte MIN 값
=> 좌측으로 5회 밀기
1111 0000 0000 0000 : -4096
> 실제로 밀었을 땐, 왼쪽에 0 3개가 있어야하지만 부호비트를 채워줌으로써 -4096이 되었다.

0111 1111 : Byte MAX 값
=> 우측으로 5회 밀기 >>> 연산
0000 0011 : 3

1000 0000 : Byte MIN 값
=> 우측으로 5회 밀기 >>> 연산
0000 0100 : 4..? 
> 4가 나온다고 생각했으나 134217724라는 값이 나왔다. 해당 값을 2진수로 나타내면 다음과 같다.
> 111_1111_1111_1111_1111_1111_1100

출력 결과에 대한 설명

 

 

byte number = Byte.MIN_VALUE;
number >>>= 5;
System.out.println(number);

===== 출력 결과 =====
-4

비트로 변경하면 1111 1100 이 된다.

음수 최대값에 대한 >>> 연산의 결과값이 의도한대로 나오지 않아서 >>>= 연산자를 이용하여 저장한 다음 출력해보았다.

예상한대로 결과가 나오지않아서 원인을 찾아봤더니, int보다 작은 비트타입을 가진 변수들은 int로 변환된 다음에 출력된다고 한다.

 

 

Byte의 MIN값으로 1000 0000 이 들어간 것 까지는 맞다.

하지만 >>> 연산시에 int로 형변환이 되기 때문에
1111 1111 1111 1111 1111 1111 1000 0000 으로 변하게 된다.

해당 비트를 오른쪽으로 5회 밀게되면

0000 0111 1111 1111 1111 1111 1111 1000 이 되며, 해당 값은 134217724가 맞다.

따라서 int보다 작은 타입에서 >>> 연산은 안하는게 좋다.

 

관계 연산자

관계 연산자의 종류는 다음과 같다.

  • == : Equal to ( 우항과 같다 )
  • != : Not Equal to ( 우항과 다르다 )
  • > : Greater than ( 우항보다 크다 )
  • >= : Greater then or Equal to ( 우항보다 크거나 같다 )
  • < : Less then ( 우항보다 작다 )
  • <= : Less then or Equal to ( 우항보다 작거나 같다 )

해당 연산자들은 관계에 대한 결과값을 boolean 타입으로 반환해준다.
따라서 단독으로 사용하는 연산자가 아니라 조건을 걸어줄 때 사용한다. (ex : if, for, while ..)

int value1 = 1;
int value2 = 2;
if(value1 == value2)
    System.out.println("value1 == value2");
if(value1 != value2)
    System.out.println("value1 != value2");
if(value1 > value2)
    System.out.println("value1 > value2");
if(value1 < value2)
    System.out.println("value1 < value2");
if(value1 <= value2)
    System.out.println("value1 <= value2");

===== 출력 결과 =====
value1 != value2
value1 < value2
value1 <= value2

 

논리연산자 ( 조건부 연산자 )

논리 연산자의 종류는 다음과 같다.

  • & ( 비트 AND )
  • | ( 비트 OR )
  • ^ ( 비트 XOR )
  • && ( 논리적 AND )
  • || ( 논리적 OR )
int value1 = 1;
int value2 = 2;
if((value1 == 1) && (value2 == 2))
    System.out.println("value1 is 1 AND value2 is 2");
if((value1 == 1) || (value2 == 1))
    System.out.println("value1 is 1 OR value2 is 1");

=== 출력 결과 ===
value1 is 1 AND value2 is 2
value1 is 1 OR value2 is 1
int speed = 5;
if(++speed == 6 || --speed == 4){
    System.out.println(true);
}
System.out.println(speed);

=== 출력 결과 ===
true
6

먼저 논리적 AND, OR연산의 결과값은 boolean 이다.
즉, 결과값으로 반환되는 값은 true 또는 false가 나오게된다.
좌항과 우항이 반드시 필요한 연산이며, 주로 조건설정시에 사용된다.

 

선행되는 조건에서 이미 조건의 결과가 결정되면 후행되는 조건의 연산은 이뤄지지 않는다.

 

int value1 = 1;
int value2 = 2;

System.out.println(value1 & value2);
System.out.println(value1 | value2);
System.out.println(value1 ^ value2);

== 출력 결과 ==
0
3
3

다음으로 비트 AND, OR, XOR 의 경우 결과값은 정수가 나오게된다.

 

 


실수의 경우 아예 비트연산이 불가능하다고 빨간줄이 그인다.

 

 

삼항 연산자

약간 특이한 연산자로 삼항연산자가 존재한다.

(조건) ? (참일 경우) : (거짓일 경우)

의 형태로 사용되는 연산자로 if-then-else 를 한줄로 표현한 것이라고 생각하면 된다.

 

int value1 = 1;
int value2 = 2;
int result;
boolean someCondition = true;
result = someCondition ? value1 : value2;

System.out.println(result);

===== 출력 결과 =====
1

 

instanceof

객체가 특정 클래스의 객체가 맞는지 확인하는 연산자이다.

[객체명] instanceof [클래스명];

의 꼴로 사용되며, 결과값은 boolean 타입이다.

 

Parent obj1 = new Parent();
Parent obj2 = new Child();

System.out.println("obj1 instanceof Parent: "
        + (obj1 instanceof Parent));
System.out.println("obj1 instanceof Child: "
        + (obj1 instanceof Child));
System.out.println("obj1 instanceof MyInterface: "
        + (obj1 instanceof MyInterface));
System.out.println("obj2 instanceof Parent: "
        + (obj2 instanceof Parent));
System.out.println("obj2 instanceof Child: "
        + (obj2 instanceof Child));
System.out.println("obj2 instanceof MyInterface: "
        + (obj2 instanceof MyInterface));

===== 출력 결과 =====
obj1 instanceof Parent: true
obj1 instanceof Child: false
obj1 instanceof MyInterface: false
obj2 instanceof Parent: true
obj2 instanceof Child: true
obj2 instanceof MyInterface: false
Child가 Parent를 상속 중

obj1의 경우 Parent의 생성자를 가지고 초기화하였기 때문에 Parent의 객체가 맞으며, Child의 객체는 아니다.

반면에 obj2의 경우 Child의 생성자를 가지고 초기화하였기 깨문에 Parent의 특성과 Child의 특성 두가지 모두를 지니고있어 둘 모두의 인스턴스가 된다.

 

화살표( -> ) 연산자

자바 8부터 도입된 람다식을 표현하는데 사용되는 연산자.

 

람다식이란, 추후 실행을 위하여 클래스의 메소드 구현을 하는 상황에서 더 쉽게 작성할 수 있는 표현식이다.

//람다식 적용 이전
Runnable r = new Runnable() {
  public void run() {
    System.out.println("Howdy, world!");
  }
};
r.run();

// 적용 이후
Runnable r2 = () -> System.out.println("Howdy, world!");
r2.run();

 

Java13, switch 연산자

기존의 switch 문

int a = 10;
switch (a){
    case 1:
    case 2:
    case 3:
        System.out.println("3보다 작거나 같다");
        break;
    case 4:
    case 5:
    case 6:
        System.out.println("6보다 작거나 같다.");
        break;
    case 7:
    case 8:
    case 9:
        System.out.println("9보다 작거나 같다.");
        break;
    default:
        System.out.println("9보다는 크다!");
        break;
}


하나의 case에 여러개의 조건을 걸 수 없는 모습을 볼 수 있으며, break를 통하여 switch 구문을 탈출하는 것을 알 수 있다.

 

 

Java 12 이후의 switch

int a = 10;
switch (a){
    case 1, 2, 3 -> System.out.println("3보다 작거나 같다");
    case 4, 5, 6 -> System.out.println("6보다 작거나 같다.");
    case 7, 8, 9 -> System.out.println("9보다 작거나 같다.");
    default -> System.out.println("9보다는 크다!");
}

하나의 case에 여러개의 조건을 달아줄 수 있게 되었으며, : 대신 -> 연산자를 통하여 표현할 수 있다.
case에 대하여 여러줄의 코드작성이 필요하면 ->와 {} 를 사용하여 표현이 가능해졌다.

 

 

int a = 10;
System.out.println(
    switch (a){
        case 1, 2, 3 -> "3보다 작거나 같다";
        case 4, 5, 6 -> "6보다 작거나 같다.";
        case 7, 8, 9 -> "9보다 작거나 같다.";
        default -> "9보다는 크다!";
    }
);

추가적으로 위의 코드와 같이 값의 반환도 가능하게 변하여 이제 구문보다는 연산자의 성격을 띄게 되었다.

 

 

Java 13 이후의 swtich

 

int a = 10;
String str = switch (a){
    case 1,2,3:
        yield "3보다 작다.";
    case 4,5,6:
        yield "6보다 작다.";
    case 7,8,9:
        yield "9보다 작다.";
    default:
        yield "9보다는 크다!";
};
System.out.println(str);

자바 SE 13버전 이후에 들어서는 yield를 이용하여 반환값을 특정할 수 있게 되었다.

 

참고자료