[Java] Java 기초(3)

36041 단어 JavaJava

[Java] Java 기초(3)

연산자란 하나, 둘 혹은 세 개의 피연산자에 대해서 특정 연산을 수행한 후 결과를 반환하는 기호이다.

간단히 말해 '연산을 수행하는 기호'이다.

피연산자는 연산의 대상이라고 할 수 있다. 리터럴이 될 수도 있고, 변수가 될 수도 있으며, 또 다른 식이 될 수도 있다.

식은 연산자와 피연산자를 조합한 것이고, 이를 수행한 것을 평가한다고 한다.

연산은 식을 평가하고 식의 평가 결과를 반환하는 것

1. 산술 연산자

산술 연산자는 우리가 흔히 말하는 더하기, 빼기, 곱하기, 나누기를 수행하는 연산자이다.

연산자설명
+더하기 연산자(문자열 연결에도 사용)
-빼기 연산자
*곱하기 연산자
/나누기 연산자
%나머지(모듈로) 연산자
int result = 1 + 2;
System.out.println("1 + 2 = " + result); // 3

int originalResult = result;
result = originalResult - 1;
System.out.println(originalResult + " - 1 = " + result); // 2

originalResult = result;
result = originalResult * 7;
System.out.println(originalResult + " * 7 = " + result); // 14

originalResult = result;
result = originalResult / 3;
System.out.println(originalResult + " / 3 = " + result); // 4

result = originalResult % 3;
System.out.println(originalResult + " % 3 = " + result); // 2

double doubleResult = 14.0 / 3.0;
System.out.println("14.0 / 3.0 = " + doubleResult); // 4.66667

doubleResult = 14.0 % 3.0;
System.out.println("14.0 % 3.0 = " + doubleResult); // 2.0

doubleResult = 14.0 / 0.0;
System.out.println("14.0 / 0.0 = " + doubleResult); // Infinity

doubleResult = -14.0 / 0.0;
System.out.println("14.0 / 0.0 = " + doubleResult); // -Infinity

doubleResult = 14.0 % 0.0;
System.out.println("14.0 % 0.0 = " + doubleResult); // NaN

doubleResult = -14.0 % 0.0;
System.out.println("14.0 % 0.0 = " + doubleResult); // NaN

try {
  result = 14 / 0; // ArithmeticException: / by zero
} catch (ArithmeticException e) {
  System.out.println("14 / 0 = " + e.getMessage());
}

try {
  result = 14 % 0; // ArithmeticException: / by zero
} catch (ArithmeticException e) {
  System.out.println("14 % 0 = " + e.getMessage());
}

정수형일 때 0으로 나누거나 모듈로 연산을 수행하는 것을 조심해야 한다. 이는 런타임에 발생하는 오류이기 때문에 컴파일타임에 확인할 수 없다.

또한 자료형의 크기에 따른 오버플로우도 조심해야 한다.

실수형은 연산 과정 자체에서 오차가 발생할 수 있으며 이를 조심해야 한다. 이는 특히 테스트 코드를 작성할 때, 어느 정도의 오차를 허용할 것인지 반드시 설정해주어야 하는 이유이다.

실수형은 0으로 나누거나 모듈로 연산을 사용하면 위와 같이 Infinity, -Infinity, NaN 값을 가진다.
(InfinityNaN이 피연산자인 경우도 존재)

1-1. 단항 연산자

+를 붙이면 양수 값을 나타낸다. (생략 가능)
-를 붙이면 음수 값을 나타낸다.

1-2. 증감 연산자

++를 붙이면 값을 1씩 증가시킨다.
--를 붙이면 값을 1씩 감소시킨다.
++, --는 오른쪽에 위치하는 경우 식을 평가하기 전에 증가시키고, 왼쪽에 위치하는 경우 식을 평가한 후에 증가시킨다.

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

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

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

for (int i = 0; i < 3; i++) {
  System.out.println(++result); // 2, 3, 4
}

System.out.println(result); // 4(이미 이전에 증가해서 그대로 출력된다.)

for (int i = 0; i < 3; i++) {
  System.out.println(result++); // 4, 5, 6
}

System.out.println(result); // 7(마지막에 증가한다.)

증감 연산자가 앞에 오면 먼저 증가/감소시키고, 대입 후 계산
증감 연산자가 뒤에 오면 먼저 대입시키고, 계산 후 증가/감소

2. 비트 연산자

Java에서는 정수 타입에 대해서 비트와 시프트 연산을 제공하는데 이를 비트 연산자라고 한다.

우리가 사용하는 정수 타입을 컴퓨터가 이해할 수 있도록 이진수 형식으로 표현된다. 이 이진수 조각 하나 하나를 비트라고 표현하고, 비트 연산은 각 비트마다의 연산을 수행한다.

~NOT 연산으로, 비트를 반전시키는 단항 연산자이다.
&AND 연산으로, 피연산자들의 비트별로 AND 연산을 수행하게 된다.
|OR 연산으로, 피연산자들의 비트별로 OR 연산을 수행하게 된다.
^XOR 연산으로, 피연산자들의 비트별로 XOR 연산을 수행하게 된다.

