[Java 기초] 4. 자바의 연산자(Operator)와 Type 변환

Kyung Jae, Cheong·2024년 8월 11일
1
post-thumbnail
  • 최대한 기초가 거의 없는 분들의 입장에서 정리해본 내용이므로, 어려운 개념들은 가급적 포함시키지 않거나 최대한 간단하게만 표현하였습니다.
  • Java 세팅 및 실습은 Windows 환경에서 IntelliJ를 통해 진행되었습니다.
  • (참고) IntelliJ 코드 실행 단축키
    • Windows, Linux : Shift+F10
    • MacOS : Ctrl+R

4. 자바의 연산자(Operator)와 Type 변환

4-1. 연산자의 우선순위와 종류

  • 자바에서는 기본적으로 제공되는 다양한 기능의 연산자들이 있습니다. 주로 특수문자들을 이용해 이를 활용하게됩니다.
  • 모든 연산자를 외울 필요까지는 없지만, 많이 알아둘수록 상황에 맞게 훨씬 효율적인 코딩을 수행할 수 있습니다.
  • 연산자의 종류 (순서대로 우선순위가 높습니다)
    1. 소괄호 : ()
    2. 단항연산자 : ++ -- ! ~ new (type)
    3. 산술연산자 : * / % -> + -
    4. Shift연산자 : << >> >>>
    5. 비교연산자 : < <= > >= instanceof
    6. 등식연산자 : == !=
    7. 비트연산자 : & ^
    8. 논리연산자 : && ||
    9. 삼항연산자 : ? :
    10. 대입연산자 : = += -= *= /= %=
  • 이 모든걸 외우실 필요는 없고 필요할때 찾아보며 쓰시면 됩니다.
  • 이후엔 연산자들을 항목별로 다시 분류하여 정리해보도록 하겠습니다.

0. 소괄호

  • 소괄호는 가장 우선순위가 높은 연산자입니다. 수학에서의 괄호와 똑같은 역할이라 보시면 됩니다. 그래서 코드의 연산식이 길어지고 복잡해지면 괄호를 통해 이를 묶어서 관리하는 것이 가독성도 높이고 유지보수하기에도 유리합니다.
  • 중요한건 왠만하면 괄호를 이용하자라는 것이고 이렇게 하면 연산자 우선 순위를 달달 외울 필요없이 사용하실 수 있으실 것이라 생각합니다.
  • 사용 예시 : 2 + 3 * 4 - 12 / 3 -> 2 + (3 * 4) - (12 / 3)

1. 산술 연산자 (Arithmetic Operators)

  • 산술 연산자는 기본적인 산술 연산을 수행하는 데 사용됩니다.
연산자설명예시결과
+덧셈a + ba와 b의 합
-뺄셈a - ba에서 b를 뺌
*곱셈a * ba와 b의 곱
/나눗셈a / ba를 b로 나눈 몫(정수), 결과(실수)
%나머지a % ba를 b로 나눈 나머지
++증가 (1 증가)a++ 또는 ++aa를 1 증가시킴
--감소 (1 감소)a-- 또는 --aa를 1 감소시킴
  • 나눗셈의 경우 지정한 데이터 타입에 따라 결과가 달라집니다. 다음 예시를 통해 확인해보겠습니다.
    • 정수형끼리의 나눗셈 : 몫을 반환합니다.
    • 실수형끼리의 나눗셈 : 나눗셈 결과를 반환합니다.

Arithmetic1.java

package operators;

public class Arithmetic1 {
    public static void main(String[] args) {
        // 1. 정수형 나눗셈
        int a = 10;
        int b = 3;
        System.out.println("a / b = " + a / b); // 3 (몫)

        // 2. 실수형 나눗셈
        double c = 10.0;
        double d = 3.0;
        System.out.println("c / d = " + c / d); // 3.33333...
    }
}

Arithmetic1.class

a / b = 3
c / d = 3.3333333333333335
  • ++--는 증감연산자라 하여 값을 1씩 증가시키거나 감소시키는 명령어입니다. 그런데 사용 위치에따라 동작하는 방식의 차이가 있습니다.
    • 변수 앞에 위치 (++a; 전위 증감연산자)
      • 앞에 위치하는 경우엔 증감연산자가 먼저 실행되어 변수 값이 바뀐 뒤에 다른 연산이 이뤄집니다.
    • 변수 뒤에 위치 (a++; 후위 증감연산자)
      • 뒤에 위치하는 경우엔 다른 여산이 먼저 이뤄진 후에 증감연산자가 실행되어 변수 값이 바뀝니다.
    • 예시를 통해 살펴보겠습니다.

