본문으로 바로가기

연산자

category Programming Language/☕ Java 2020. 11. 26. 10:10

연산자

 

대표적인 연산자로 다음 4 연산자가 있다.

 

산술 연산자 * / + -

비교(관계) 연산자 > < <= >= == !=

논리 연산자 && ||

대입 연산자(assignment operator) = 

 

특수한 형태의 연산자는 추후에 다루자

 

 

전위/후위 단항 연산자

 

전에 많이 써봐서 간단하다.

i++ : 값이 참조되기 전에 증가시킨다
++i : 값이 참조된 후에 증가시킨다.

 

public class javaVar {
    public static void main(String[] args) {
        int i = 3;
        System.out.println(i++); // 참조된 후에 덧셈이 되므로 그대로 3 출력

        i = 3;
        System.out.println(++i); // 참조되기 전에 덧셈이 되므로 4 출력
    }
}

만약 이해하기 어렵다면 전위형(++i)은 다른 조작 전에 우선 실행되며 후위형(i++)은 조작 후에 이뤄진다고 생각하자

 

 

 

산술 연산자

 

기본 내용은 생략합니다.

 

문제는 다음과 같이 타입에서 나타난다. 둘 다 int이기 때문에 반환하는 값도 int가 된다.

int a = 3;
int b = 4;
System.out.println(b / a); // 1.333333 이 아니라 1

 

타입 프로모션을 이용하려면 어느 한 쪽을 float이나 double로 바꿔야 한다. float은 소수점 6자리 까지 정확도를 보증하는 것을 잊지말자

float a = 3f;
int b = 4;
System.out.println(b / a); // 1.3333334

 

나눗셈, 나머지 연산에는 무조건 float, double 자료형을 사용한다고 보는게 편합니다.

float a = 10.0f / 3.0f;
float b = 10 / 3;
		
System.out.println(a); // 3.3333333
System.out.println(b); // 3.0

 

int 0 으로 나누면 에러가 나지만 float, double으로 나누면 infinity가 나옵니다.

System.out.println(3/0); // error
System.out.println(3/0.0); // infinity

 

 

비교(관계) 연산자, 논리 연산자

 

기본 내용은 생략합니다.

 

연산의 특성 중에 Short-Circuit Evaluation(Lazy Evaluation) 이라는 것이 있다.(이하 SCE)
SCE를 간단히 설명하면, "연산의 효율 및 속도를 높이기 위해 불필요한 연산을 생략하는 행위" 이다.

쉽게 말해 True || .... 이런 꼴이 있다면 그 이후를 연산하는 것은 무의미하다. 어차피 논리적으로 True이기 때문이다. 따라서 이후는 연산하지 않는다. 이는 javascript에서도 동일하다.

 

저는 true && ... 꼴로 작성해서 true일 경우에만 ... 로직을 실행하게 만드는 식의 트릭을 종종 사용합니다.

public class javaVar {
    public static void main(String[] args) {
        int n1 = 0;
        int n2 = 0;
        boolean result;

        result = ((n1 += 10) < 0) && ((n2 += 10) > 0); // 이미 false && 꼴이라서 이후 연산 안 함
        System.out.println("result: " + result + ", n1:" + n1 + ", n2:" + n2);

        result = ((n1 += 10) > 0) || ((n2 += 10) > 0); // 이미 true || 꼴이라서 이후 연산 안 함
        System.out.println("result: " + result + ", n1:" + n1 + ", n2:" + n2);
    }
}

 

 

== 연산자는 단순히 값이 아니라 메모리 주소가 같은지 확인합니다. 때문에 reference 타입 비교에서는 의도대로 동작하지 않고, 값을 비교하기 위해서는 equals 메서드를 사용해야 한다는 점을 기억해둡시다. 사실 deepcopy할 때도 그렇고 primitive/reference 타입 차이를 이해하는 건 기본이죠.

String s1 = new String("HELLO"); 
String s2 = new String("HELLO"); 
        
System.out.println(s1 == s2);  // false
System.out.println(s1.equals(s2)); // true 
  • Both s1 and s2 refers to different objects.
  • When we use == operator for s1 and s2 comparison then the result is false as both have different addresses in memory.
  • Using equals, the result is true because its only comparing the values given in s1 and s2.

 

 

대입 연산자(assignment operator) = 

 

복합 대입 연산자. 

사실 기존에도 많이 써온 내용이지만 비트 연산자에 대입 연산을 같이 쓰는 경우는 별로 없었다.