x~x
01
10

xyx | yx & yx ^ y
11110
10101
01101
00000
// 참고로 부호비트도 고려해야 합니다.
byte a = 0b00001010; //  10  (0|0001010)
byte b = 0b00101000; //  40  (0|0101000)
byte c = -0b00001010; // -10 (1|1110110)

// ~ 연산자(1의 보수를 만들어줌)
System.out.println(~a); // 1|1110101 => -11 여기에 1을 더하면 -10이 됩니다.(2의 보수)
System.out.println(~c); // 0|0001001 => 9   여기에 1을 더하면 10이 됩니다.

// & 연산자
System.out.println(a & b); // 0|0001000 => 8
System.out.println(a & c); // 0|0000010 => 2
System.out.println(b & c); // 0|0100000 => 32

// | 연산자
System.out.println(a | b); // 0|0101010 => 42
System.out.println(a | c); // 1|1111110 => -2
System.out.println(b | c); // 1|1111110 => -2

// ^ 연산자(다른 경우에만 1)
System.out.println(a ^ b); // 0|0100010 => 34
System.out.println(a ^ c); // 1|1111100 => -4
System.out.println(b ^ c); // 1|1011110 => -34

2-1. 시프트 연산자

시프트 연산자는 피연산자의 각 비트를 오른쪽(>>) 또는 왼쪽 (<<)으로 이동시키는 연산자이다.

<< 연산자의 경우 빈 칸을 0으로 채우면 되지만, >> 연산자는 음수일 때는 1로, 양수일 때는 0으로 빈칸을 채운다.

x << n 연산은 x * 2n과 같다.
x >> n 연산은 x / 2n과 같다.

이를 사용하는 이유는 비트 연산이 일반 산술 연산보다 속도가 빠르기 때문이다. 하지만 가독성이 떨어지므로 성능 개선이 필수적인 부분에서만 사용해야 한다.

Unsigned Right 시프트 연산자(>>>)는 오른쪽으로 이동하되, 빈칸을 0으로 채운다.

3. 관계 연산자

관계 연산자는 피연산자가 같은지(==), 같지 않은지(!=), 큰지(>), 크거나 같은지(>=), 작은지(<), 작거나 같은지(<=) 비교하는 연산자들이다.

연산 결과가 맞으면 true, 틀리면 false로 boolean 타입을 반환한다.

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

문자열의 동등성을 비교할 때에는 equals() 메소드를 사용해야 한다.

4. 논리 연산자

논리 연산자는 피연산자로 boolean 값을 받으며, 이는 평가의 결과가 될 수도 있다.

&& 연산자는 두 연산결과가 참인지 판단한다. 만약 둘 중 하나라도 거짓이라면 거짓을 반환한다. 만약, 왼쪽의 피연산자가 거짓이라면 오른쪽의 피연산자는 평가하지 않는다.
|| 연산자는 두 연산결과 중 한 쪽만 참이어도 참을 반환한다. 만약, 왼쪽의 피연산자가 참이라면 오른쪽의 피연산자는 평가하지 않는다.
! 연산자는 피연산자를 하나만 받는 단항 연산자이며, true를 false로, false를 true로 변경한다. 식의 평가 방향은 오른쪽에서 왼쪽이다.

위에서 말한 평가하지 않는 것을 short circuit evaluation이라고 하며, 식의 평가를 수행하지 않기 때문에 좀 더 빠른 결과를 얻을 수 있다.

5. instanceof

instanceof 연산자는 타입 비교 연산자이다. 이를 이용해 객체가 클래스의 인스턴스, 하위 클래스의 인스턴스 또는 특정 인터페이스를 구현하는 클래스의 인스턴스인지 테스트할 수 있다.

public class Main {

  public static void main(String[] args) {
    Animal animal1 = new Animal();
    Animal animal2 = new Cat();

    System.out.println("animal1 is instanceof Animal: " + (animal1 instanceof Animal)); // true
    System.out.println("animal1 is instanceof Cat: " + (animal1 instanceof Cat)); // false
    System.out.println("animal1 is instanceof Meowable: " + (animal1 instanceof Meowable)); // false

    System.out.println("animal2 is instanceof Animal: " + (animal2 instanceof Animal)); // true
    System.out.println("animal2 is instanceof Cat: " + (animal2 instanceof Cat)); // true
    System.out.println("animal2 is instanceof Meowable: " + (animal2 instanceof Meowable)); // true
  }
}

class Animal {}
interface Meowable {}
class Cat extends Animal implements Meowable {}

6. assignment(=) operator

할당 연산자(=)는 연산자 오른쪽의 값을 연산자 왼쪽의 피연산자에 할당한다. 식의 평가 방향은 오른쪽에서 왼쪽이다.

int x = 0;
위와 같은 연산자는 x라는 이름을 가진 변수 공간에 0이라는 int형 리터럴을 할당한다.

또한, 객체도 할당할 수 있다.

