본문 바로가기
Java Tutorials

#3 Lesson: Language Basics - Operators

by xogns93 2024. 7. 18.

Operators

변수를 선언하고 초기화하는 방법을 배웠으니 이제 그 변수들을 활용하는 방법에 대해 알고 싶을 것입니다. 자바 프로그래밍 언어의 연산자를 학습하는 것은 좋은 시작입니다. 연산자는 특정 작업을 수행하는 특수 기호로, 하나, 둘 또는 셋의 피연산자에 대해 작업을 수행한 다음 결과를 반환합니다.

자바 프로그래밍 언어의 연산자를 탐색하는 동안 연산자의 우선순위가 높은 것부터 미리 알고 있으면 도움이 될 수 있습니다. 다음 표에 나열된 연산자는 우선순위 순서대로 나열되어 있습니다. 표의 맨 위에 나타나는 연산자일수록 우선순위가 높습니다. 우선순위가 높은 연산자가 상대적으로 우선순위가 낮은 연산자보다 먼저 평가됩니다. 동일한 라인에 있는 연산자는 우선순위가 동일합니다. 동일한 우선순위의 연산자가 동일한 표현식에 나타날 때 어떤 것이 먼저 평가될지를 결정하는 규칙이 필요합니다. 할당 연산자를 제외한 모든 이항 연산자는 왼쪽에서 오른쪽으로 평가됩니다. 할당 연산자는 오른쪽에서 왼쪽으로 평가됩니다.

 

Operator Precedence

Operators Precedence
postfix expr++ expr--
unary ++expr --expr +expr -expr ~ !
multiplicative * / %
additive + -
shift << >> >>>
relational < > <= >= instanceof
equality == !=
bitwise AND &
bitwise exclusive OR ^
bitwise inclusive OR |
logical AND &&
logical OR ||
ternary ? :
assignment = += -= *= /= %= &= ^= |= <<= >>= >>>=

 

※ Postfix(후위 증가 연산자)

자바에서 postfix라는 용어는 보통 후위 연산자(Postfix Operator)를 의미합니다. 후위 연산자는 변수의 값에 대한 연산이 먼저 수행된 후에 그 값을 증가시키거나 감소시키는 연산자를 말합니다. 자바에서 대표적인 후위 연산자는 후위 증가 연산자(x++)와 후위 감소 연산자(x--)입니다.

후위 증가 연산자 (x++)

 이 연산자는 변수를 먼저 사용한 후에 그 값을 1 증가시킵니다.
 예를 들어, int x = 5; int y = x++; 이 코드에서 y의 값은 5가 되고, x의 값은 6이 됩니다.

 

후위 감소 연산자 (x--)

 이 연산자는 변수를 먼저 사용한 후에 그 값을 1 감소시킵니다.
⦁ 예를 들어, int x = 5; int y = x--; 이 코드에서 y의 값은 5가 되고, x의 값은 4가 됩니다.

 

예제

public class PostfixExample {
    public static void main(String[] args) {
        int a = 10;
        int b = a++;  // b는 10이 되고, a는 11이 됩니다.
        
        int c = 10;
        int d = c--;  // d는 10이 되고, c는 9가 됩니다.
        
        System.out.println("a: " + a + ", b: " + b);
        System.out.println("c: " + c + ", d: " + d);
    }
}


이 예제에서, a++ c--는 각각 a c의 값을 먼저 할당한 후에 1씩 증가하거나 감소시킵니다. 따라서 출력 결과는 다음과 같습니다:

a: 11, b: 10
c: 9, d: 10

 

후위 연산자는 변수의 현재 값을 사용하고 난 후에 변경하기 때문에, 다른 연산자나 명령문과 함께 사용될 때 그 동작을 잘 이해하고 있어야 합니다.

 

※ 단항 연산자

