티스토리 뷰

JAVA

[Java Study 03] 연산자

DevBee 2021. 4. 13. 20:29

이번에는 Java에서 제공하는 다양한 연산자에 대해 알아보겠습니다. 연산자란, 특정 작업을 수행하기 위한 기호를 의미합니다.

 


산술 연산자

수학적인 계산을 위해 사용되는 연산자를 말합니다.

연산 프로그래밍 일반
더하기 + +
빼기 - -
곱하기 * x
나누기 / ÷
나머지 % (특별한 기호 x)

나누기의 경우 몫을 반환하고 나머지를 구하고 싶다면 나머지 연산자를 사용하여야 합니다.

int a = 5;
int b = 2;

System.out.println(a / b);  // 2
System.out.println(a % b);  // 1

double c = a / b;  // 2.0 - a, b 모두 정수이기 때문에 몫이 2가 나오고 이를 double 타입으로 변환하여 2.0이 저장됩니다.
System.out.println(a / c);  // 2.5 - 피연산자 중 하나라도 실수 타입이면 소수점까지 나옵니다.

 


비트 연산자

데이터를 비트 단위로 연산하기 위한 연산자입니다. 비트 단위 연산이기 때문에 0과 1로 표현이 가능한 정수 타입이나 정수형으로 캐스팅이 가능한 자료형만 비트 연산이 가능합니다.

 

- 비트이동연산자

연산식 설명
x << y 정수 x의 각 비트를 왼쪽으로 y만큼 이동시킵니다. (빈 자리는 0으로 채워집니다.)
x >> y 정수 x의 각 비트를 오른쪽으로 y만큼 이동시킵니다. (빈 자리는 정수 x의 최상위 부호 비트와 같은 값으로 채워집니다.)
x >>> y 정수 x의 각 비트를 오른쪽으로 y만큼 이동시킵니다. (빈 자리는 0으로 채워집니다.)

예를 들어, 2 << 3 의 경우 16 이라는 결과가 나옵니다.

 

- 비트논리연산자

연산자 논리 설명
& AND 두 비트가 모두 1인 경우에만 연산 결과가 1
| OR 두 비트 중 하나만 1이라도 연산 결과가 1
^ XOR 두 비트가 서로 다를 때(하나는 0, 하나는 1)만 연산 결과가 1
~ NOT 비트 반전 (보수)으로 0이면 1, 1이면 0

 


관계 연산자

비교 연산자라고도 하며 부등호를 의미합니다.

연산자 설명
< 왼쪽 항이 작으면 true, 아니면 false
> 왼쪽 항이 크면 true, 아니면 false
<= 왼쪽 항이 작거나 같으면 true, 아니면 false
>= 왼쪽 항이 크거나 같으면 true, 아니면 false
== 왼쪽 항과 오른쪽 항이 같으면 true, 아니면 false
!= 왼쪽 항과 오른쪽 항이 다르면 true, 아니면 false

프로그래밍에서 "같다"를 의미하는 연산자는 ==입니다. =은 대입 연산자로 오른쪽 항을 왼쪽에 저장할 때 사용합니다.

 


논리 연산자

연산자 설명
&& (논리 곱) 두 항이 모두 참인 경우에만 결과 값이 참입니다.
|| (논리 합) 두 항 중 하나만 참이라도 결과 값은 참입니다.
! (부정) 단항 연산자로 참인 경우에는 거짓으로 바꾸고 거짓은 참으로 바꿉니다.

 

논리 연산자를 사용하면 단락회로평가가 가능합니다. 단락회로평가(short circuit evaluation)는 두 항 중에 앞의 항에서 결과 값이 정해지는 경우 뒷 항이 참인지 거짓인지 평가하지 않는 것을 말합니다. 논리 연산자를 사용할 경우 단락회로평가에 의해 예상치 못한 결과가 나올 수 있어 주의해야 합니다.

int num1 = 10;
int num2 = 2;

boolean value1 = ((num1 = num1 + 10)<10) && ((num2 = num2 + 2)<10);

