학습할 것
- 산술 연산자
- 비트 연산자
- 관계 연산자
- 논리 연산자
- instanceof
- assignment(=) operator
- 화살표(->) 연산자
- 3항 연산자
- 연산자 우선 순위
- (optional) Java 13. switch 연산자
산술 연산자산술 연산자는 `+`,`-`,`*`,`/`,`%`와 같은 사칙연산을 다루는 연산자이다.
`/` : 정수형 타입일 경우 몫을 반환하고, 실수형 타입일 경우 실수로 표현되는 몫을 반환한다.
`%` : 정수형 타입일 경우 나머지를 반환하고, 실수형 타입일 경우 정수인 몫을 가질 때의 나머지를 반환한다.
산술 연산자는 두 개의 피연산자를 가지며, 연산은 오른쪽에서 왼쪽으로 이뤄진다.
int a = 10;
int b = 3;
int plus = a + b;
System.out.println("a + b = " + plus); // 13
int minus = a - b;
System.out.println("a - b = " + minus); // 7
int multiple = a * b;
System.out.println("a * b = " + multiple); // 30
int iDivide = a / b;
System.out.println("a / b = " + iDivide); // 3
double dDivide = a / (double)b;
System.out.println("a / b = " + dDivide); // 3.3333333333
숫자형 변수를 사용한 산술 연산은 위와 같다.
char type과 String type또한 산술 연산이 가능하다.
String s1 = "hello";
String s2 = "world";
String sPlus = s1 + s2;
System.out.println("s1 + s2 = " + sPlus); // helloworld
char c1 = 'a';
char c2 = 'b';
System.out.println(c1 + c2); // 195
System.out.println(c1 - c2); // -1
System.out.println(c1 * c2); // 9506
System.out.println(c1 / c2); // 0
String type은 덧셈 연산만 가능하지만, char type은 모든 산술 연산자가 적용된다.
char type은 정수형 타입으로 형변환되 산술 연산자가 적용된다.
추가적으로, 자바에서는 operator overloading을 지원하지 않는다. 이유는 다음과 같다.
- 자바는 OOP 언어로서 단순함과 깔끔함을 추구한다. operator overloading을 지원한다면, 코드 디자인은 더 복잡해질 것이다.
- 하나의 operator가 다양한 의미로 사용될 경우 프로그래밍 에러가 많아질 것이다.
- operator의 진짜 의미를 찾기 위한 과정 때문에 JVM은 느려질 것이다.
비트 연산자비트 연산자는 비트 단위로 논리 연산을 수행할 때 사용하는 연산자이다.
byte b1 = 1;
byte b2 = 3;
byte and = b1 & b2;
먼저 기본적으로 비트 연산자는 모든 정수형 타입에서 사용될 수 있지만,
비트 연산의 결과값은 int
형이기 때문에 int형보다 큰 데이터 타입에서만 값을 받을 수 있다.
따라서 위의 b1 & b2
는 연산되지만, 해당 값이 byte and
에 담겨지지는 못한다.
&
byte b1 = 2; // 0010
byte b2 = 5; // 0101
System.out.println( b1 & b2 ); // 0
byte b3 = 3; // 0011
byte b4 = 2; // 0010
System.out.println( b3 & b4 ); // 2
&
연산자는 비트 AND 연산을 수행한다. 이진수로 나타냈을 때 대응되는 두 비트가 모두 1이면 1을 반환한다.
|
byte b1 = 2; // 0010
byte b2 = 5; // 0101
System.out.println( b1 | b2 ); // 7
byte b3 = 3; // 0011
byte b4 = 2; // 0010
System.out.println( b3 | b4 ); // 3
|
연산자는 비트 OR 연산을 수행한다. 대응되는 두 비트 중 하나라도 1이면 1을 반환한다.
^
byte b1 = 2; // 0010
byte b2 = 5; // 0101
System.out.println( b1 ^ b2 ); // 7
byte b3 = 3; // 0011
byte b4 = 2; // 0010
System.out.println( b3 ^ b4 ); // 1
^
연산자는 비트 XOR 연산을 수행한다. 대응되는 두 비트가 다르면 1을, 같으면 0을 반환한다.
~
byte b1 = 2; // 00000010
System.out.println( ~b1 ); // 11111101, -3
byte b3 = 3; // 00000011
System.out.println( ~b3 ); // 11111100, -4
~
연산자는 비트 NOT 연산을 수행한다. 모든 비트를 반전한다. 1이면 0, 0이면 1로 바뀐다.
부호를 바꿀 때 NOT연산을 수행 후 1을 더한다. NOT 연산만 수행할 경우는 부호를 바꾸고 1을 뺀 것과
동일한 결과를 갖는다. 따라서 ~2
는 2
의 부호를 바꾸고 1을 뺀 것이므로 -3
이 된다.
<<
byte b1 = 2; // 00000010
System.out.println( b1 << 1 ); // 00000100, -3
byte b3 = 3; // 00000011
System.out.println( b3 << 3 ); // 00011000, -4
<<
연산자는 지정한 수만큼 비트를 왼쪽으로 이동시킨다. 즉 2를 지정한 횟수만큼 곱해지는 결과를 얻는다.
>>
byte b1 = 2; // 00000010
System.out.println( b1 >> 1 ); // 00000001, 1
byte b3 = 3; // 00000011
System.out.println( b3 >> 3 ); // 00000000, 0
>>
연산자는 지정한 수만큼 비트를 오른쪽으로 이동시킨다. 즉 2를 지정한 횟수만큼 나누는 결과를 얻는다.
이떄 비트의 이동으로 생기는 왼쪽 비트들은 양수일 때는 0, 음수일 떄는 1로 채워진다.
>>>
byte b1 = -1; // 11111111
System.out.println( b1 >>> 2 ); // 1073741823
byte b3 = 7; // 00000111
System.out.println( b3 >>> 1 ); // 3
>>>
연산자는 부호 비트를 포함하여 모든 비트를 오른쪽으로 이동시킨다. >>
연산자와는 다르게
왼쪽에 생긴 비트는 0으로 채워진다. 따라서 -1
에 연산자를 적용시켰을 때 가능한 양수 중 가장 큰
값이 나오게 된다.
관계 연산자
관계 연산자는 두 변수를 비교하여 true
or false
를 반환한다.==
,!=
,>
,<
,>=
,<=
==
: 두 피연산자가 동일하면 true!=
: 두 피연산자가 일치하지 않으면 true>
: 왼쪽 피연산자가 오른쪽 피연산자보다 크면 true<
: 오른쪽 피연산자가 왼쪽 피연산자보다 크면 true>=
: 왼쪽 피연산자가 오른쪽 피연산자보다 크거나 같으면 true<=
: 오른쪽 피연산자가 왼쪽 피연산자보다 크거나 같으면 true
char c = 'a';
int i = 97;
System.out.println(c == i);
int i10 = 10;
double d10 = 10.0;
System.out.println(i10 == d10);
// int i1 = 1;
// boolean b = true;
// System.out.println(i1 == b);
boolean형이 아닌 모든 primitive type 변수들은 모든 관계 연산자를 통해 비교가 가능하지만,
boolena형은 boolean형 끼리만 비교가능하다.
논리 연산자
논리 연산자에는 &&
,||
,!
가 있다. 각 연산자의 의미보다는 short circuit effect
에 관해 설명하고자 한다.
Short Circuit Effect
short circuit effect는 &&
, ||
연산자에서 명백하게 True일 경우 뒤에 있는 조건을 확인하지 않고,
결론을 내려버리는 것을 말한다. 예를 들어, &&
연산에서 앞의 조건이 false일 경우 뒤 조건은 확인하지 않는다.
만약 조건에 특정 메소드를 실행시키고자 한다면 short circuit에 의해 실행되지 않을 수 있음에 주의해야 한다.
public static void main(String[] args) {
if(returnFalse() && returnTrue()){
}
System.out.println();
if(returnTrue() && returnFalse()){
}
}
// returnFalse
// returnTrue
// returnFalse
public static boolean returnTrue(){
System.out.println("returnTrue");
return true;
}
public static boolean returnFalse(){
System.out.println("returnFalse");
return false;
}
위 경우, 첫 번째 if문에서는 앞의 조건이 false이기 때문에 뒤의 returnTrue()메소드가 실행되지 않는다.
그에 반해, 두 번째 if문에서는 앞의 조건이 true이기 때문에 뒤의 returnFalse()메소드가 실행된다.
public static void main(String[] args) {
if(returnFalse() || returnTrue()){
}
System.out.println();
if(returnTrue() || returnFalse()){
}
}
// returnFalse
// returnTrue
//
// returnTrue
public static boolean returnTrue(){
System.out.println("returnTrue");
return true;
}
public static boolean returnFalse(){
System.out.println("returnFalse");
return false;
}
위 경우, 첫 번째 if문에서는 앞의 조건이 false이기 때문에 뒤의 returnTrue()메소드가 실행된다.
그에 반해, 두 번째 if문에서는 앞의 조건이 true이기 때문에 뒤의 returnFalse()메소드가 실행되지 않는다.
이처럼, &&
연산은 앞의 조건이 false일 경우 뒤 조건을 확인하지 않고, ||
연산에서는 앞의 조건이 true일 경우 뒤의 조건을 확인하지 않는다.
반드시 실행되어야 할 메소드를 조건으로 넣는 것에 유의해야 한다.
instanceof
instanceof는 말 그대로 주어진 객체가 주어진 클래스의 인스턴스 인지 확인하는 명령어이다.
객체 instanceof 클래스
형태로 입력하고, 인스턴스라면 true를 리턴하고, 아니라면 false를 리턴한다.
public class A {
}
public class B extends A {
}
public class InstanceOfTest {
public static void main(String[] args) {
A a = new A();
B b = new B();
System.out.println("a is instanceof A? " + (a instanceof A)); // true
System.out.println("b is instanceof A? " + (b instanceof A)); // true
System.out.println("a is instanceof B? " + (a instanceof B)); // false
System.out.println("b is instanceof B? " + (b instanceof B)); // true
}
}
위 코드에서는 클래스 A,B를 InstanceOfTest 클래스와 동일한 파일에 둔 것처럼 하였지만, 실제로
자바에서 클래스는 클래스명과 동일한 파일에 속해야 한다. 상속관계와 implement관계에 있는 인스턴스라도,
자식 클래스의 인스턴스는 부모 클래스의 인스턴스라고 취급된다. 따라서 true를 반환한다.
instanceof 연산자는 언제 사용될까?
어떤 타입인지 알 수 없는 객체를 casting할 때 해당 객체의 type을 확인하기 위해 instanceof를 사용한다.
이는 ClassCastException
을 필할 수 있게 도와준다.
instanceof는 객체에 대한 null check를 수행하는데 사용될 수 있다.
Circle circle = null
if(circle instanceof Object){
}
java의 모든 클래스는 Object 클래스를 상속받고 있다. null이 아닌 모든 객체는 Object의 instance이다.
이를 활용하여 Object의 인스턴스가 아닌 객체는 null
이라고 판단할 수 있다.
assignment(=) operator
variable = value
형태로 동작하여 값을 변수에 할당하는 operator를 assignment(=) operator라고 말한다.
primitive type의 경우 value에 담긴 값을 variable에 할당하지만, object type의 경우 value에 담김
object의 reference를 variable에 할당한다. 만약 object type을 primitivee type처럼 값 전체를 할당하고
싶으면, 다음과 같은 방법을 사용해야 한다.
// Shallow Copy
User u = new User("kjm", "26");
User s = new User(u.name,u.age);
// Deep Copy
User u = new User("kjm", "26");
User d = new User(u)
// Copy Constructor를 활용해야 한다.
assignment operator는 +,-,*,/,%
와 함께 사용될 수 있다.
- a += 10 : a = a + 10
- a -= 10 : a = a - 10
- a *= 10 : a = a * 10
- a /= 10 : a = a / 10
- a %= 10 : a = a % 10
화살표(->) 연산자화살표 연산자는 Lambda 표현식이라고 불린다. 이는 메소드를 하나의 표현식으로 나타낼 때 사용된다.
예를 들어, 일반적인 메소드를 화살표 연산자를 통해 간략하게 표현할 수 있다.
int max(int x, int y){
return x > y ? x : y;
}
(x,y) -> x > y ? x : y;
(parameters) -> {statements}
or (parameters) -> expression
형태로 사용된다.
화살표 연산자는 15주차에 학습할 람다식과 관련되있으므로 그때 자세하게 공부할 예정이다.
3항 연산자
3항 연산자는 영어로 Ternary Operator라고 부른다. 연산자의 형태는 ? :
이며, if-else 구문과 리턴하는
구문을 합쳐 간단하게 표현한 것이다. 문법은 다음과 같다.
booleanExpression ? expression1 : expression2
booleanExpression이 true이면 expression1이 반환되고, false이면 expression2가 반환된다.
int a = 10;
int b;
if(a > 7){
b = 20;
}else{
b = 5;
}
int a = 10;
int b = a > 7 ? 20 : 5
위와 같이 매우 간략하게 코드를 작성할 수 있다. 3항 연산자는 중첩하여 사용이 가능한데 되도록이면
가독성을 위해서 중첩하여 사용하는 것은 지양해야 한다.||
연산자 우선 순위
위에서 부터 우선순위가 높은 순서이다.
| 연산자 | 우선순위 |
| --- | --- |
| post increment and decrement | ++, -- |
| prefix increment and decrement, and unary | ++, --, +, -, ~, ! |
| multiplicative | *, /, % |
| additive | +, - |
| shift | <<, >>, >>> |
| relational | <, >, <=, >=, instanceof |
| equality | ==, != |
| bitwise AND | & |
| bitwise exclusive OR | ^ |
| bitwise inclusive OR | | |
| logical AND | && |
| logical OR | || |
| ternary | ? : |
| assignment | =, +=, -=, *=, /=, %=, &=, ^=, |=, <<=, >>=, >>>= |
(optional) Java 13. switch 연산자
Java 13에서의 switch 연산자는 switch문과 유사하면서 다르다.
Java 12이전의 switch문
int result;
switch(String mode){
case "a":
case "b":
result = 1;
break;
case "c":
result = 2;
break;
default:
result = -1;
}
Java 12에서의 switch문에서는 ,
를 이용해 case들을 묶을 수 있다. 또한 break 1;
과 같은 형태로
값을 리턴할 수 있으며 ->
연산자를 통해 break
표현 없이 리턴값을 지정할 수 있다.
int result = switch(String mode){
case "a","b" -> 1
case "c" -> 2
default -> -1;
}
Java 13부터는 yield
라는 키워드를 통해 리턴값을 지정할 수 있다.
int result = switch(String mode){
case "a","b" :
yield 1;
case "c" :
yield 2;
default :
yield -1;
}
참고자료
- 산술 연산자 : http://www.tcpschool.com/java/java_operator_arithmetic
- why java doesn't support operator overloading : https://javarevisited.blogspot.com/2011/08/why-java-does-not-support-operator.html#axzz7Iso3NtIa
- 비트 연산자 : http://www.tcpschool.com/java/java_operator_bitwise
- 논리 연산자 : https://www.geeksforgeeks.org/java-logical-operators-with-examples/
- short circuit operators : https://www.geeksforgeeks.org/short-circuit-logical-operators-in-java-with-examples/
- instanceof : https://www.baeldung.com/java-instanceof
- 연산자 우선순위 : https://www.programiz.com/java-programming/operator-precedence
- java13 switch : https://mkyong.com/java/java-13-switch-expressions/
'프로그래밍 언어 > 자바' 카테고리의 다른 글
[Java] 상속 (0) | 2023.11.21 |
---|---|
[Java] 클래스 (1) | 2023.11.21 |
[Java] 제어문 (0) | 2023.11.21 |
[Java] 자바 데이터 타입, 변수 그리고 배열 (0) | 2023.11.21 |
[Java] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가 (0) | 2023.11.21 |