산술 연산자
수학 연산을 처리하는 연산자이다.
+
: 더하기 연산자-
: 빼기 연산자*
: 곱하기 연산자/
: 나누기 연산자%
: 나머지 연산자
1 | public class StudyHalle { |
이외에 증감 연산자(++
, --
)도 있다. 다만 연산자의 위치에 따라 증감이 이뤄지는 시기가 달라진다.
1 | public class StudyHalle03 { |
초기값(param
)과 반복 횟수(count
)를 파라미터로 받는 메서드 2개를 만들었다. 둘다 for문을 통해 반복 횟수만큼 초기값을 증감하여 콘솔에 출력하는 메서드인데, 값을 증감하는 방식이 다르다.
afterSensitization()
: param++;beforeSensitization()
: ++param;
먼저 afterSensitization()
를 콘솔에 출력했을때의 결과이다.
1 | 1 증감 : 1 |
초기값이 1인데 증감연산자를 통해 증감(param++
)을 실행하자마자 출력했을 때의 값도 1이었다. 이후에 다시 값을 출력했을때 비로소 증감이 이뤄진걸 알 수 있다.
이번엔 beforeSensitization()
을 실행해보았다.
1 | 0 증감 : 2 |
아까와는 다른 결과이다. 초기값 1을 받아서 증감(++param
)을 실행했을때, 곧바로 적용되어 출력되었다.
변수 또는 리터럴 뒤에 증감연산자가 올 경우는 해당 라인에서 즉시 값이 바뀌는게 아니라 그 다음 실행문부터 증감이 적용되며, 변수 또는 리터럴 앞에 증감연산자가 올 경우는 해당 라인에서 즉시 값이 바뀌는 것이다.
비트 연산자
비트 연산은 개발할때 해본적이 없어서 좀 생소했다.
일단 비트는 0과 1만 표현할 수 있기 때문에 비트 연산 역시 0과 1로 이뤄진다.
& (AND : 비트 논리곱)
x & y
: x,y 모두 1일 때에만 1을 반환
0101 & 0111
을 검토해보면, 각각의 자릿수를 비교해보면 두번째와 4번째 비트만 모두 1이므로 결과는 0101
이 된다.
인텔리제이에서 실제로 비트 연산이 이뤄지는지 확인해보았다. IDE에서 바이너리 코드를 보고 싶다면, Integer
객체의 toBinaryString()
메서드를 이용하면 볼수 있다.
1 | int a = 5; // 0101 |
| (OR : 비트 논리합)
x | y
: x,y 중 하나라도 1이면 1을 반환
1010 | 0100
을 검토해보면, 둘중 하나라도 1인 비트는 첫번째, 두번째, 세번째 비트이므로 결과는 1110
이 된다.
1 | int c = 10; // 1010 |
^ (XOR : 비트 베타적 논리합)
x ^ y
: x, y가 서로다를때 1을 반환
1 | int e = 10; // 0000 1010 |
~ (NOT 연산자)
~
: 비트를 1이면 0, 0이면 1로 반환
1 | int num = 10; // 0000 1010 |
<< (Left shift 연산자)
x << y
: x의 비트를 왼쪽으로 y만큼 이동(빈자리는 0으로 채움)
0011 << 0010
을 검토해보면, 0011
을 왼쪽으로 0010
만큼 이동해야 한다. 0010
은 십진수로 2이므로 0011
의 비트를 왼쪽으로 2만큼 이동하면, 1100
이 된다.
1 | int g = 3; // 0011 |
>> (Right shift 연산자)
x >> y
: x의 비트를 y만큼 오른쪽으로 이동(빈자리는 a)
-8 >> 2
를 검토해보자. 음의 십진수 -8의 비트는 다음과 같다.
1 | int i = -8; |
이 수를 오른쪽으로 비트를 2만큼 이동하면, 1 1110
이므로 -2가 된다.
비트로 십진수 음수를 표현하는 방법
출처 : 마유의 전자 이야기 - 음수 십진수를 음수 이진수로 표현 하는 방법
1 | int i = -8; // 1 1000 |
>>> (Unsigned right shift 연산자)
x >>> y
:>>
(Right shift 연산자)와 기본적으로 같지만, 오른쪽으로 밀려나면서 생기는 비트 왼쪽의 공백은 최상위 부호 비트가 아니라 0으로 채워진다. 따라서 양수이건 음수이건 항상 양수로 반환된다.
1 | int i = -8; // 1 1000 |
출처
관계 연산자
x > y
: x가 y보다 크면 true, x가 y와 같거나 작으면 false 반환x < y
: x가 y보다 작으면 true, x가 y와 같거나 크면 false 반환x >= y
: x가 y보다 크거나 같으면 true, x가 y보다 작으면 false 반환x <= y
: x가 y보다 작거나 같으면 true, x가 y보다 크면 false 반환x == y
: x와 y가 같으면 true, 다르면 false 반환x != y
: x와 y가 다르면 true, 같으면 false 반환
1 | public class Arithmetic { |
1 | x : 10 |
여기서 하나 짚고넘어가야할 점이 있다. 비교연산자(==
)를 이용하여 문자열 비교가 가능하지만, new
키워드로 생성자를 이용하여 String
객체를 생성한 경우라면, 원했던 비교가 이뤄지지 않을수 있다. 글이 길어져서 아래 링크로 대체한다.
논리 연산자
&&
: 좌항 우항 모두 true일때만 true를 반환, 그렇지 않으면 false 반환.||
: 좌항 우항 중 하나의 항이라도 true이면 true를 반환, 둘다 false 일때만 false를 반환.!
: true면 false, false면 true를 반환.
1 | public class Arithmetic { |
1 | ex01 && ex02 : false |
instance of
instance of
는 런타임시에 객체의 타입을 검사하는 연산자이다.
아래의 예제코드를 확인해보자.
StudyHalle03
클래스의 main()
을 실행하면 다음과 같은 결과를 출력한다.
1 | true |
첫번째 출력문은 Arithmetic
클래스의 인스턴스 arithmetic
의 객체 타입이 Arithmetic
인지를 instanceof
연산자를 통해 검사한 결과이다. 당연히 true
를 반환한다.
두번째 출력문은 ex01
의 인스턴스 변수 num
의 객체 타입이 Integer
인지 검사했다. Example01.java
를 보면 당연히 true
인걸 알 수 있다.
네번째 출력문에서는 ex01.num
은 Integer
이지만, Integer
의 상위 객체가 Object
이므로 이역시도 true
를 반환한다.
다섯번째 출력문에서는 인스턴스 ex02
의 타입이 Example01
타입인지 검사했다. ex02
인스턴스의 객체 타입은 Example02
이지만, Example02
클래스는 Example01
클래스를 상속받으므로 인스턴스 ex02
의 객체 타입은 Example01
이면서 동시에 Example02
이기도하다. 이 역시 true
를 반환한다.
assignment(=) operator
=
: 좌항에 변수가 오면 우항엔 리터럴, 좌항에 인스턴스가 오면 우항엔 객체가 오는 대입 연산자.
1 | int num = 10; |
+=
: 좌항의 변수에 우항의 리터럴을+
한후, 다시 좌항의 변수에 대입하는 연산자
1 | int num = 10; |
-=
: 좌항의 변수에 우항의 리터럴을-
한후, 다시 좌항의 변수에 대입하는 연산자
1 | int num = 30; |
*=
: 좌항의 변수에 우항의 리터럴을*
한후, 다시 좌항의 변수에 대입하는 연산자
1 | int num = 5; |
/=
: 좌항의 변수에 우항의 리터럴을/
한후, 다시 좌항의 변수에 대입하는 연산자
1 | int num = 15; |
%=
: 좌항의 변수에 우항의 리터럴을%
연산 후, 다시 좌항의 변수에 나머지 값을 대입하는 연산자
1 | int num = 20; |
3항 연산자
변수를 선언 또는 리터럴을 대입할때 조건 분기처리할 수 있는 연산자이다.
1 | int var = (condition) ? (true) : (false); |
?
을 기준으로 좌항에 조건식이 들어가며, 우항은 다시 :
을 기준으로 나눈다. 조건식의 결과에 따라 true
이면, :
의 좌항, false
이면, :
우항으로 대입된다.
아래 예제 코드는 Date
클래스를 통해 불러온 현재 시간을 통해 String
형 변수에 넣는 값을 분기처리하여 대입하는 코드이다.
1 | import java.text.SimpleDateFormat; |
연산자 우선 순위
우선순위 | 연산자 |
---|---|
1 | ++, –, ~, ! (증감/부정 연산자) |
2 | *, /, % |
3 | +, - |
4 | <<, >>, >>> (비트 단위 시프트 연산자) |
5 | <, <=, >, >= |
6 | ==, != |
7 | & (비트 단위 논리 연산자) |
8 | ^ |
9 | | |
10 | && |
11 | || |
12 | ? : (삼항 연산자) |
13 | ==, +=, -=, *=, /=, %=, <<=, >>=, &=, ^=, ~= (대입 연산자) |
출처 : 남궁성 - Java의 정석
화살표(->) 연산자
자바에서 화살표 연산자(->
)는 람다식(Lambda Expression)을 의미한다. 람다는 JDK 1.8부터 도입되었는데, 자바라는 언어를 객체지향 언어인 동시에 함수형 언어로서도 동작하게 해준 개념이다.
람다는 메서드를 하나의 식으로 표현하는 표현식이다. 메서드이면서 이름도 없고, 반환값도 없다. 클래스에 종속되지도 않는다. 이상의 설명은 아래 포스팅으로 대체하고, 이 포스팅에선 사용법만 간단히 설명하겠다.
DevAndy - [Java8] 람다와 함수형 인터페이스
람다식을 알아보기 위해 예제코드를 통해 들여다보자.
람다에선 타입을 명시하지 않아도 되는데, 컴파일러가 타입 추론이 가능케 하기 위해서는 함수형 인터페이스 를 사용해야 한다. 인터페이스는 반드시 메서드가 하나만 작성되어 있어야 하며, 인터페이스에 함수형 인터페이스임을 명시하는 @FuntionalInterface
도 필요하다.
1 |
|
1 | public class StudyHalle03 { |
위의 코드에서 프로그램 메인 메서드를 람다식으로 바꾸면 다음과 같다.
1 | public class StudyHalle03 { |
변경사항을 확인해보자.
maxNum()
의 메서드명과 파라미터의 데이터 타입을 삭제했다. 함수형 인터페이스이기 때문에 메서드가 하나만 존재하여 컴파일러가 타입 추론이 가능하기 때문에 데이터 타입을 삭제했다.
메서드명 역시 함수형 인터페이스에선 메서드가 하나만 존재하기 때문에 굳이 이름을 명시하지 않아도 어떤 메서드를 사용하는지 컴파일러가 추론할 수 있다.
이처럼 함수형 인터페이스를 사용하여 메서드를 보다 간단하게 작성한걸 람다식이라고 한다.
출처