Arithmetic2.java

package operators;

public class Arithmetic2 {
    public static void main(String[] args) {
        // 1. 전위 증감연산자
        int a = 10;
        int b = 5;
        System.out.println("a = " + a + ", b = " + b);
        int result = ++a * b;
        System.out.println("++a * b = " + result); // 55
        System.out.println("a = " + a + ", b = " + b); // a = 11, b = 5

        System.out.println("-----------------------");

        // 2. 후위 증감연산자
        int c = 10;
        int d = 5;
        System.out.println("c = " + c + ", d = " + d);
        int result2 = c++ * d;
        System.out.println("c++ * d = " + result2); // 50
        System.out.println("c = " + c + ", d = " + d); // c = 11, d = 5

        System.out.println("-----------------------");
    }
}

Arithmetic2.class

a = 10, b = 5
++a * b = 55
a = 11, b = 5
-----------------------
c = 10, d = 5
c++ * d = 50
c = 11, d = 5
-----------------------
  • 위 코드 결과처럼 연산 전후의 변수들은 하나의 변수 값이 1 증가하여 같게 나오지만, 연산 결과에서 차이가 발생했습니다. 그래서 다음과 같이 정리할 수 있습니다.
    • 앞쪽에 ++--가 붙으면 변수를 먼저 바꾸고 연산을 수행한다.
    • 뒤쪽에 ++--가 붙으면 연산을 먼저 수행하고 변수를 바꾼다.

2. 비교 연산자 (Relational Operators)

  • 비교 연산자는 두 값을 비교하여 참(true) 또는 거짓(false)을 반환합니다.
연산자설명예시결과
==같음a == ba가 b와 같으면 true
!=같지 않음a != ba가 b와 다르면 true
>a > ba가 b보다 크면 true
<작음a < ba가 b보다 작으면 true
>=크거나 같음a >= ba가 b보다 크거나 같으면 true
<=작거나 같음a <= ba가 b보다 작거나 같으면 true
  • 여기서 주의해야할 점은 ==!=에서 숫자형은 문제가 없으나, 문자열을 비교하는 경우에는 "문자열1".equals("문자열2")의 형태로 비교해야한다는 것입니다.
    • 이는 참조형 변수에서 ==!=의 경우 값을 비교하는 것이 아니라 메모리의 참조값을 비교하기때문에 나타나는 현상인데, 자세한건 지금 알려고 하실 필요는 없습니다.
  • 예시 코드를 통해 살펴보도록 하겠습니다.

Relational.java

package operators;

public class Relational {
    public static void main(String[] args) {
        // 1. 비교 연산자
        int a = 10;
        int b = 20;
        int c = 10;

        System.out.println("a == b : " + (a == b)); // false
        System.out.println("a == c : " + (a == c)); // true
        System.out.println("a != b : " + (a != b)); // true
        System.out.println("a != c : " + (a != c)); // false
        System.out.println("a > b : " + (a > b)); // false
        System.out.println("a < b : " + (a < b)); // true
        System.out.println("a >= b : " + (a >= b)); // false
        System.out.println("a <= b : " + (a <= b)); // true

        System.out.println("-----------------------");

        // 2. 문자열 비교
        String str1 = "hello";
        String str2 = "hello";
        String str3 = new String("hello");

        System.out.println("str1 == str2 : " + (str1 == str2)); // true
        System.out.println("str1 == str3 : " + (str1 == str3)); // false
        System.out.println("str1.equals(str3) : " + str1.equals(str3)); // true
    }
}

Relational.class

a == b : false
a == c : true
a != b : true
a != c : false
a > b : false
a < b : true
a >= b : false
a <= b : true
-----------------------
str1 == str2 : true
str1 == str3 : false
str1.equals(str3) : true

3. 논리 연산자 (Logical Operators)

  • 논리 연산자는 불리언(true/false) 값을 조합하거나 반전시킵니다.
연산자설명예시결과
&&논리 ANDa && ba와 b가 모두 참이면 true
||논리 ORa || ba혹은 b가 참이면 true
!논리 NOT!aa가 참이면 false, 거짓이면 true
  • 비교 연산자와 함께 쓸때는 가능한 소괄호()를 붙여서 해주는 것이 가독성과 우선순위 설정에 좋습니다.

