[Java/자바] 연산자의 모든 것

연산자는 '연산을 수행하는 기호'를 말하며 +, -, * 등이 연산자에 해당됩니다.

 

연산자가 연산을 수행하려면 연산자의 대상이 있어야하는데, 이 대상을 '피연산자'라고 합니다.

피연산자로 상수, 변수, 식을 사용할 수 도 있습니다.

연산자(operator) = 연산을 수행하는 기호 ( +, -, * 등 )
피연산자(operand) = 연산자의 작업 대상(변수, 상수, 리터럴, 수식)

 

연산자의 종류

연산자의 종류는 다음과 같으며 하나씩 알아보도록 하겠습니다.

종류 연산자 설명
산술 연산자 +   -    *    /    %     <<     >> 사칙 연산( +, -, *, / )과 나머지 연산 ( % )
비교 연산자 >    <     >=     <=    ==    != 크고 작음과 같거나 크고 작음, 같은 다름을 비교
논리 연산자 &&     ||     !     &     |     ^     ~ 그리고 (AND) 와 또는 (OR)으로 조건 연결
대입 연산자 = 우변의 값을 좌변에 저장
삼항 연산자 (type) ?  true : false 타입이 true인지 false인지 판별

 

연산자의 우선운위와 연산방향
  1. 단항, 이항, 삼항 연산자 순으로 우선순위를 가집니다.
  2. 산술, 비교, 논리, 대입 연산자 순으로 우선순위를 가집니다.
  3. 단항, 부호, 대입 연산자를 제외한 모든 연산의 방향은 왼쪽에서 오른쪽입니다.
  4. 괄호 ()를 사용하면 가장 우선순위로 연산합니다.

출처 : 혼공 자바

 


단항 연산자

 

증감연산자 ++, --

증감연산자는 피연산자에 저장된 값을 1 증가 또는 감사 시킵니다.

증가 연산자 (++) = 피연산자의 값을 1 증가시킨다.
감소 연산자 (--) = 피연산자의 값을 1 감소하킨다.

 

일반적으로 단항 연산자는 피연산자의 왼쪽에 위치하지만, 증감 연산자는 피연산자의 왼쪽 오른쪽 둘다 위치가능하며,

위치에 따라 전위형, 후위형으로 나뉩니다.

타입 설명 사용예시
전위형 값이 참조되기 전에 증가시킨다 j = ++i;
후위형 값이 참조된 후에 증가시킨다 j = i++;

 

결과적으로는 증가되는 것은 같지만 차이점을 알아보자면 다음 코드를 보면 됩니다.

public class Operator3 {

	public static void main(String[] args) {
		int i = 5, j = 5;
		System.out.println(i++);
		System.out.println(++j);
		System.out.println("i =" + i + ", j = " + j);
	}

}

출력 결과

위 코드를 보면, i++ (후위형) 은 값이 증가 되기 전에 i에 저정된 값 5를 println()에게 넘겨주고 나서 i 값이 증가해 5가 출력되고,

++j (전위형) 은 j에 저장된 값을 증가시킨 후에 println()에게 값을 넘겨주므로 6이 출력됩니다.

결과적으로는 i, j 모두 1씩 증가되어 6이 됩니다. 

 

부호 연산자 + , -

부호 연산자는 피연산자 왼쪽에 위치하며 '-' 음수인지 '+' 양수인지 판별합니다.

부호 연산자는 boolean과 char를 제외한 기본형에만 사용할 수 있습니다.

 

public class Operator4 {
	public static void main(String[] args) {
		int i = -10;
		i = +i;
		System.out.println(i);
		
		i = -10;
		i = -i;
		System.out.println(i);
	}

}

출력 결과

 


산술 연산자

 

사칙 연산자 +, -, *, /

사칙 연산자는 덧셈 ( + ), 뺄셈 ( - ), 곱셈 ( * ), 나눗셈 ( / )으로 프로그래밍에서 가장 많이 사용되는 연산자입니다.

public class OperatorEx5 {