단항 연산자(Unary Operator)는 하나의 피연산자에 대해 작용하는 연산자입니다. 이 연산자는 단일 피연산자에 대해 수행되는 작업을 정의하며, 피연산자 하나를 가지고 단일 연산을 수행합니다. 예를 들어, -expr에서 -는 단항 연산자입니다. 이것은 피연산자인 expr의 부호를 반전시킵니다. 다른 예로는 !expr에서 !는 논리 부정 연산자로, 피연산자 expr의 논리값을 반대로 만듭니다. 단항 연산자는 하나의 피연산자만을 요구하며, 대부분은 피연산자의 값을 변경하거나 변환하는 데 사용됩니다.

 

※ 이항 연산자

이항 연산자(Binary Operator)는 두 개의 피연산자를 필요로 하는 연산자입니다. 이 피연산자들은 연산의 좌항과 우항에 위치하며, 이항 연산자가 이들을 조작하여 새로운 값을 생성합니다.
예를 들어, 산술 연산자인 +는 두 개의 숫자를 더하는 데 사용됩니다. 여기서 5와 3은 이항 연산자 +의 두 피연산자입니다.

또 다른 예로, 비교 연산자인 ==는 두 개의 값을 비교합니다. 아래의 코드에서는 a와 b의 값이 같은지 비교하고 있습니다.

int a = 5;
int b = 3;
boolean isEqual = (a == b);

이항 연산자는 다양한 작업에 사용될 수 있으며, 두 개의 피연산자를 조작하여 새로운 값을 생성하거나 비교하는 데 사용됩니다.

 

Assignment, Arithmetic, and Unary Operators

The Simple Assignment Operator

가장 일반적으로 만나게 될 연산자 중 하나는 간단한 할당 연산자 "="입니다. 이 연산자는 Bicycle 클래스에서 보았습니다. 이 연산자는 오른쪽의 값을 왼쪽의 피연산자에 할당합니다.

int cadence = 0;
int speed = 0;
int gear = 1;


이 연산자는 또한 객체에 대한 객체 참조를 할당하는 데에도 사용될 수 있습니다. 이에 대해서는 객체 생성에서 논의했습니다.

 

The Arithmetic Operators

Java 프로그래밍 언어는 덧셈, 뺄셈, 곱셈 및 나눗셈을 수행하는 연산자를 제공합니다. 기본 수학에서의 대응되는 연산자들을 보면서 익숙할 것입니다. 새로 보일 수 있는 유일한 기호는 "%"입니다. 이 연산자는 하나의 피연산자를 다른 피연산자로 나누고 나머지를 결과값으로 반환합니다.


Operator Description
+ Additive operator (also used for String concatenation)
- Subtraction operator
* Multiplication operator
/ Division operator
% Remainder operator

아래 ArithmeticDemo 프로그램은, 산술 연산을 테스트합니다.

class ArithmeticDemo {

    public static void main (String[] args) {

        int result = 1 + 2;
        // result is now 3
        System.out.println("1 + 2 = " + result);
        int original_result = result;

        result = result - 1;
        // result is now 2
        System.out.println(original_result + " - 1 = " + result);
        original_result = result;

        result = result * 2;
        // result is now 4
        System.out.println(original_result + " * 2 = " + result);
        original_result = result;

        result = result / 2;
        // result is now 2
        System.out.println(original_result + " / 2 = " + result);
        original_result = result;

        result = result + 8;
        // result is now 10
        System.out.println(original_result + " + 8 = " + result);
        original_result = result;

        result = result % 7;
        // result is now 3
        System.out.println(original_result + " % 7 = " + result);
    }
}

 

이 프로그램은 다음과 같은 출력을 콘솔에 출력합니다.

1 + 2 = 3
3 - 1 = 2
2 * 2 = 4
4 / 2 = 2
2 + 8 = 10
10 % 7 = 3

 

 산술 연산자를 단순 할당 연산자와 결합하여 복합 할당을 만들 수도 있습니다. 예를 들어, x+=1; 및 x=x+1;은 모두 x의 값을 1씩 증가시킵니다.