Logical.java

package operators;

public class Logical {
    public static void main(String[] args) {
        // 1. 논리 연산자
        boolean a = true;
        boolean b = false;

        System.out.println("a && b : " + (a && b)); // false
        System.out.println("a || b : " + (a || b)); // true
        System.out.println("!a : " + !a); // false
        System.out.println("!b : " + !b); // true

        System.out.println("-----------------------");

        // 2. 논리 연산자와 비교 연산자
        int c = 10;
        int d = -10;

        System.out.println("(c > 0) && (d > 0) : " + ((c > 0) && (d > 0))); // false
        System.out.println("(c > 0) || (d > 0) : " + ((c > 0) || (d > 0))); // true
    }
}

Logical.class

a && b : false
a || b : true
!a : false
!b : true
-----------------------
(c > 0) && (d > 0) : false
(c > 0) || (d > 0) : true

4. 비트 연산자 (Bitwise Operators)

  • 비트 연산자는 주로 정수형의 비트 단위 연산을 수행합니다.
    • 이걸 어디에 쓰죠?
      • 일반적으로는 비트 연산을 수행하는 경우가 거의 없는 것은 사실이긴 합니다.
      • 다만, 만약 하드웨어 기반의 소프트웨어를 개발하는 경우에는 상당히 중요하게 쓰입니다.
    • 이진법으로 치환한 비트단위의 정수형을 비교하거나 변환할때 주로 쓰입니다.
      • 필요한 경우가 아니라면 이런게 있구나 정도로 넘어가시면 됩니다.
연산자설명예시결과
&비트 ANDa & ba와 b의 비트가 모두 1이면 1
|비트 ORa | ba와 b의 비트가 하나라도 1이면 1
^비트 XORa ^ ba와 b의 비트가 서로 다르면 1
~비트 NOT~aa의 비트를 반전
<<왼쪽 시프트a << 2a의 비트를 2만큼 왼쪽으로 시프트
>>오른쪽 시프트a >> 2a의 비트를 2만큼 오른쪽으로 시프트
>>>오른쪽 시프트 (부호 없음)a >>> 2a의 비트를 2만큼 오른쪽으로 시프트(부호 없음)

Bitwise.java

package operators;

public class Bitwise {
    public static void main(String[] args) {
        // 1. 비트 논리 연산자
        int a = 200; // 11001000
        int b = 127; // 01111111

        System.out.println("a & b : " + (a & b) + "(" + Integer.toBinaryString(a & b) + ")"); // 72(1001000)
        System.out.println("a | b : " + (a | b) + "(" + Integer.toBinaryString(a | b) + ")"); // 255(11111111)
        System.out.println("a ^ b : " + (a ^ b) + "(" + Integer.toBinaryString(a ^ b) + ")"); // 183(10110111)

        System.out.println("-----------------------");

        // 2. 비트 반전 & 이동 연산자
        int c = -77; // 10110011
        System.out.println("~c : " + (~c) + "(" + Integer.toBinaryString(~c) + ")"); // 76(1001100)
        System.out.println("c << 2 : " + (c << 2) + "(" + Integer.toBinaryString(c << 2) + ")"); // -308(11001100)
        System.out.println("c >> 2 : " + (c >> 2) + "(" + Integer.toBinaryString(c >> 2) + ")"); // -20(11101100)
        System.out.println("c >>> 2 : " + (c >>> 2) + "(" + Integer.toBinaryString(c >>> 2) + ")"); // 1073741804(11111111111111111111111111001100)

    }
}

Bitwise.class

a & b : 72(1001000)
a | b : 255(11111111)
a ^ b : 183(10110111)
-----------------------
~c : 76(1001100)
c << 2 : -308(11111111111111111111111011001100)
c >> 2 : -20(11111111111111111111111111101100)
c >>> 2 : 1073741804(111111111111111111111111101100)
  • & 연산자가 쓰이는 예시 : 8비트 데이터가 저장되어야하는데 9비트 이상의 데이터가 들어온 경우 이를 다시 8비트로 제한할때 (a & 255 -> a & 11111111)
    • 이런 경우도 있구나 정도로 넘어가시길 !