	public static void main(String[] args) {
		int a = 10;
		int b = 4;
		
		System.out.printf("%d + %d = %d%n", a, b, a+b);
		System.out.printf("%d - %d = %d%n", a, b, a-b);
		System.out.printf("%d * %d = %d%n", a, b, a*b);
		System.out.printf("%d / %d = %d%n", a, b, a/b); // int 형이므로 소숫점 버려짐
		System.out.printf("%d / %f = %f%n", a, (float)b, a / (float)b); 
	}
}

출력 결과

원래 10 / 4 는 결과가 2.5여야하는데 a와 b가 int 형이므로 소숫점을 버려 2라는 결과가 나옵니다.

소숫점 결과까지 얻고 싶을 때는 int 보다 표현범위가 변수 둘 또는 하나를 큰 float 형으로 형변환하여 연산하면 2.5를 얻을 수 있습니다.

 

또 다른 예시를 봅시다.

public class OperatorEx7 {

	public static void main(String[] args) {
		byte a = 10;
		byte b = 30;
		byte c = (byte) (a*b);
		System.out.print(c);
	}

}

출력 결과

원래 10 * 30은 300이라는 값이 나와야합니다.

하지만 byte의 표현가능한 범위는 -128 ~ 127 로 300은 표현범위를 넘어서 값 손실이 일어나 44 라는 값이 결과로 나왔습니다.

이처럼 값 손실을 방지하기 위해서는 충분히 큰 자료형을 사용해야합니다.

 

 

또한 숫자뿐만 아니라 문자도 연산이 가능합니다.

public class OperatorEx11 {
	public static void main(String[] args) {
		char a = 'a';
		char e = 'e';
		char zero = '0';
		char three = '3';
		
		System.out.printf("'%c' - '%c' = %d%n", e, a, e - a);
		System.out.printf("'%c' - '%c' = %d%n", three, zero, three - zero);
		System.out.printf("'%c'= %d%n", a, (int)a);
		System.out.printf("'%c'= %d%n", e, (int)e);
		System.out.printf("'%c'= %d%n", zero, (int)zero);
		System.out.printf("'%c'= %d%n", three, (int)three);

	}
}

출력 결과

해당 문자들은 위와 같은 유니코드 코드를 가져 위와 같은 연산이 가능합니다.

문자 코드 문자 코드 문자 코드
0 48 A 65 a 97
1 49 B 66 b 98
2 50 C 67 c 99
3 51 D 68 d 100
4 52 E 69 e 101
5 53 ... ... .... ...
6 54 W 87 w 119
7 55 X 88 x 120
8 56 Y 89 y 121
9 57 Z 90 z 122

 

유니코드 코드를 이용해 다음과 같이 대소문자로 변경할 수 도 있습니다.

public class OperatorEx15 {
	public static void main(String[] args) {
		char lowerCase = 'a';
		 // 대문자 A는 소문자 a보다 코드 값이 32가 작다.
		// 반대로 대문자를 소문자로 변환하려면 대문자의 코드 값에 32를 더해주면된다.
		char upperCase = (char) (lowerCase - 32); 
		System.out.println(upperCase);
	}
}

 

 나머지 연산자 %

나머지 연산자는 왼쪽의 피연산자를 오른족 피연산자로 나누고 난 나머지 값을 결과로 반환합니다.

나머지 연산자는 주로 짝수(n%2==0) , 홀수(n%3==0) , 배수, 약수를 구하는데 주로 사용됩니다.

public class OperatorEx20 {
	public static void main(String[] args) {
		System.out.println(10%9);
		System.out.println(-10%9);
		System.out.println(10%-9); // 피연산자의 부호는 무시한다.
		System.out.println(-10%-9);  // 피연산자의 부호는 무시한다.
	}

}

출력 결과

 

 


비교 연산자

 

대소비교 연산자 < , > , <= , >=

두 피연산자의 값의 크기를 비교합니다. 참이면 true를, 거짓이면 false를 반홚바니다.

기본형 중에서는 boolean형을 제외하고 다 사용할 수 있지만, 참조형에서는 사용할 수 없습니다.

 

