연산자(operator): 연산을 수행하는 기호(+, -, *, / 등)
피연산자(operand): 연산자의 작업 대상(변수, 상수, 리터럴, 수식)
연산자는 피연산자로 연산을 수행하고 나면 항상 결과값을 반환.
식: 연산자와 피연산자를 조합하여 계산하고자하는 바를 표현한 것.
식을 계산하여 결과를 얻는 것을 ‘식을 평가한다’고 한다.
대입 연산자: = 변수와 같이 값을 저장할 수 있는 공간에 결과를 저장하기 위해 필요한 연산자.
// y: 변수
// =: 대입 연산자
// *, x: 연산자
// 4, 3: 피연산자
y = 4 * x + 3;
| 종류 | 연산자 | 설명 |
|---|---|---|
| 산술 연산자 | + - * / % << >> | 사칙 연산과 나머지 연산 |
| 비교 연산자 | > < >= <= == != | 크고 작음과 같고 다름을 비교 |
| 논리 연산자 | && | |
| 대립 연산자 | = | 우변의 값을 좌변에 저장 |
| 기타 | (type) ?: instanceof | 형변환 연산자, 삼항 연산자, instanceof연산자 |
피연산자의 개수에 의한 분류
삼항 연산자: 피연산자의 개수가 세 개. ?: 하나만 존재.
연산자를 기능별, 피연산자의 개수별로 나누어 분류하는 이유 → 연산자의 우선순위 때문
식에 사용된 연산자가 둘 이상인 경우, 연산자의 우선순위에 의해서 연산순서가결정.
연산자의 결합규칙
1. 산술 > 비교 > 논리 > 대입. 대입은 제일 마지막에 수행.
2. 단항(1) > 이항(2) > 삼항(3). 단항 연산자의 우선순위가 이항 연산자보다 높음.
3. 단항 연산자와 대입 연산자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽.
| 종류 | 결합 규칙 | 연산자 | 우선순위 |
|---|---|---|---|
| 단항 연산자 | ← | ++ — + - ~ ! (type) | 높음 |
| 산술 연산자 | → | * / & | |
| 산술 연산자 | → | + - | |
| 산술 연산자 | → | << >> | |
| 비교 연산자 | → | < > <= >= instanceof | |
| 비교 연산자 | → | == != | |
| 논리 연산자 | → | & | |
| 논리 연산자 | → | ^ | |
| 논리 연산자 | → | | | |
| 논리 연산자 | → | && | |
| 논리 연산자 | → | || | |
| 삼항 연산자 | → | ?: | |
| 대입 연산자 | ← | = += -= *= /= %= <<= >>= &= ^= |= | 낮음 |
이항 연산자는 두 피연산자의 타입이 일치해야 연산이 가능하므로, 피연산자의 타입이 서로 다르다면 연산 전에 형변환 연산자로 타입을 일치시켜야함.
int i = 10;
float f = 20.0f;
float result = f + (float)i;
산술 변환의 규칙
1. 두 피연산자의 타입을 같게 일치시킨다. (보다 큰 타입으로 일치)
2. 피연산자의 타입이 int보다 작은 타입이면 int로 변환된다.
증가 연산자(++): 피연산자의 값을 1 증가시킴.
감소 연산자(—): 피연산자의 값을 1 감소시킴.
전위형(prefix): 피연산자의 왼쪽에 위치.
후위형(postfix): 피연산자의 오른쪽에 위치.
class OperatorEx2 {
public static void main(String args[]) {
int i=5, j=0;
j = i++;
System.out.println("j=i++; 실행 후, i=" + i +", j="+ j);
// i = 6, j = 5
i=5; // 결과를 비교하기 위해, i와 j의 값을 다시 5와 0으로 변경
j=0;
j = ++i;
System.out.println("j=++i; 실행 후, i=" + i +", j="+ j);
//i = 6, j = 6
}
}
전위형은 변수의 값을 먼저 증가시킨 후에 변수의 값을 읽음.
후위형은 변수의 값을 먼저 읽어온 후에 값을 증가.
식에 두 번 이상 포함된 변수에 증감연산자를 사용하는 것은 피해야 함.
부호 연산자’-’는 피연산자의 부호를 반대로 변경한 결과를 반환.
부호 연산자는 boolean형과 char형을 제외한 기본형에만 사용 가능.
class OperatorEx4 {
public static void main(String[] args) {
int i = -10;
i = +i;
System.out.println(i);
i=-10;
i = -i;
System.out.println(i);
}
}
피연산자가 정수형인 경우, 나누는 수로 0을 사용할 수 없음. → ArithmeticException 발생.
연산자 통해서 연속된 char 출력하기
class OperatorEx14 {
public static void main(String[] args) {
char c = 'a';
for(int i=0; i<26; i++) { // 블럭{} 안의 문장을 26번을 반복한다.
System.out.print(c++); //'a'부터 26개의 문자를 출력한다.
}
System.out.println(); // 줄바꿈을 한다.
c = 'A';
for(int i=0; i<26; i++) { // 블럭{} 안의 문장을 26번을 반복한다.
System.out.print(c++); //'A'부터 26개의 문자를 출력한다.
}
System.out.println();
c='0';
for(int i=0; i<10; i++) { // 블럭{} 안의 문장을 10번을 반복한다.
System.out.print(c++); //'0'부터 10개의 문자를 출력한다.
}
System.out.println();
}
}
왼쪽의 피연산자를 오른쪽 피연산자로 나누고 난 나머지 값을 결과로 반환.
피연산자로 정수만 허용됨.
class OperatorEx20 {
public static void main(String[] args) {
System.out.println(-10 % 8); // -2
System.out.println(10 % -8); // 2
System.out.println(-10 % -8); // -2
}
}
나머지 연산자는 나누는 수로 음수도 허용하지만 부호는 무시한다.
| 비교 연산자 | 연산 결과 |
|---|---|
| > | 좌변 값이 크면 true, 아니면 false |
| < | 좌변 값이 작으면 true, 아니면 false |
| >= | 좌변 값이 크거나 같으면 true, 아니면 false |
| <= | 좌변 값이 작거나 같으면 true, 아니면 false |
| 비교 연산자 | 연산 결과 |
|---|---|
| == | 두 값이 같으면 true, 아니면 false |
| != | 두 값이 다르면 true, 아니면 false |
문자열의 비교
class OperatorEx23 {
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("abc");
System.out.printf("\"abc\"==\"abc\" ? %b%n", "abc"=="abc"); // T
System.out.printf(" str1==\"abc\" ? %b%n", str1=="abc"); // T
System.out.printf(" str2==\"abc\" ? %b%n", str2=="abc"); // F 객체가 다르기 때문
System.out.printf("str1.equals(\"abc\") ? %b%n", str1.equals("abc")); // T
System.out.printf("str2.equals(\"abc\") ? %b%n", str2.equals("abc")); // T
System.out.printf("str2.equals(\"ABC\") ? %b%n", str2.equals("ABC")); // F
System.out.printf("str2.equalsIgnoreCase(\"ABC\") ? %b%n", str2.equalsIgnoreCase("ABC")); // T
}
}
문자열을 비교할 때 equals()
대소문자를 구별하지 않고 비교하고 싶으면 equalsIgnoreCase()
|| (OR 결합): 피연산자 중 어느 한 쪽만 true이면 true를 결과로 얻는다.
&& (AND 결합): 피연산자 둘 다 true여야 true를 결과로 얻는다.
| x | y | x || y | x && y |
|---|---|---|---|
| true | true | true | true |
| true | false | true | false |
| false | true | true | false |
| false | false | false | false |
class OperatorEx24 {
public static void main(String args[]) {
int x = 0;
char ch = ' ';
x = 15;
System.out.printf("x=%2d, 10 < x && x < 20 =%b\n", x, 10 < x && x < 20);
// true
x = 6;
System.out.printf("x=%2d, x%%2==0 || x%%3==0 && x%%6!=0 =%b\n", x, x%2==0||x%3==0&&x%6!=0);
System.out.printf("x=%2d, (x%%2==0 || x%%3==0) && x%%6!=0 =%b\n", x,(x%2==0||x%3==0)&&x%6!=0);
// true
// false
ch='1';
System.out.printf("ch='%c', '0' <= ch && ch <= '9' =%b\n", ch, '0' <= ch && ch <='9');
// true
ch='a';
System.out.printf("ch='%c', 'a' <= ch && ch <= 'z' =%b\n", ch, 'a' <= ch && ch <='z');
// true
ch='A';
System.out.printf("ch='%c', 'A' <= ch && ch <= 'Z' =%b\n", ch, 'A' <= ch && ch <='Z');
// true
ch='q';
System.out.printf("ch='%c', ch=='q' || ch=='Q' =%b\n", ch, ch=='q' || ch=='Q');
// true
}
}
효율적인 연산
| x | y | x || y |
|---|---|---|
| true | true | true |
| true | false | true |
| false | true | false |
| false | false | false |
x가 true이면 x || y 는 항상 true이다
| x | y | x && y |
|---|---|---|
| true | true | true |
| true | false | false |
| false | true | false |
| false | false | false |
x가 false이면 x && y 는 항상 false이다.
논리 부정 연산자 !
피연산자가 true면 false로, false면 true로 결과를 반환.
| x | y |
|---|---|
| true | false |
| false | true |
| (OR연산자): 피연산자 중 한 쪽의 값이 1이면, 1을 결과로 얻는다. 그 외에는 0을 얻는다.
& (AND연산자): 피연산자 양 쪽이 모두 1이어야만 1을 결과로 얻는다. 그 외에는 0을 얻는다.
^ (XOR연산자): 피연산자의 값이 서로 다를 때만 1을 결과로 얻는다. 같을 때에는 0을 얻는다.
| x | y | x | y | x & y | x ^ y |
|---|---|---|---|---|
| 1 | 1 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 | 1 |
| 0 | 1 | 1 | 0 | 1 |
| 0 | 0 | 0 | 0 | 0 |
| 는 주로 특정 비트의 값을 변경할 때 사용.
&는 주로 특정 비트의 값을 뽑아낼 때 사용.
^는 간단한 암호화에 사용.
class OperatorEx28 {
public static void main(String[] args) {
int x = 0xAB;
int y = 0xF;
System.out.printf("x = %#X \t\t%s%n", x, toBinaryString(x));
System.out.printf("y = %#X \t\t%s%n", x, toBinaryString(y));
System.out.printf("%#X | %#X = %#X \t%s%n", x, y, x | y, toBinaryString(x | y));
System.out.printf("%#X & %#X = %#X \t%s%n", x, y, x & y, toBinaryString(x & y));
System.out.printf("%#X ^ %#X = %#X \t%s%n", x, y, x ^ y, toBinaryString(x ^ y));
System.out.printf("%#X ^ %#X ^ %#X = %#X %s%n", x, y, y, x ^ y ^ y, toBinaryString(x ^ y ^ y));
} // main의 끝
// 10진 정수를 2진수로 변환하는 메서드
static String toBinaryString(int x) {
String zero = "00000000000000000000000000000000";
String tmp = zero + Integer.toBinaryString(x);
return tmp.substring(tmp.length()-32);
}
}
/* x = 0XAB 00000000000000000000000010101011
y = 0XAB 00000000000000000000000000001111
0XAB | 0XF = 0XAF 00000000000000000000000010101111
0XAB & 0XF = 0XB 00000000000000000000000000001011
0XAB ^ 0XF = 0XA4 00000000000000000000000010100100
0XAB ^ 0XF ^ 0XF = 0XAB 00000000000000000000000010101011 */
비트 전환 연산자 ~
피연산자를 2진수로 표현했을 때, 0은 1로, 1은 0으로 바꾼다.
논리부정 연산자 ! 와 비슷.
| x | ~x |
|---|---|
| 1 | 0 |
| 0 | 1 |
피연산자의 1의 보수를 얻을 수 있어 비트전환연산자를 1의 보수 연산자라고 부르기도 함.
class OperatorEx29 {
public static void main(String[] args) {
byte p = 10;
byte n = -10;
System.out.printf(" p =%d \t%s%n", p, toBinaryString(p));
System.out.printf("~p =%d \t%s%n", ~p, toBinaryString(~p));
System.out.printf("~p+1=%d \t%s%n", ~p+1, toBinaryString(~p+1));
System.out.printf("~~p =%d \t%s%n", ~~p, toBinaryString(~~p));
System.out.println();
System.out.printf(" n =%d%n", n);
System.out.printf("~(n-1)=%d%n", ~(n-1));
} // main의 끝
// 10진 정수를 2진수로 변환하는 메서드
static String toBinaryString(int x) {
String zero = "00000000000000000000000000000000";
String tmp = zero + Integer.toBinaryString(x);
return tmp.substring(tmp.length()-32);
}
}
/* p =10 00000000000000000000000000001010
~p =-11 11111111111111111111111111110101
~p+1=-10 11111111111111111111111111110110
~~p =10 00000000000000000000000000001010
n =-10
~(n-1)=10 */
쉬프트 연산자 << >>
피연산자의 각 자리를 오른쪽(>>) 또는 왼쪽(<<)으로 이동.
자리이동으로 저장범위를 벗어난 값들은 버려지고 빈자리는 0으로 채워짐.
class OperatorEx30 {
public static void main(String[] args) {
int dec = 8;
System.out.printf("%d >> %d = %4d \t%s%n", dec, 0, dec >> 0, toBinaryString(dec >> 0));
System.out.printf("%d >> %d = %4d \t%s%n", dec, 1, dec >> 1, toBinaryString(dec >> 1));
System.out.printf("%d >> %d = %4d \t%s%n", dec, 2, dec >> 2, toBinaryString(dec >> 2));
System.out.printf("%d << %d = %4d \t%s%n", dec, 0, dec << 0, toBinaryString(dec << 0));
System.out.printf("%d << %d = %4d \t%s%n", dec, 1, dec << 1, toBinaryString(dec << 1));
System.out.printf("%d << %d = %4d \t%s%n", dec, 2, dec << 2, toBinaryString(dec << 2));
System.out.println();
dec = -8;
System.out.printf("%d >> %d = %4d \t%s%n", dec, 0, dec >> 0, toBinaryString(dec >> 0));
System.out.printf("%d >> %d = %4d \t%s%n", dec, 1, dec >> 1, toBinaryString(dec >> 1));
System.out.printf("%d >> %d = %4d \t%s%n", dec, 2, dec >> 2, toBinaryString(dec >> 2));
System.out.printf("%d << %d = %4d \t%s%n", dec, 0, dec << 0, toBinaryString(dec << 0));
System.out.printf("%d << %d = %4d \t%s%n", dec, 1, dec << 1, toBinaryString(dec << 1));
System.out.printf("%d << %d = %4d \t%s%n", dec, 2, dec << 2, toBinaryString(dec << 2));
System.out.println();
dec = 8;
System.out.printf("%d >> %2d = %4d \t%s%n", dec, 0, dec >> 0, toBinaryString(dec << 2));
System.out.printf("%d >> %2d = %4d \t%s%n", dec, 32, dec >> 32, toBinaryString(dec << 2));
} // main의 끝
// 10진 정수를 2진수로 변환하는 메서드
static String toBinaryString(int x) {
String zero = "00000000000000000000000000000000";
String tmp = zero + Integer.toBinaryString(x);
return tmp.substring(tmp.length()-32);
}
}
/*
8 >> 0 = 8 00000000000000000000000000001000
8 >> 1 = 4 00000000000000000000000000000100
8 >> 2 = 2 00000000000000000000000000000010
8 << 0 = 8 00000000000000000000000000001000
8 << 1 = 16 00000000000000000000000000010000
8 << 2 = 32 00000000000000000000000000100000
-8 >> 0 = -8 11111111111111111111111111111000
-8 >> 1 = -4 11111111111111111111111111111100
-8 >> 2 = -2 11111111111111111111111111111110
-8 << 0 = -8 11111111111111111111111111111000
-8 << 1 = -16 11111111111111111111111111110000
-8 << 2 = -32 11111111111111111111111111100000
8 >> 0 = 8 00000000000000000000000000100000
8 >> 32 = 8 00000000000000000000000000100000
*/
조건식 ? 식1 : 식 2;
// 조건식이 참이면 식1 수행, 거짓이면 식2 수행
// 1
result = (x > y) ? x : y;
// 2
if (x > y) {
result = x;
} else {
result = y;
}
// 1과 2는 같음.
class OperatorEx32 {
public static void main(String args[]) {
int x, y, z;
int absX, absY, absZ;
char signX, signY, signZ;
x = 10;
y = -5;
z = 0;
absX = x >= 0 ? x : -x; // x의 값이 음수이면, 양수로 만든다.
absY = y >= 0 ? y : -y;
absZ = z >= 0 ? z : -z;
signX = x > 0 ? '+' : ( x==0 ? ' ' : '-'); // 조건 연산자를 중첩
signY = y > 0 ? '+' : ( y==0 ? ' ' : '-');
signZ = z > 0 ? '+' : ( z==0 ? ' ' : '-');
System.out.printf("x=%c%d\n", signX, absX);
System.out.printf("y=%c%d\n", signY, absY);
System.out.printf("z=%c%d\n", signZ, absZ);
}
}
대입 연산자: 변수와 같은 저장공간에 값 또는 수식의 연산 결과를 저장하는데 사용.
lvalue와 rvalue
lvalue = left value = 왼쪽 피연산자. 변수처럼 값을 변경할 수 있는 것이어야 함. 리터럴, 상수 불가능.
rvalue = right value = 오른쪽 피연산자. 변수, 식, 상수 모두 가능.
복합 대입 연산자
복합 대입 연산자: 다른 연산자(op)와 결합하여 op= 과 같은 방식으로 사용.
결합된 두 연산자는 반드시 공백없이 붙여써야 함.
ex) x += 3; y -= 2;