또한, + 연산자는 두 개의 문자열을 연결하는 데에도 사용할 수 있습니다. 다음 ConcatDemo 프로그램에서 보여지는 것처럼요.

class ConcatDemo {
    public static void main(String[] args){
        String firstString = "This is";
        String secondString = " a concatenated string.";
        String thirdString = firstString+secondString;
        System.out.println(thirdString);
    }
}

이 프로그램의 마지막 코드 라인의 변수 thirdString에는 "This is a concatenated string."이 포함되어 있습니다. 이는 표준 출력에 프린트됩니다.

 

The Unary Operators

단항 연산자는 오직 하나의 피연산자만 필요로 합니다. 이들은 값에 1을 증가시키거나 감소시키는, 표현식을 부정하는, 혹은 불리언 값의 반전을 하는 등 다양한 작업을 수행합니다.

 

Operator Description
+ Unary plus operator; indicates positive value (numbers are positive without this, however)
- Unary minus operator; negates an expression
++ Increment operator; increments a value by 1
-- Decrement operator; decrements a value by 1
! Logical complement operator; inverts the value of a boolean

다음 프로그램, UnaryDemo, 은 unary operators을 테스트합니다:

class UnaryDemo {

    public static void main(String[] args) {

        int result = +1;
        // result is now 1
        System.out.println(result);

        result--;
        // result is now 0
        System.out.println(result);

        result++;
        // result is now 1
        System.out.println(result);

        result = -result;
        // result is now -1
        System.out.println(result);

        boolean success = false;
        // false
        System.out.println(success);
        // true
        System.out.println(!success);
    }
}

 

증가/감소 연산자는 피연산자 앞에 (prefix) 또는 뒤에 (postfix) 적용할 수 있습니다. result++; ++result; 코드 모두 result가 1씩 증가하는 결과를 낼 것입니다. 유일한 차이는 prefix 버전 (++result)은 증가된 값을 평가하는 반면, poxtfix 버전 (result++)은 원래 값으로 평가됩니다. 단순한 증가/감소만 수행하는 경우에는 어떤 버전을 선택하든 상관 없습니다. 그러나 더 큰 표현식의 일부에 이 연산자를 사용하면 선택한 연산자에 따라 상당한 차이가 발생할 수 있습니다.

다음 프로그램인 PrePostDemo는 prefix/postfix unary 증가 연산자를 설명합니다.

class PrePostDemo {
    public static void main(String[] args){
        int i = 3;
        i++;
        // prints 4
        System.out.println(i);
        ++i;			   
        // prints 5
        System.out.println(i);
        // prints 6
        System.out.println(++i);
        // prints 6
        System.out.println(i++);
        // prints 7
        System.out.println(i);
    }
}

 

 

Equality, Relational, and Conditional Operators

The Equality and Relational Operators

등호 및 관계 연산자는 한 피연산자가 다른 피연산자보다 큰지, 작은지, 같은지 또는 같지 않은지를 판단합니다. 이러한 연산자 대부분은 아마도 익숙하게 보일 것입니다. 두 개의 기본 값이 동일한지 테스트할 때는 "==", "=" 대신에 사용해야 함을 명심하세요.

 

==      equal to
!=      not equal to
>       greater than
>=      greater than or equal to
<       less than
<=      less than or equal to
다음 프로그램,  ComparisonDemo, 비교 연산자들을 테스트합니다:
class ComparisonDemo {