비교연산자 연산결과
> 좌변 값이 크면 true, 아니면 false
< 좌변 값이 작으면, true 아니면 false
>= 좌변 값이 크거나 같으면 true, 아니면 false
<= 좌변 값이 작거나 같으면 true, 아니면 false

 

등가비교 연산자 == , !=

두 피연산자의 값이 같은지 또는 다른지를 비교합니다.

등가비교 연산자는 기본형은 물론 참조형, 즉 모든 자료형에 사용할 수 있습니다.

비교연산자 연산결과
== 두 값이 같으면 true, 아니면 false
!= 두 값이 다르면 true, 아니면 false

 

문자열의 비교

두 문자열을 비교할 때는 비교 연산자 == 대신, equals() 라는 메서드를 사용해야 합니다.

equals()는 비교하는 두 문자열이 같으면 true를 다르면 false를 반환합니다.

public class OperatorEx23 {
	// 문자열은 비교 할 대는 == 연산자 말고 equals() 메서드를 사용해야한다.
	// equals() 는 서로 객체가 달라도 내용이 같으면 true를 반환한다.

	public static void main(String[] args) {
    
		 /* String은 클래스이므로 아래 처럼 new를 사용해 객체를 생성해야하지만, 
        특별히 String만 예외로 new를 사용하지 않고 str1처럼 객체를 생성할 수 있다. */
        
        String str1 = "abc";
        String str2 = new String("abc");

		System.out.printf("\"abc\"==\"abc\" ? %b%n", "abc"=="abc");
		System.out.printf(" str1 ==\"abc\" ? %b%n", str1=="abc");
		System.out.printf(" str2 ==\"abc\" ? %b%n", str2=="abc");
		System.out.printf("str1.equals(\"abc\") ? %b%n", str1.equals("abc"));
		System.out.printf("str2.equals(\"abc\") ? %b%n", str2.equals("abc"));
		
		// equalsIgnoreCase() 는 대소문자를 구별하지 않고 비교한다.
		System.out.printf("str2.equalsIgnoreCase(\"ABC\") ? %b%n", str2.equalsIgnoreCase("ABC"));


	}
}

출력 결과

 

위 결과를 보면 비교연산자 ( == ) 를 사용했을 시 str2와 "abc"의 내용이 같은데도 false를 반환한다.

내용은 같지만 다른 객체이기 때문이다.

하지만 equals()는 객체가 달라도 내용이 같으면 true를 반환한다.

그렇기 때문에 문자열을 비교할땐 꼭 equals()를 사용해야한다.


논리 연산자

논리 연산자 &&, ||

&& (AND 그리고) = 피연산자 양쪽이 모두 true 여야 true를 결과로 얻는다.
|| (OR 또는) = 피연산자 중 어는 한쪽 만 true이면 true를 결과로 얻는다.

 

유니코드를 이용한 숫자 문자 확인 코드

import java.util.*;

public class OperatorEx25 {

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		char ch = ' ';
		System.out.println("문자를 하나 입력하세요. : ");
		
		String input = scanner.nextLine();
		ch = input.charAt(0);
		
		if('0' <= ch && ch <= '9') {
			System.out.println("입력하신 문자는 숫자 입니다");
		}
		
		if(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) {
				System.out.println("입력하신 문자는 영문자입니다.");
	}

}
}

 

효율적인 연산

OR연산자 "||" 의 경우 한 쪽만 참이면 true를 반환하기 때문에, 좌측 피연산자가 true이면 우측 피연산자의 값을 평가하지 않습니다.

AND 연사자 "&&" 의 경우 둘 중 하나라도 거짓이면 false를 반환하기 때문에, 좌측 연산자가 false이면, 우측 피연산자의 값을 평가하지 않습니다.

 

그래서 이 특징을 사용해 피연산자의 위치에 따라 연산속도를 효율적으로 할 수 있습니다.

OR 연산자 "||" 의 경우에는 연산결과가 "true"일 확률이 높은 것을 왼쪽에,

AND 연산자 "&&" 의 경우에는 연산결과가 "false" 일 확률이 높은 것을 왼쪽에 놓아야 더 빠른 연산 결과를 얻을 수 있습니다. 

 

 

