산술 연산자는 +
,-
,*
,/
,%
와 같은 사칙연산을 다루는 연산자이다.
/
: 정수형 타입일 경우 몫을 반환하고, 실수형 타입일 경우 실수로 표현되는 몫을 반환한다.
%
: 정수형 타입일 경우 나머지를 반환하고, 실수형 타입일 경우 정수인 몫을 가질 때의 나머지를 반환한다.
산술 연산자는 두 개의 피연산자를 가지며, 연산은 오른쪽에서 왼쪽으로 이뤄진다.
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을 지원하지 않는다. 이유는 다음과 같다.
비트 연산자는 비트 단위로 논리 연산을 수행할 때 사용하는 연산자이다.
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<=
: 오른쪽 피연산자가 왼쪽 피연산자보다 크거나 같으면 truechar 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는 &&
, ||
연산자에서 명백하게 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 클래스
형태로 입력하고, 인스턴스라면 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
이라고 판단할 수 있다.
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는 +,-,*,/,%
와 함께 사용될 수 있다.
화살표 연산자는 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항 연산자는 영어로 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 | =, +=, -=, =, /=, %=, &=, ^=, |=, <<=, >>=, >>>= |
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;
}