    public static void main(String[] args){
        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

 

The Conditional Operators

&& || 연산자는 두 개의 부울 표현식에 대해 Conditional-AND 및 Conditional-OR 연산을 수행합니다. 이 연산자들은 "단축 평가(short-circuiting)" 동작을 나타내며, 이는 두 번째 피연산자가 필요한 경우에만 평가된다는 것을 의미합니다.

"Short-circuiting"은 조건식을 평가하는 동안, 전체 표현식의 결과를 빠르게 결정하기 위해 연산자가 추가적인 피연산자를 평가하지 않고 중단하는 것을 의미합니다. 
예를 들어, 조건-AND 연산자 "&&"에서 첫 번째 피연산자가 false로 평가된다면, 두 번째 피연산자를 평가하지 않고 전체 표현식을 false로 결정합니다. 비슷하게, 조건-OR 연산자 "||"에서 첫 번째 피연산자가 true로 평가된다면, 두 번째 피연산자를 평가하지 않고 전체 표현식을 true로 결정합니다.
이러한 동작은 불필요한 연산을 줄이고 실행 시간을 최적화하는 데 도움이 됩니다.

 

&& Conditional-AND
|| Conditional-OR

 

다음 프로그램, ConditionalDemo1, 이러한 연산자들을 테스트합니다:

class ConditionalDemo1 {

    public static void main(String[] args){
        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");
    }
}

 

다른 조건부 연산자는 ?: 입니다. 이는 if-then-else 문의 축약형으로 생각할 수 있습니다 (이 강좌의 Control Flow Statements 섹션에서 설명됩니다). 이 연산자는 세 개의 피연산자를 사용하므로 삼항[ternary] 연산자라고도 합니다. 다음 예제에서 이 연산자는 다음과 같이 해석되어야 합니다: "만약 someCondition이 true이면, value1의 값을 result에 할당합니다. 그렇지 않으면, value2의 값을 result에 할당합니다."

다음 프로그램, ConditionalDemo2, ?: 연산자를 테스트합니다:

class ConditionalDemo2 {

    public static void main(String[] args){
        int value1 = 1;
        int value2 = 2;
        int result;
        boolean someCondition = true;
        result = someCondition ? value1 : value2;

        System.out.println(result);
    }
}

 

someCondition이 true이므로, 이 프로그램은 콘솔 화면에 "1"을 출력합니다. 코드를 더 읽기 쉽게 만들기 위해 if-then-else 문 대신 ?: 연산자를 사용하세요. 예를 들어, 표현식이 간결하고 부작용(예: 할당)이 없는 경우에 사용합니다.

부작용이라는 용어는 변수나 상태에 대한 예측 및 관리를 어렵게 만드는 일반적인 프로그래밍 개념입니다. 예를 들어, 코드의 어떤 부분이 실행될 때 변수의 값이 변경되면, 그것은 부작용입니다. 왜냐하면 이러한 변경으로 인해 코드의 다른 부분이 예상과 다르게 동작할 수 있습니다. 코드에서 특정 변수의 값을 조건에 따라 변경한다는 것은 이 변수의 값이 프로그램 실행 중에 변할 수 있다는 것을 의미합니다. 이는 코드를 이해하고 디버그하기 어렵게 만들 수 있습니다. 특히 프로그램이 복잡해지고 큰 규모가 되면서, 어떤 부분에서 변수의 값을 변경하는지 추적하기 어려워집니다. 그러나 부작용이 항상 나쁘다고는 할 수 없습니다. 때로는 부작용이 필요한 경우도 있습니다. 예를 들어, 변수에 값을 할당하는 것은 많은 프로그래밍 작업에서 필수적입니다. 그러나 부작용이 발생할 때는 그에 따른 부작용이 코드의 다른 부분에 미치는 영향을 고려해야 합니다.

 

할당 연산자의 부작용을 이해하는 한 가지 방법은 변수의 값을 추적하는 것입니다.

int x = 5;
int result;

// if-then-else 문 사용
if (x > 0) {
    result = 1;
} else {
    result = 0;
}

System.out.println(result);


위의 코드에서, result의 값을 추적해보면:
1. result의 초기값은 알 수 없습니다.
2. x > 0 조건이 참이라면, result에 1이 할당됩니다.
3. x > 0 조건이 거짓이라면, result에 0이 할당됩니다.

즉, result의 값은 조건에 따라 변경됩니다. 이것이 할당 연산자의 부작용입니다. 이전에는 result의 값을 확실히 알 수 없었지만, 조건문이 실행된 후에는 result의 값이 정해집니다.

이와 달리, 삼항 연산자를 사용하면 부작용이 없는 코드를 작성할 수 있습니다.

int x = 5;
int result = (x > 0) ? 1 : 0;

System.out.println(result);


위의 코드에서는 삼항 연산자를 사용하여 result의 값을 설정합니다. 조건에 따라 새로운 값을 할당하므로 result의 값이 변경되는 것이 아니라, 새로운 값을 얻게 됩니다. 따라서 부작용이 발생하지 않습니다.

 

 

 

The Type Comparison Operator instanceof

instanceof 연산자는 피연산자 객체를 지정된 타입과 비교합니다. 이를 사용하여 객체가 특정 클래스의 인스턴스인지, 하위 클래스의 인스턴스인지 또는 특정 인터페이스를 구현한 클래스의 인스턴스인지 테스트할 수 있습니다.

다음 프로그램, InstanceofDemo는, 부모 클래스(Parent라고 명명된)와 간단한 인터페이스(MyInterface라고 명명된)를 정의하며, 이를 상속받고 인터페이스를 구현하는 자식 클래스(Child라고 명명된)를 정의합니다.

class InstanceofDemo {
    public static void main(String[] args) {

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

class Parent {}
class Child extends Parent implements MyInterface {}
interface MyInterface {}

 

출력:

obj1 instanceof Parent: true
obj1 instanceof Child: false
obj1 instanceof MyInterface: false
obj2 instanceof Parent: true
obj2 instanceof Child: true
obj2 instanceof MyInterface: true

 

instanceof 연산자를 사용할 때 null은 어떠한 클래스의 인스턴스도 아니라는 점을 명심하세요.

 

Bitwise and Bit Shift Operators

자바 프로그래밍 언어는 정수형에 대한 비트 연산과 비트 시프트 연산을 수행하는 연산자도 제공합니다. 이 섹션에서 다루는 연산자들은 그다지 흔하게 사용되지는 않습니다. 따라서 이에 대한 설명은 간단합니다. 목적은 여러분이 이러한 연산자의 존재를 알도록 하는 것입니다.

단항 비트 보수 연산자 "~"는 비트 패턴을 반전시킵니다. 이 연산자는 모든 정수형에 적용될 수 있으며, "0"을 "1"로, "1"을 "0"으로 바꿉니다. 예를 들어, 바이트는 8개의 비트를 포함하고 있습니다. 이 연산자를 "00000000"이라는 비트 패턴의 값에 적용하면 해당 패턴이 "11111111"로 변경됩니다.

부호 있는 좌측 시프트 연산자 "<<"는 비트 패턴을 왼쪽으로 시프트하고, 부호 있는 우측 시프트 연산자 ">>"는 비트 패턴을 오른쪽으로 시프트합니다. 비트 패턴은 좌측 피연산자에 의해 제공되며, 시프트할 위치의 수는 우측 피연산자에 의해 제공됩니다. 부호 없는 우측 시프트 연산자 ">>>"는 왼쪽 최상위 위치에 0을 시프트합니다. 반면 ">>" 후의 최상위 위치는 부호 확장에 따라 달라집니다.

비트 단위 AND 연산을 수행하는 비트 & 연산자가 있습니다.

비트 단위 배타적 OR 연산을 수행하는 비트 ^ 연산자가 있습니다.

비트 단위 포함 OR 연산을 수행하는 비트 | 연산자가 있습니다.

 

The following program, BitDemo, uses the bitwise AND operator to print the number "2" to standard output.

class BitDemo {
    public static void main(String[] args) {
        int bitmask = 0x000F;
        int val = 0x2222;
        // prints "2"
        System.out.println(val & bitmask);
    }
}