5. 할당 연산자 (Assignment Operators)

  • 할당 연산자는 값을 변수에 할당하는 데 사용됩니다.
    • 복합 할당 연산자는 산술 연산자와 결합되어 사용되는 경우를 말합니다.
연산자설명예시결과
=할당a = bb의 값을 a에 할당
+=더한 후 할당a += ba에 b를 더한 값을 a에 할당
-=뺀 후 할당a -= ba에서 b를 뺀 값을 a에 할당
*=곱한 후 할당a *= ba에 b를 곱한 값을 a에 할당
/=나눈 후 할당a /= ba를 b로 나눈 값을 a에 할당
%=나머지를 구한 후 할당a %= ba를 b로 나눈 나머지를 a에 할당
  • 여기서 중요하게 알아둘 개념은 자바는 항상 변수의 값을 복사해서 할당한다는 원칙입니다.
    • 즉, 변수에 다른 변수를 할당하는 경우에는 기존의 변수 값이 변하지 않는다는 것입니다. 이는 기본형 변수나 참조형 변수나 마찬가지입니다.
      - 다만, 기본형 변수와 참조형 변수의 차이가 있는데, 기본형 변수는 값을 직접 복사해서 할당하는 반면, 참조형 변수는 메모리 주소값인 참조값을 복사해서 할당하게 됩니다. 자세한 내용은 초급편에서 다시 다룰 예정입니다.
    • python으로 치면 기본형 변수에선 deepcopy가 기본적으로 이뤄지는 것이라 보면 됩니다.
  • 예시를 통해서 살펴보도록 하겠습니다.

Assignment.java

package operators;

public class Assignment {
    public static void main(String[] args) {
        // 1. 할당 연산자
        int a = 10;
        System.out.println("a = " + a); // 10

        int b = a;
        int c = 2 * b;

        System.out.println("a = " + a); // 10
        System.out.println("b = " + b); // 10
        System.out.println("c = " + c); // 20

        System.out.println("-----------------------");

        // 2. 복합 대입 연산자
        int d = 10;
        System.out.println("d = " + d); // 10
        System.out.println("d += 5 : " + (d += 5)); // 15
        System.out.println("d -= 5 : " + (d -= 5)); // 10
        System.out.println("d *= 5 : " + (d *= 5)); // 50
        System.out.println("d /= 4 : " + (d /= 4)); // 12
        System.out.println("d %= 7 : " + (d %= 7)); // 5
        System.out.println("d = " + d); // 5
    }
}

Assignment.class

a = 10
a = 10
b = 10
c = 20
-----------------------
d = 10
d += 5 : 15
d -= 5 : 10
d *= 5 : 50
d /= 4 : 12
d %= 7 : 5
d = 5
  • 위 예시처럼 b에 a값을 할당하고 b를 변경해주어도 a값에는 변화가 없습니다.
    • 이는 자바는 항상 변수의 값을 복사해서 할당한다는 원칙에 의한 것입니다.
  • 산술 연산자와 결합한 복합 할당 연산자는 다음과 같은 코드를 축약한 것이라 볼 수 있습니다.
    • d = d + 5 -> d += 5

6. 삼항 연산자 (Ternary Operator)

  • 삼항 연산자는 간단한 조건문을 표현할 때 사용됩니다.
    • 유일하게 3개의 항을 가지는 연산자라해서 삼항 연산자라 부릅니다.
    • python을 배우신 분이라면 numpy.where()와 같은 표현방식으로 이해하시면 됩니다.
연산자설명예시결과
? :조건 ? 참일 때의 값 : 거짓일 때의 값int result = (a > b) ? a : b;a가 b보다 크면 a, 그렇지 않으면 b
  • 이는 조건문을 다룰때 다시 다루겠지만, 조건이 boolean(true/false)인 경우에 유용하게 쓰일 수 있는 연산자입니다.

7. 기타 연산자 (Others)

  • 지금 단계에서는 이해하기 어려운 연산자들이라 이런게 있구나 넘어가시면 됩니다. 이후에 다시 다룰 예정입니다.
연산자설명예시결과
instanceof객체가 특정 클래스의 인스턴스인지 확인if (obj instanceof String)obj가 String의 인스턴스이면 true
new객체를 생성MyClass obj = new MyClass();MyClass의 새로운 인스턴스를 생성
.멤버 접근obj.method()obj의 method 메서드를 호출
[]배열 요소 접근arr[0]배열 arr의 첫 번째 요소를 반환
  • 예시 (instanceof)