System.out.println(num1); // 20
System.out.println(num2);  // 2  앞 항이 false 이기 때문에 뒷 항을 평가하지 않고 바로 fasle 반환

System.out.println(value1); //false

boolean value2 = ((num1 = num1 + 10)>10) || ((num2 = num2 + 2)<10);

System.out.println(num1); // 30
System.out.println(num2);  // 2  앞 항이 true 이기 때문에 뒷 항을 평가하지 않고 바로 true 반환

System.out.println(value2); //true

 


조건 연산자

조건 연산자는 간단한 조건을 만들어 내는 연산자로 유일한 삼항 연산자입니다. 형태는 조건식 ? 참일 때 결과 : 거짓일 때 결과 입니다.

String lang = "kr";
String greet = "kr".equals(lang) ? "안녕하세요." : "hello";
System.out.println(greet);  // 결과: 안녕하세요.

 


instanceof

instanceof 연산자는 객체가 어떤 클래스인지, 어떤 클래스를 상속받았는지(또는 구현했는지) 확인하는데 사용할 수 있는 연산자입니다. 형태는 오브젝트 instanceof 타입 입니다.

public class Person {
}

````````````````````````````````````

// 위에서 생성한 Person 객체를 상속
public class Student extends Person {
}

````````````````````````````````````

public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        Student student = new Student();
        
        if (person instanceof Person) {
            System.out.println("Person 클래스입니다.");
        }
        
        if (student instanceof Student) {
            System.out.println("Student 클래스입니다.");
        }
        
        if (person instanceof Student) {
            System.out.println("출력 x");
        } else {
            System.out.println("person 변수는 Student 클래스가 아닙니다.");
        }
        
        if (student instanceof Person) {
            System.out.println("student 변수는 Student 클래스이지만 Student 클래스가 Person 클래스의 자식이기 때문에 이 문장은 출력됩니다.");
        }
    }
}

 

모든 클래스들은 Object 클래스를 상속하기 때문에 "오브젝트 instanceof Object"는 항상 true입니다. 오브젝트가 null로 초기화되어 있는 경우에는 항상 false입니다.

 


assignment(=) operator

할당 연산자라고 하며 오른쪽 값을 왼쪽 변수에 할당(저장)할 때 사용합니다. 

// 일반적인 할당 연산자
int a = 5; // a라는 변수에 5라는 값을 할당

// 그 외 할당 연산자
a += 5; // a = a + 5;와 동일
a -= 3; // a = a - 3;과 동일
a *= 2; // a = a * 2;와 동일
a /= 4; // a = a / 4;와 동일
a %= 2; // a = a % 2;와 동일

 


화살표(->) 연산자

자바 8부터 사용 가능하며, 람다 표현식과 함께 사용됩니다. 람다 표현식은 일종의 익명 함수와 같은데, 익명 함수를 이해하기 위해 먼저 익명 클래스부터 살펴보겠습니다.

 

익명 클래스(Anonymous class)는 클래스를 만들지 않고 인터페이스만으로 인스턴스를 생성하는 방법입니다. 익명 클래스는 클로저 기능을 사용할 때 효과적입니다.

클로저란 내부 함수가 외부 함수의 지역 변수를 참조할 수 있다는 것입니다. 아래 예제는 클로저 기능을 사용하는 익명 클래스를 구현한 것입니다. (익명 클래스와 클로저에 대한 설명은 추후 다시 정리하겠습니다.)

interface Test {
    void run();
}

public class Example {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            final int idx = i;  // 익명함수 내에서 사용할 때 변경을 막기 위해 상수로 선언하였습니다.

            Test test = new Test() {
                @Override
                public void run() {
                    // 익명함수 내부에서 외부 함수의 지역변수인 idx에 접근할 수 있습니다.
                    System.out.println("Test Anonymous Class call run - " + idx);
                }
            };

            execute(test);  // 익명함수의 run 메소드 실행
        }
    }

    private static void execute(Test test) {
        test.run();
    }
}

 

익명 클래스 내부에 하나의 메소드만 있는 경우에는 람다식과 화살표 연산자를 통해 구현하면 더 간단합니다. 람다식은 메소드를 하나의 간결한 식으로 표현한 것이며 클래스에 종속되지 않고 메소드를 변수처럼 다룰 수 있습니다.