a += b a = a + b
a -= b a = a - b
a *= b a = a * b
a /= b a = a / b
a %= b a = a % b
a &= b a = a & b
a ^= b a = a ^ b
a <<= b a = a << b
a >>>= b a = a >>> b
int n1 = 0;
n1 += 3;
System.out.println(n1); // output : 3

 

여기서 대항연산자는 자동 형변환을 해준다.

int n1 = 0;
n1 += 15L; // n1 long타입이 됨

 

 

비트 연산자

python으로 다뤄본 적이 있으니 다음 포스트 참고 darrengwon.tistory.com/863

 

비트 연산자는 각각의 비트를 대상으로 연산을 진행하는 연산자이며 피연산자는 반드시 정수여야 한다.
실수를 대상으로 하는 비트 연산은 의미가 없기에 자바는 이를 지원하지 않는다.

 

연산자설명

& 비트 단위로 AND 연산을 한다
| 비트 단위로 OR 연산을 한다
^ 비트 단위로 XOR 연산을 한다
~ 피연산자의 모든 비트를 반전시켜서 얻은 결과를 반환한다

 

비트 쉬프트 연산자가 조금 더 중요하다.

비트를 왼쪽 혹은 오른쪽으로 1칸씩 움직이는 것

<< 피연산자의 비트 열을 왼쪽으로 이동. 이동에 따른 빈 공간은 0으로 채움
>> 피연산자의 비트 열을 오른쪽으로 이동. 이동에 따른 빈 공간은 음수의 경우 1, 양수의 경우 0으로 채움
>>> 피연산자의 비트 열을 오른쪽으로 이동. 이동에 따른 빈 공간은 0으로 채움

 

int a = 7;
System.out.println(Integer.toBinaryString(a)); // 7은 111이다
System.out.println(a >> 1); // 오른쪽으로 한칸씩. 011이 되어서 3이 됩니다.
System.out.println(a << 1); // 왼쪽으로 한칸씩. 1110이 되어서 14가 됩니다.

 

instanceof

쉽게 말해 클래스의 인스턴스이냐 아니냐를 묻는 겁니다. 바로 코드로 보시죠. 직관적으로 이해가 가능합니다.

 

  • == 객체의 메모리 주소의 비교
  • instanceof 해당 클래스의 인스턴스인가?

 

  • instanceof 연산자는 주로 조건문에서 객체가 형변환 가능한 타입인지 확인할 때 많이 사용한다.
class Test {
  ...
}

class Test2 extends Test{
  ...
}

class Operator{
    public static void main(String[] args) {
        Test obj1 = new Test();
        Test obj2 = new Test2();
        System.out.println(obj1 instanceof Test);    // true
        System.out.println(obj1 instanceof Test2);   // false
        System.out.println(obj2 instanceof Test);    // true, 상속 관계
        System.out.println(obj2 instanceof Test2);   // true
        System.out.println(null instanceof Object);  // false
    }
}

 

 

삼항 연산자

 

생략. js에서랑 같음.

int a = 3;
String output = a > 3 ? "over 3" : "nono";
System.out.println(output);

 

 

화살표 연산자

 

우리가 생각하는 () => ... 꼴 함수다. js에서 => 를 사용했듯 java에서는 -> 를 사용한다.

(매개변수들) -> {함수 구현} 
public class thread {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                System.out.println("스레드 생성");
            }
        }).start();

        new Thread(() -> {
            System.out.println("람다 표현식을 사용한 스레드 생성");
        }).start();
    }
}

 

 

 

연산자의 우선순위

 

일반 사칙 연산에서는 *, / 가 우선되고 +, -가 나중에 계산되는 우선순위가 있다. 일반 수학에서 사용되는 법칙이 마찬가지로 적용된다.

int output = 3 + 2 * 3;
System.out.println(output);

 

연산자에 대한 우선순위가 정해져 있다. 산술 > 비교 > 논리 > 대입 순인데 이런 순서를 외우는 것보다 대부분 상식선에서 해결 가능한 부분이므로 참고만 하고 넘어가도록하자. 

[], ., () 1
expr++, expr-- 2
++expr, --expr, +expr, -expr, ~, !, (type) 3
*, /, % 4
+, - 5
<<, >>, >>> 6
<, >, <=, >=, instanceof 7
==, != 8
& 9
^ 10
| 11
&& 12
|| 13
? expr : expr 14
=, +=, -=, *=, /=, %=, ^=, |=, <<=, >>=, >>>= 15

 

참고한 글)

 

github.com/yeo311/java-study-with-whiteship/tree/main/week3

www.notion.so/3-f3a94e0092664d8aa1debe7e88dec43b

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체