논리 부정 연산자  !

이 연산자는 피연산자가 true 이면 false를 , false 이면 true로 결과를 반환합니다.

즉 true와 false 결과 값을 반대로 바꿉니다.

x !x
true false
false true

어떠한 값에 논리 부정 연산자 ! 를 반복 사용하면 참과 거짓이 차례대로 반복됩니다.

이러한 성질을 이용하여 TV 전원버튼 같은 토글 버튼을 논리적으로 구현할 수 있습니다.

 

 

비트 연산자 &  |   ^   

비트 연산자는 피연산자를 비트단위로 논리 연산합니다.

| (OR 연산자) = 피연산자 중 한쪽 값이 1이면, 1의 결과를 얻고, 그 외에는 0을 얻는다.
& (AND 연산자) = 피연산자 양 쪽이 모두 1어야만 1의 결과를 얻고, 그 외에는 0을 얻는다.
^ (XOR 연산자) = 피연산자의 값이 서로 다를 때만 1의 결과를 얻고, 같으면 0을 얻는다.
x y x | y x & y x ^ y
1 1 1 1 0
1 0 1 0 1
0 1 1 0 1
0 0 0 0 0

 

 

비트 전환 연산자 ~ ( '1의 보수' 연산자 )

피연산자를 2진수로 표현했을 때, 0은 1로, 1은 0으로 바꿉니다.

x ~x
1 0
0 1

 

비트 전환 연산자 ~ 에 의해 비트 전환 되고 나면, 부호있는 타입의 피연산자는 부호가 반대로 변경됩니다.

즉, 피연산자의 '1의 보수'를 얻을 수 있습니다.

 

2진수 10진수
00001010 10
11110101 -11
11110101 + 00000001 = 11110110 (2의 보수) -11 + 1 = -10 ( 2의 보수 )

1의 보수에 + 1 을 하면 2의 보수를 얻을 수 있습니다.

 

 

쉬프트 연산자 <<   >>

피연산자의 각 자리(2진수로 표현했을 때)를 오른쪽 ( >> ) , 또는 왼쪽 ( << ) 으로 이동(shift) 합니다.

예를 들어 8 << 2 는 왼쪽 피연산자인 10진수 8의 2진수를 왼쪽으로 2자리 이동합니다.

이 때, 자리이동으로 저장범위를 벗어난 값들은 버려지고 빈자리는 0으로 채워집니다.

 

1. 10진수 8을 2진수로 변환하면 

0 0 0 0 1 0 0 0

2. 8 << 2 는 왼쪽 피연산자인 10진수 8의 2진수를 왼쪽으로 2자리 이동하고 자리이동으로 저장범위를 벗어난 값들은 버려집니다.

0 0 1 0 0 0    

3.  빈자리는 0으로 채워집니다.

0 0 1 0 0 0 0 0

4. 8 << 2 의 결과는 2진수로 00100000이 됩니다 ( 10진수로 32 )

 

 

<< 연산자의 경우 피연산자의 부호에 상관없이 각 자리를 왼쪽으로 이동시키며 빈칸을 0으로 채우면되지만,

>> 연산자의 경우 오른쪽으로 이동시키기 떄문에 부호있는 정수는 부호를 유지하기 위해 왼쪽 피연산자가 음수인 경우 빈자리를 1로 채웁니다. ( 양수일 땐 0 )

-8 >> 2 
1 1 1 1 1 0 0 0
    1 1 1 1 1 0
1 1 1 1 1 1 1 0
-8 >> 2  는 10진수로 -2가 된다.

 

위 표를 보면 다음과 같다는 결과를 알 수 있다.

x << n은 : x * 2^n의 결과와 같다.
x >> n은 : x / 2^n의 결과와 같다.

 


참고 자료
https://hongong.hanbit.co.kr/%EC%9E%90%EB%B0%94-%EA%B8%B0%EC%B4%88-%EC%97%B0%EC%82%B0%EC%9E%90-%EC%97%B0%EC%82%B0%EC%9D%98-%EB%B0%A9%ED%96%A5%EA%B3%BC-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84/