@FunctionalInterface
interface Test {
    void run();
}

public class Example {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            final int idx = i;  // 익명함수 내에서 사용할 때 변경을 막기 위해 상수로 선언하였습니다.

            // 람다식으로 함수형 인터페이스를 구현할 수 있습니다.
            Test test = () -> System.out.println("Test Anonymous Class call run - " + idx);
            execute(test);  // 매개변수로 람다식 메소드 전달
        }
    }

    private static void execute(Test test) {
        test.run();
    }
}

 


연산자 우선 순위

연산자의 우선순위는 생활코딩의 연산자 우선순위 표를 참고하도록 하겠습니다. 일반적으로 소괄호() 를 주면 우선순위가 가장 높아집니다.

 


Java 13. switch 연산자

Java12부터 switch 연산자가 도입되었습니다. 이전 switch 문법과 다른점을 비교하여 살펴보겠습니다.

// 이전 버전의 switch문
private static void switchOperator() {
    String day = "FRIDAY";

    int happy = 5;

    switch (day) {
        case "MONDAY":
        case "TUESDAY":
            System.out.println("MONDAY or TUESDAY");
            happy = 0;
            break;
        case "FRIDAY":
            System.out.println("FRIDAY");
            happy = 10;
            break;
        default:
            System.out.println("OTHER DAY");
            happy = 5;
    }

    System.out.println(happy);
}

// Java 12 버전의 switch 연산자
private static void switchOperator() {
    String day = "FRIDAY";

    int happy = switch(day) {
        case "MONDAY", "TUESDAY" -> 0;
        case "FRIDAY" -> 10;
        default -> 5;
    );

    System.out.println(happy);
}

// Java 13에 추가된 것
private static void switchOperator() {
    String day = "FRIDAY";

    int happy = switch(day) {
        case "MONDAY", "TUESDAY":
            System.out.println("MONDAY or TUESDAY");
            yeild 0;
        case "FRIDAY":
            System.out.println("FRIDAY");
            yeild 10;
        default:
            System.out.println("OTHER DAY");
            yeild 5;
    );

    System.out.println(happy);
}

 

일반 switch case 문을 사용할 경우 필요이상으로 장황하게 코드를 작성하게 되고 에러 발생 시 디버깅이 어려우며 break;를 실수로 쓰지 않아 원하지 않았던 동작이 일어날 수 있다는 단점이 있습니다.

 

이를 개선한 switch 연산자는 case에 해당하는 결과 처리를 lambda로 수행할 수 있습니다. 또한 case에 맞는 결과 값을 바로 리턴하여 변수에 저장할 수 있습니다. 특히 var 를 통해 변수를 선언할 경우 서로 다른 타입의 결과를 반환할 수 있습니다.

var happy = switch(day) {
    case "MONDAY", "TUESDAY" -> "0";
    case "FRIDAY" -> 10;
    default -> throw new IllegalStateException("Unexpected value");
};

 

Java13에서는 yield라는 키워드가 추가되었습니다. (yield 는 변수명으로도 사용 가능합니다.)

'->' 오른쪽에 오는 처리는 꼭 단일 수행일 필요는 없습니다. {} 를 사용하여 (또는 -> 대신 : 를 사용하여) 처리할 내용을 적은 뒤 반환할 값이 있을 때 yield 반환값; 형태로 사용할 수 있습니다.

 


참고

비트연산자: coding-factory.tistory.com/521

관계, 논리, 조건연산자: velog.io/@foeverna/Java-%EC%97%B0%EC%82%B0%EC%9E%90-%EA%B4%80%EA%B3%84-%EB%85%BC%EB%A6%AC-%EC%A1%B0%EA%B1%B4-%EB%B9%84%ED%8A%B8-%EC%97%B0%EC%82%B0%EC%9E%90

instanceof: codechacha.com/ko/java-instance-of/

switch 연산자: catch-me-java.tistory.com/31velog.io/@nunddu/Java-Switch-Expression-in-Java-14

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함