Animal animal = new Cat();
위와 같은 식은 animal이라는 Animal 타입 참조 변수에 새로 만들어진 Cat 인스턴스의 참조를 할당한다.

할당할 수 없는 리터럴과 같은 값은 왼쪽 피연산자가 될 수 없다.

6-1. 복합문(Compound Statement), 복합 할당(Compond Assignment)

다른 연산자와 할당 연산자를 결합한다.

a = a + 1; 이라는 식의 경우 a가 중복된다. 이를 간단히 나타내기 위해 a += 1;로 표현할 수 있다.

대입 연산자기능
=왼쪽의 피연산자에 오른쪽 피연산자 대입
+=왼쪽의 피연산자에 오른쪽의 피연산자를 더한후, 그 결과 값을 왼쪽의 피연산자를 대입
-=왼쪽의 피연산자에 오른쪽의 피연산자를 뺀후, 그 결과 값을 왼쪽의 피연산자를 대입
*=왼쪽의 피연산자에 오른쪽의 피연산자를 곱한후, 그 결과 값을 왼쪽의 피연산자를 대입
/=왼쪽의 피연산자에 오른쪽의 피연산자를 나눈후, 그 결과 값을 왼쪽의 피연산자를 대입
%=왼쪽의 피연산자에 오른쪽의 피연산자를 나눈후, 그 나머지 값을 왼쪽의 피연산자를 대입
&=왼쪽의 피연산자에 오른쪽의 피연산자와 비트 & 연산후, 그 결과 값을 왼쪽의 피연산자를 대입
|=왼쪽의 피연산자에 오른쪽의 피연산자와
^=왼쪽의 피연산자에 오른쪽의 피연산자와 ^연산후, 그 결과 값을 왼쪽의 피연산자를 대입
<<=왼쪽의 피연산자에 오른쪽의 피연산자만큼 왼쪽 시프트 후, 그 결과 값을 왼쪽의 피연산자를 대입
>>=왼쪽의 피연산자에 오른쪽의 피연산자만큼 부호 유지 오른쪽 시프트후, 그 결과 값을 왼쪽의 피연산자를 대입
>>>=왼쪽의 피연산자에 오른쪽의 피연산자만큼 부호에 상관없이 오른쪽 시프트 후, 그 결과 값을 왼쪽의 피연산자를 대입

7. 화살표(->) 연산자

화살표 연산자는 Java8에서 추가된 람다 표현식의 한 부분이다.

(파라미터) -> { 함수몸체 } 형태로 작성한다.

람다 표현식은 함수형 프로그래밍에서 가장 중요한 부분 중 하나이며, 이를 도입함으로써 Java는 함수형 언어로도 기능하게 되었다. 이는 일부 익명 클래스를 간단히 표현할 수 있는 방법이라고도 볼 수 있다.
또한 @FunctionalInterface 어노테이션을 붙인 인터페이스의 구현체로도 사용할 수 있다.

IntelliJ에서는 이런 람다 표현식으로 코드를 변경시켜주는 기능이 내장되어 있다.

람다식은 메소드가 매개변수로 전달되는 것이 가능하고, 결과로 반환되는 것도 가능하게 해준다.

8. 삼항 연산자

? :는 논리 연산자의 일종으로 피연산자 세 개를 받는 유일한 연산자여서 삼항 연산자로 많이 불린다.
이는 if-else문과 비슷하게 보이며, 한 줄로 간단하게 표현할 수 있다.

조건 ? 참일 때 값 : 거짓일 때 값의 표현으로 사용한다.

9. 연산자 우선 순위

연산자 우선 순위는 식을 평가할 때 많은 영향을 미친다. 우선 순위에 따라서 의도한 대로 프로그램이 동작하지 않을 수 있기 때문이다.

아래 표는 연산자의 우선순위를 나타낸 표이다. 상단에 가까울 수록 우선순위가 높으며, 같은 줄의 연산자는 동일한 우선순위를 가진다. 이런 경우 식의 평가순서가 영향을 미치게 된다. 할당 연산자를 제외한 모든 이항 연산자는 왼쪽에서 오른쪽으로 평가되며, 할당 연산자는 오른쪽에서 왼쪽으로 평가한다.

실제 프로그래밍을 할 때는 헷갈릴 가능성이 높으므로 ()로 먼저 수행하고 싶은 식을 묶어주는 것이 가독성 등의 여러 관점에서 더 좋다.

10. switch 연산자

기존 switch문에서는 다수의 casebreak가 존재하게 된다. break를 빼먹을 경우 다음 분기로 넘어가고, return값이 존재할 수 없다.

switch 연산자에서는 break를 사용하지 않아도 된다. yield 예약어를 사용해 값을 return 할 수 있다.
case -> A와 같이 표현하거나 break 대신 yield로 return 값을 반환해주도록 표현한다.

switch(expression){
	case expression -> expression;
	...
	default -> expression
};

switch(expression){
	case expression:
		expression;
		yield expression;
	...
	default:
		expression;
		yield expression;
};

좋은 웹페이지 즐겨찾기