자바가 제공하는 다양한 연산자를 학습하세요.
산술 연산자는 수학적인 계산에 사용되는 연산자를 말한다. 기본적으로 아는 +, -, *, /, %(사칙연산자)가 산술 연산자이다.
+연산자는 숫자 값은 더해주는 연산자이고, 문자열의 경우 두개의 문자열을 이어 새로운 문자열을 만들어준다.
int a = 10;
int b = 15;
int c = a + b; // 25
String s1 = "abc";
String s2 = "def";
String s3 = s1 + s2; //abcdef
int d = 0;
d += 10; //d = d + 10; 과 동일
-연산자는 숫자 값을 빼주는 연산자이다.
int a = 10;
int b = 5;
int c = a - b; // 5
a -= 5; //a = a - 5; 와 동일
*연산자는 숫자 값을 곱해주는 연산자이다.
int a = 10;
int b = 5;
int c = a * b; // 50
a *= 5; // a = a * 5; 와 동일
/연산자는 숫자 값을 나눠주는 연산자이다. 나머지 연산자와 구별되며 몫을 나타내준다.
정수를 나누는 경우 소숫점아래로 값이 나와도 소숫점 아래의 값은 표현해주지 않으므로 주의해야한다.
예를들어 3 / 2의 경우 일반적으로 1.5라는 값을 예상하지만 정수형 타입이기 때문에 1이라는 값을 출력한다.
실수형 타입에서 정수형 타입을 나눠주는 경우 (3.0 / 2) 자동으로 실수형으로 변환된다.
int a = 3;
double b = 3.0;
System.out.println(a/2); // 1을 출력
System.out.println(b/2); // 1.5를 출력
Java에서는 정수 값을 0으로 나누는 것을 허용하지 않는다. 만약 0으로 나누는 경우 ArithmeticExcepion을 발생시킨다.
만약, 실수 값을 0.0으로 나눈다면 어떤 값이 나올까? 실수 값을 0.0으로 나누면 Infinity라고 출력되는 것을 확인할 수 있다. Infinity는 무한대라는 특수한 값을 나타낸다.
따라서, 연산 결과가 Infinity인지 확인해주려면 Double.isInfinite()메소드를 이용해 확인해줘야한다.
또한, 실수 값을 나누다보면 소수점 마지막자리가 이상함을 발견할 수 있는데, 이는 소수점이하의 값이 무한히 지속될때 소수점표기가 오류나는 것이다. 따라서 이러한 오류를 없애기 위해서 BigDecimal 클래스를 이용해 처리한다.
double a = 3.0;
int b = 3;
System.out.println(a/0.0); // Infinity
System.out.println(b/0); // ArithmeticException 발생
double c = 7.0;
System.out.println(c/3); //2.3333333333333335 소수점 마지막자리 오류
BigDecimal bigDecimal = BigDecimal.valueOf(7.0);
System.out.println(bigDecimal1.divide(BigDecimal.valueOf(3),16,BigDecimal.ROUND_HALF_UP));
//출력결과 : 2.3333333333333333
%연산자는 숫자값을 나눴을 때 나머지가 나오는 연산자이다.
나누기 연산자와 동일하게 0으로 나누는 경우에 ArithmeticExeption을 발생시킨다. 하지만 0.0으로 나누는 경우에는 NaN(Not a Number)라는 특수한 값을 출력한다. 나누기 연산과 마찬가지로 Double.isNaN() 메소드를 통해서 해당 값이 NaN값인지 확인할 수 있다.
int a = 3;
System.out.println(a%2); // 1
System.out.println(a%0); // ArithmeticExeption
System.out.println(a%0.0); // NaN
Double b = 3 % 0.0;
System.out.println(c.isNaN()); //true;
연산자 | 논리 | 설명 |
---|---|---|
& | AND | 비교하는 두 비트가 모두 1인 경우 에만 연산결과가 1 나머지는 0 |
| | OR | 비교하는 두 비트 중 하나가 1이라면 연산결과가 1 나머지는 0 |
^ | XOR | 비교하는 두 비트가 서로 다르다면 1 같다면 0 |
~ | NOT | 비트의 반전 (1의 보수) |
10진수 47과 5를 비트 연산하기 위해 2진수로 바꿔서 각각의 연산자를 알아보자.
양의 정수의 비트연산은 각각의 연산대로 연산이 가능하다. 하지만 우리는 양의 정수 비트의 반전을 했을경우 출력결과에 대해 주의깊게 봐야한다.
~ 연산자는 정수를 2진수로 표현했을 때, 비트의 반전을 취하는데 여기서 비트의 반전이란 1은 0으로, 0은 1로 바꾸는 연산을 말한다. 00101111(47)을 비트 반전하면 11010000일 것이다. 이를 우리는 1의 보수라고 한다.
그렇다면 1의 보수는 무엇일까?
컴퓨터는 음수를 표현할 수 없기 때문에 문제를 해결하기 위해서 등장한 것이 1의 보수이다.
앞에서 우리가 보수를 취한 값(11010000)중 가장 앞의 부분을 최상위 비트(Most Significant Bit = MSB)라고 한다. 이 최상위 비트 부분이 0이면 양수 1이면 음수이다.
이렇게 보면 11010000 값은 208이라는 정수가 된다고 생각할 수 있다.
여기서 2의 보수가 등장하는데 2의 보수는 10진수의 음수형이라 생각하면 된다. 따라서, 47의 2의 보수는 -47이된다.
2의 보수는 1의 보수에 +1을 해주면 되는 값이므로 11010000이 1의 보수 이므로 +1을 한 11010001이 2의 보수가 된다.
따라서 ~ 연산자는 1의 보수법과 동일하므로 따라서, 11010000의 가장 앞 부분이 1이므로 음수임을 알수 있고, 나머지 0인 부분의 비트를 읽어주면 47이 되고 여기에 -1 값을 빼주면 -48이 된다.
int a = 0b00101111; //47
int b = 0b00000101; //5
int andBit = a & b; // 뒤에서 첫번째와 세번째가 동일하므로
System.out.println(andBit); //0b00000101 = 5를 출력
int orBit = a | b; // 비교한 비트중 하나라도 1이라면 결과가 1이므로
System.out.println(orBit); //0b00101111 = 47을 출력
int xorBit = a ^ b; // 비교한 비트중 서로 다른 비트만 결과가 1이므로
System.out.println(xorBit); //0b00101010 = 42를 출력
int notA = ~a; // a의 보수를 출력
System.out.println(notA); //-48 출력
관계 연산자는 관계 연산자를 중심으로 우항과 좌항의 값을 비교해 대소 관계 혹은 동등 관계를 boolean값으로 판단해주는 연산자이다. 관계연산자를 비교연산자라고도 말한다.
연산자 | 설명 | 예시 |
---|---|---|
== | 좌항과 우항이 같으면 true값 | 10 == 10 // true |
!= | 좌항과 우항이 다르면 true 값 | 10 != 5 // true |
> | 좌항이 우항 보다 크면 true값 | 10 > 5 // true |
>= | 좌항이 우항보다 크거나 같으면 true값 | 10 >= 10 // true |
< | 우항이 좌항보다 크면 true값 | 5 < 10 // true |
<= | 우항이 좌항보다 크거나 같으면 true값 | 10 <= 10 //true |
논리 연산자는 boolean 형 값을 결과로 하는 조건식만 허용한다.
연산자 | 표기 | 설명 |
---|---|---|
AND 연산자 | && | 좌항과 우항이 모두 참이면 true값 |
OR 연산자 | || | 좌항과 우항 중 하나라도 참이면 true |
&& 연산자가 || 연산자보다 우선순위가 높으므로 조건식에서 &&와 ||가 함께 사용되는 경우 괄호를 이용해 우선순위를 명확히 해주어야한다.
instanceof 연산자는 래퍼런스 타입 변수가 어떠한 클래스 혹은 인터페이스를 참조하고 있는지 확인하여 True/False를 return해주는 연산자이다.
래퍼런스 타입 변수를 캐스팅 하는 경우에 상위 인스턴스는 같은 인스턴스를 참조하는지, 하위 인스턴스를 참조하는지 알 수 없기 때문에 instanceof연산자를 이용해 확인할 수 있다.
class Suv {
public void isType() {
System.out.println("yes, Type of Car");
}
}
class Santafe extends Suv {
@Override
public void isType() {
System.out.println("Santafe is Suv Type");
}
}
예제를 보면 Suv클래스를 Santafe 클래스가 상속 하고 있다.
첫번째 경우에 Suv 클래스는 자신을 생성하므로 instanceof 연산자로 Suv클래스를 확인하면 true, Santafe를 확인하면 false를 출력한다.
Suv suv = new Suv();
suv.isType();
System.out.println(suv instanceof Suv); //true
System.out.println(suv instanceof Santafe); //Suv 클래스는 자신만 참조하므로 false
두번째 경우에 Suv 클래스는 Santafe 클래스를 캐스팅하여 생성했기 때문에 Suv클래스와 Santafe 클래스 모두 instanceof 연산자로 확인하면 true를 출력한다.
//Suv 부모 클래스가 Santafe 클래스를 참조함을 instanceof로 확인가능
Suv suv1 = new Santafe();
suv1.isType();
System.out.println(suv1 instanceof Suv); //true
System.out.println(suv1 instanceof Santafe); //true
프로그래밍에서 = 연산자는 일반적으로 생각하는 같다라는 뜻과 달리 우항의 값을 좌항에 대입한다는 뜻이다.
연산자 | 설명 |
---|---|
= | 좌항에 우항 값을 대입 |
+= | 좌항값에 우항값을 더한 후 그 결과 값을 좌항에 대입 |
-= | 좌항값에 우항값을 뺀 후 그 결과 값을 좌항에 대입 |
*= | 좌항값에 우항값을 곱한 후 그 결과 값을 좌항에 대입 |
/= | 좌항값에 우항값을 나눈 후 그 결과 값을 좌항에 대입 |
%= | 좌항값에 우항값을 나눈 후 나온 나머지 값을 좌항에 대입 |
&= | 좌항값에 우항값을 &(AND) 비트 연산 후 그 결과 값을 좌항에 대입 |
|= | 좌항값에 우항값을 |(OR) 비트 연산 후 그 결과 값을 좌항에 대입 |
^= | 좌항값에 우항값을 ^(XOR) 비트 연산 후 그 결과 값을 좌항에 대입 |
<<= | 좌항값에 우항값만큼 왼쪽 시프트 연산한 결과값을 좌항에 대입 |
>>= | 좌항값에 우항값만큼 오른쪽 시프트 연산한 결과값을 좌항에 대입 |
자바 8에서 람다식이 등장하면서 화살표 연산자도 함께 사용되기 시작하였다.
파라미터를 전달하는 행위의 용도로 화살표 연산자를 사용한다.
인터페이스를 익명함수로 구현해서 호출할 때 밑에 예제와 같이 일반적인 메소드 구현과 동일하게 구현한다.
MaxNumber maxNumber = new MaxNumber() {
@Override
public int getMaxNumber(int x, int y) {
return x >= y ? x : y;
}
};
하지만 람다식을 이용해 구현해서 사용한다면 예제와 같이 화살표 연산자를 이용해 파라미터의 전달이 가능하다.
MaxNumber maxNumber = (x, y) -> x >= y ? x : y;
조건문이 if/else 문으로 간단하게 구분되는 경우 삼항 연산자를 이용하여 간결하게 줄이는 것이 가능하다.
형태는 조건식 ? 피연산자 1 : 피연산자 2라는 형식을 가지며 조건의 연산 결과가 true라면 피연산자1을 false라면 피연산2를 반환한다.
int a = (10>4) ? 10 : 5; //조건이 true이기 때문에 10 출력
기존 Java에는 Switch와 Case문으로 구성된 Swtich 연산자가 있다.
int result;
switch(arg) {
case 1 :
result = 1;
break;
case 2 :
result = 2;
break;
case 3:
result = 3;
break;
default :
result = 4;
}
Java12 에서 Switch 연산자는 단일 Case 문이 아닌 Multiple Case 문을 ,로 구분해 사용할 수 있게 하였으며, :(콜론)과 → (Arrow)를 이용할 수 있게 되었다.
Arrow의 경우 값을 바로 return 해준다.
또한, break문이 단순 Switch 문을 빠져나가는 용도 였다면, Java 12 에서는 break문을 통해 값을 return도 가능하도록 지원한다.
//단일 Case 문이 아닌 Multiple Case 문
int result;
switch(arg) {
case 1,2 :
result = 1;
break;
case 3,4,5 :
result = 2;
break;
default :
result = 4;
}
//Arrow를 통해 바로 값 return
String result = switch(arg) {
case 1,2 -> "hello";
case 3 -> "world";
default -> "!"
};
//break 문을 통한 값 return
String result = switch(arg) {
case 1: break "hello";
case 2: break "world";
};
Java 13에서는 Java12에서 지원하던 break 문 return 역할을 yield 키워드로 변경했다.
//yield 문을 통한 값 return
String result = switch(arg) {
case 1: yield "hello";
case 2: yield "world";
};
https://maktooob.tistory.com/22
https://reference-m1.tistory.com/105
https://colossus-java-practice.tistory.com/18
https://programmer-seva.tistory.com/9
https://skyoo2003.github.io/post/2016/11/09/java8-lambda-expression