String name = "Alice";
boolean isString = name instanceof String;  // isString은 true

4-2. 형변환(Type Casting)

  • 말 그대로 형태(Type)을 변환시킨다는 뜻입니다.
  • 이때 형변환은 크게 두가지 형태로 구분됩니다.
    • 자동 형변환 (혹은 묵시적 형변환) : 개발자가 직접 변환하지 않아도 작은 범위에서 큰 범위로 자동으로 변환
    • 강제 형변환 (혹은 명기적 형변환) : 개발자가 직접 변환해주어야하고, 그렇지 않으면 오류가 발생

자동 형변환

  • 자동 형변환은 작은 범위에서 큰 범위로 자바 내부적으로 자동으로 변환되어 실행 됩니다.
  • 이때 다음과 같은 순서로 형 변환이 일어납니다. (괄호는 메모리 byte 수를 의미합니다)
    • byte(1) -> short(2) -> char(2) -> int(4) -> long(8) -> float(4) -> double(8)
  • 전반적으로는 메모리 크기가 작은 경우에서 큰 경우로 순서가 정해지는 것을 알 수 있는데, 한가지 예외사항이 있습니다. 바로 long(8) -> float(4) 부분입니다.
    • 이는 정수형과 실수형의 표현 방식의 차이에서 비롯된 것인데, 어째뜬 표현할 수 있는 숫자의 범위는 다음과 같습니다.
      • long : -2^63 ~ 2^63-1
      • float : -3.4E38 ~ 3.4E38
    • 메모리가 아닌 숫자의 표현 범위가 훨씬 넓은 float가 long보다는 더 많은 데이터를 표현할 수 있습니다. 그래서 자동 형 변환은 long에서 float로 이뤄집니다.

강제 형변환

  • 강제 형변환은 자동 형변환이 안되어 오류가 발생하는 경우나, 정수의 나눗셈을 몫이 아닌 실수 값으로 표현하고자 할때와 같이 다양한 상황에서 필요하게 됩니다.
    • 즉, 메모리 크기나 범위가 큰 경우에서 작은 경우로 변환하는 경우에 강제 형변환이 필요합니다.
  • 강제로 형변환을 하는 방법은 해당 변수의 앞쪽에 소괄호()로 변환 시킬 타입을 명시하여 실시할 수 있습니다.
  • 다만 주의할 점은 작은 숫자의 범위를 초과하는 경우엔 오버플로우(overflow)라는 문제가 발생할 수 있습니다.
  • 예시를 통해 살펴보겠습니다.

Casting.java

package operators;

public class Casting {
    public static void main(String[] args) {
        long l = 2147483648L;
        float f = 3.14F;

        // 1. 자동 형변환
        double d = l + f; // long + float -> float + float -> double + float -> double
        System.out.println("d = " + d); // 2147483651.14

        // 2-1. 강제 형변환 (오버플로우)
        int i = (int)l; // long -> int
        System.out.println("i = " + i); // -2147483648 (오버플로우)

        // 2-2. 강제 형변환 (정확한 값)
        int i2 = (int)f; // float -> int
        System.out.println("i2 = " + i2); // 3
    }
}

Casting.class

d = 2.147483648E9
i = -2147483648
i2 = 3
  • 위 예시처럼 서로 다른 타입의 계산을 수행할때는 큰 범위로 자동 형변환이 일어나며, 강제 형변환을 실시한 경우엔 오버플로우를 조심해야합니다.
    • 하지만 오버플로우가 일어날 것 같다면 변수 타입을 애초에 큰 범위로 바꾸면 문제를 예방할 수 있긴 합니다.

마무리

  • 본 포스팅에서는 Java에서 주로 쓰이는 연산자에 대해 알아보았고, 변수의 Type을 변환하는 방식에 대해서 다루어 보았습니다.
  • 이후엔 배열에 대해서 간단히 다루어보고, 기본 단계에서 아주 중요한 문법인 조건문과 반복문에 대해서 다룰 예정입니다!
profile
일 때문에 포스팅은 잠시 쉬어요 ㅠ 바쁘다 바빠 모두들 화이팅! // Machine Learning (AI) Engineer & BackEnd Engineer (Entry)

1개의 댓글

comment-user-thumbnail
2025년 2월 19일

잘 봤습니다.

답글 달기