1. 연산자
1.1 연산자의 종류
- 연산자의 종류
연산자명 | 종류 | 설명 |
산술연산자 | + , - , * , / ,% | 두값을 사칙연산해주는 연산자 |
관계연산자 | >, <, >=, <=, ==, != | 두값을 비교하는 연산자 |
논리연산자 | &&, ||, !, ^, | 두값의 논리값을 비교하는 연산자 |
비트연산자 | >>, >>>, <<, &, |, ^, ~ | 비트의 값을 연산하는 연산자 |
조건연산자(삼항연산자) | 조건 ? 값1 : 값2 | 조건에 따라 값을 결정하는 연산자 |
증감연산자 | i++, ++i, i--, --i | 값을 1증가 혹은 1감소시키는 연산자 |
할당연산자 | +=,-=,*=, /=, %= | 값을 계산하고 대입하는 연산자 |
1.2 연산자의 우선순위
- 연산자 우선순위에 따라 코드를 수행한다.
2. 산술연산자
2.1 산술연산자의 기본종류
- 산술연산은 기본적인 사칙연산을 나타낸다.
package com.eomcs.lang.ex05;
//# 산술 연산자 : +, -, *, /, %
//
public class Exam0110 {
public static void main(String[] args) {
System.out.println(100 + 27);
System.out.println(100 - 27);
System.out.println(100 * 27);
System.out.println(100 / 27);
System.out.println(100 % 27); // 나눈 나머지
}
}
2.2 형변환
- 묵시적 형변환
- 자바에서 정수는 기본 int형이다.
- 정수연산을 할때 int(4byte)형의 메모리를 만든 후, 연산을 수행한다.
package com.eomcs.lang.ex05;
//# 산술 연산자 : 기본 연산 단위
//
public class Exam0130 {
public static void main(String[] args) {
byte b;
b = 5; // OK!
b = 6; // OK!
// - 리터럴 5, 6은 4바이트 정수 값이다.
// - 정수 리터럴은 기본이 4바이트 크기이지만,
// - byte 변수에 저장할 수 있다면 허락한다!
b = 5 + 6; // OK!
// - 리터럴끼리 산술 연산한 결과도 리터럴로 간주한다.
// - 그 결과 값이 변수의 범위 내의 값이면 허락한다.
System.out.println(b);
// - 이유? 리터럴 값은 컴파일 단계에서 그 값이 얼마인지 확인할 수 있기 때문이다.
// - 변수의 경우는 컴파일 단계에서 값을 확인할 수 없다.
byte x = 5, y = 6, z;
z = x; // OK!
z = y; // OK!
// z = x + y; // 컴파일 오류!
//
// "자바의 정수 연산은 최소 단위가 4바이트이다."
// "그래서 byte나 short의 연산 단위가 기본으로 4바이트이다."
//
// - 자바는 정수 변수에 대해 산술 연산을 수행할 때,
// 그 변수의 값이 4바이트 보다 작다면(byte나 short 라면),
// 4바이트로 만들어 연산을 수행한다.
// - 즉 임시 4바이트 정수 메모리를 만든 다음에
// 그 메모리에 값을 담은 후에 연산을 수행한다.
// - 따라서 x + y는 바로 실행하지 않고
// 임의의 4바이트 정수 메모리를 만든 다음에
// 그 메모리에 x와 y 값을 넣고 연산을 수행한다.
// - 연산 결과도 당연히 4바이트이다.
// 그래서 4바이트 값을 1바이트 메모리에 넣지 못하기 때문에
// 컴파일 오류가 발생하는 것이다!
// short도 마찬가지이다.
short s1 = 5;
short s2 = 6;
short s3;
s3 = s1; // OK!
s3 = s2; // OK!
// s3 = s1 + s2; // 컴파일 오류!
// 결론!
// - 숫자의 크기에 상관없이 작은 숫자를 다루더라도
// 정수를 다룰 때는 그냥 int를 사용하라!
// - byte는 보통 파일의 데이터를 읽어 들일 때 사용한다.
- 자바의 최소 연산 단위는 int이다.
- int 보다 작은 크기의 메모리 값을 다룰 때는 내부적으로 int로 자동 형변환을 수행한 다음에 연산을 수행한다.
- 내부적으로 자동 형변환하는 것을 "암시적 형변환(implicit type conversion)"이라 부른다.
=> byte + byte = int
=> short + short = int
=> byte + short = int
- 연산 결과는 항상 피연산자의 타입과 같다.
=> int + int = int
=> long + long = long
=> float + float = float
=> double + double = double
- 다른 타입과 연산을 수행할 때는 내부적으로 같은 타입으로 맞춘 다음에 실행한다.
2.3 오버플로우
- 오버플로우 발생원인 : 선언된 변수타입보다 큰 값을 대입하게 되면 오버플로우가 발생
package com.eomcs.lang.ex05;
//# 산술 연산자 : 연산의 결과 타입
//
public class Exam0142 {
public static void main(String[] args) {
int x = Integer.MAX_VALUE; // 0x7fffffff = 약 +21억
int y = Integer.MAX_VALUE; // 0x7fffffff = 약 +21억
int r1 = x + y; // 0x7fffffff + 0x7fffffff = 0xfffffffe = -2
System.out.println(r1); // int(4byte) + int(4byte) = int(4byte)
long r2 = x + y; // 0x7fffffff + 0x7fffffff = 0xfffffffe = -2
System.out.println(r2); // int(4byte) + int(4byte) = int(4byte)
// x+y의 값이 레지스터상에서 이미 오버플로우가 발생하였다.
// r2의 크기는 8byte이지만, 레지스터에 있는 -2를 전달 받기 때문에 r2는 -2가 된다.
2.4 부동소수점의 계산
- 오버플로우
package com.eomcs.lang.ex05;
//# 산술 연산자 : 연산의 결과 타입
//
public class Exam0143 {
public static void main(String[] args) {
float f1 = 987.6543f;
float f2 = 1.111111f;
System.out.println(f1);
System.out.println(f2);
float r1 = f1 + f2;
// f1과 f2에 들어 있는 값이 유효자릿수라 하더라도
// 연산 결과가 유효자릿수가 아니라면 값이 잘리거나 반올림 된다.
// => float과 float의 연산 결과는 float이기 때문이다.
// 987.6543
// + 1.111111
// ---------------
// 988.765411 <=== float의 유효자릿수인 7자리를 넘어간다.
// 988.7654 <=== 유효자릿수를 넘어가는 수는 짤린다
}
}
- 자릿수 삭제
package com.eomcs.lang.ex05;
//# 산술 연산자 : 연산의 결과 타입
//
public class Exam0143 {
public static void main(String[] args) {
float f1 = 987.6543f;
float f2 = 1.111111f;
System.out.println(f1);
System.out.println(f2);
double r2 = f1 + f2;
System.out.println(r2);
// 기대값: 988.765411
// 결과값: 988.765380859375
// 기대한 결과가 나오지 않은 이유?
// => float과 float의 연산 결과는 float이다.
// => double 변수에 저장하기 전에 이미 float 값이 되면서 일부 값이 왜곡되었다.
// 그런데 r1 변수와 달리 뒤에 이상한 숫자가 많이 붙는 이유는 무엇인가?
// => IEEE 754의 이진수 변환 문제때문이다.
// => 4바이트 float 부동소수점을 8바이트 double 부동소수점 변수에 저장할 때
// 왜곡된 값이 들어 갈 수 있다.
// => float을 double 변수에 넣을 때 왜곡이 발생하기 때문에
// 가능한 double 변수로 값을 바꾼 다음에 연산을 수행하라.
// 더 좋은 것은 처음부터 double 변수를 사용하라!
// 다음과 같이 처음부터 double 변수를 사용하라!
double d1 = 987.6543;
double d2 = 1.111111;
double r5 = d1 + d2; // = 988.765411
System.out.println(r5);
// 그럼에도 실제 출력해보면 맨 뒤에 극한의 작은 수가 붙는다.
// 이유? IEEE 754 이진수 변환 문제이다. 고민하지 말라!
// 어떻게 처리할 건데? 맨 뒤에 붙은 극한의 작은 수는 그냥 잘라 버린다.
}
}
3. 관계연산자
3.1 관계연산자의 기본종류
package com.eomcs.lang.ex05;
//# 관계 연산자(relational operators: <, <=, >, >=), 등위 연산자(equality operators: ==, !=)
//
public class Exam0210 {
public static void main(String[] args) {
int a = 10;
int b = 20;
// 비교의 결과는 true 또는 false이다.
// 즉 boolean 값이다.
boolean r1 = a < b; // true
// int r2 = a < b; // 컴파일 오류!
System.out.println(a < b); // true
System.out.println(a <= b); // true
System.out.println(a > b); // false
System.out.println(a >= b); // false
System.out.println(a == b); // false
System.out.println(a != b); // true
}
}
3.2 부동소수점의 비교
- 부동소수점의 비교 및 논리연산은 IEE-754변환(정규화 과정에서 발생)에 따라 극한의 소수값이 발생할 수 있다.
package com.eomcs.lang.ex05;
public class Exam0220 {
public static void main(String[] args) {
double d1 = 987.6543;
double d2 = 1.111111;
System.out.println((d1 + d2) == 988.765411);
// 결과는 false이다.
// 이유?
// - 부동소수점 값을 IEEE 754 명세에 따라 2진수로 바꿔 메모리에 담을 때
// 정규화(소수점 이하의 수를 2진수로 바꾸는) 과정에서
// 정수로 딱 떨어지지 않는 경우가 있다.
// 즉 극한의 미세 소수점이 붙을 수 있다.
// - CPU나 OS, JVM의 문제가 아니다.
// - IEEE 754 명세에 따라 부동소수점을 처리하는 모든
// 컴퓨터에서 발생하는 문제이다.
// - 이런 부동소수점을 계산할 때 기대하는 값과 다른 값이 나올 수 있다.
// - 또한 연산한 결과를 메모리에 담을 때도 정규화 과정에서
// 극한의 미세 소수점이 붙을 수 있다.
System.out.println(d1);
System.out.println(d2);
System.out.println(d1 + d2);
// 987.6543 + 1.111111 = 988.7654110000001
}
}
- 이를 해결하기 위해, 극한의 작은값은 삭제를 해준다.
// 관계 연산자 : 부동소수점 비교
package com.eomcs.lang.ex05;
public class Exam0220 {
public static void main(String[] args) {
double d1 = 987.6543;
double d2 = 1.111111;
System.out.println((d1 + d2) == 988.765411); //false
double x = 234.765411;
double y = 754.0;
System.out.println((x + y) == 988.765411); //true
System.out.println((d1 + d2) == (x + y)); // false
// 소수점 뒤에 붙은 극소수의 값을 무시하면 된다.
// => JVM이 자동으로 처리하지 않는다.
// => 다음과 같이 개발자가 직접 처리해야 한다.
double EPSILON = 0.00001;
System.out.println(Math.abs((d1 + d2) - (x + y)) < EPSILON);
}
}
4. 논리연산자
4.1 논리연산자의 기본종류
package com.eomcs.lang.ex05;
//# 논리 연산자 : &&, ||, !(not), ^(XOR; exclusive-OR)
//
public class Exam0310 {
public static void main(String[] args) {
// AND 연산자
// - 두 개의 논리 값이 모두 true일 때 결과가 true가 된다.
System.out.println(true && true);
System.out.println(true && false);
System.out.println(false && true);
System.out.println(false && false);
System.out.println("-----------------------");
// OR 연산자
// - 두 개의 논리 값 중 한 개라도 true이면 결과는 true가 된다.
System.out.println(true || true);
System.out.println(true || false);
System.out.println(false || true);
System.out.println(false || false);
System.out.println("-----------------------");
// NOT 연산자
// - true는 false로 false는 true로 바꾼다.
System.out.println(!true);
System.out.println(!false);
System.out.println("-----------------------");
// exclusive-OR(XOR)연산자
// - 배타적 비교 연산자라 부른다.
// - 두 개의 값이 다를 때 true이다.
System.out.println(true ^ true);
System.out.println(false ^ false);
System.out.println(true ^ false);
}
}
4.2 논리연산자 &&와 &
- 두개를 사용하면 값을 비교하기 위해 사용한다.
- 한개를 사용하면 논리값과 비트값을 비교하기 위해 사용한다.
package com.eomcs.lang.ex05;
//# 논리 연산자 : &, |
//
public class Exam0320 {
public static void main(String[] args) {
int a = 8; // 0b 0000 0000 0000 1000
int b = 5; // 0b 0000 0000 0000 0101
System.out.println(a | b); // 0b 1101
System.out.println(a == 8 && b ==5); // true
System.out.println(false & true); //false
System.out.println(false && false); //false
}
}
- 한개를 사용하면 논리연산자 전체를 수행하지만, 두개를 사용하면 먼저나온 값에 따라 후행 조건이 결정된다.
package com.eomcs.lang.ex05;
//# 논리 연산자 : || vs |
//
public class Exam0340 {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
boolean r = a || (b = true);
// a = true, b = false, r = true
System.out.printf("a=%b, b=%b, r=%b\n", a, b, r);
a = true;
b = false;
r = a | (b = true);
// a = true, b = true, r = true
System.out.printf("a=%b, b=%b, r=%b\n", a, b, r);
}
}
5. 비트연산자
5.1 비트연산자의 기본종류
- 논리연산자
package com.eomcs.lang.ex05;
//# 비트 연산자 & 활용: 그림의 한 픽셀에서 빨강 색을 제거하고 싶다.
//
public class Exam0354 {
public static void main(String[] args) {
int pixel = 0x003f4478; // 각 바이트의 값이 '00RRGGBB' 이라 가정하다.
System.out.println(pixel & 0x0000ffff);
// pixel = 00000000_00111111_01000100_01111000
// & 00000000_00000000_11111111_11111111
// 00000000_00000000_01000100_01111000
int pixel2 = 0x003f4478; // 각 바이트의 값이 '00RRGGBB' 이라 가정하다.
System.out.println(pixel2 | 0x00000057);
// pixel2 = 00000000_00111111_01000100_01111000
// | 00000000_00000000_00000000_01010111
// 00000000_00111111_01000100_01111111
}
}
- 시프트연산자
>> | >>> | << |
- 자바에서는 unsigned 타입이 없기 때문에 최상위 비트에 0으로 삽입하기 위해서 >>> 가 있다.
package com.eomcs.lang.ex05;
//# 비트 이동 연산자 : >>, >>>, <<
//
public class Exam0410 {
public static void main(String[] args) {
// << 비트 이동 연산자 사용법
// - 왼쪽으로 비트를 이동시킨다.
// - 오른 쪽 빈자리는 0으로 채운다.
// - 왼쪽 경계를 넘어간 비트는 자른다.
//
int i = 1;
// [00000000000000000000000000000001] = 1
System.out.println(i << 1);
// 0[0000000000000000000000000000001 ]
// [00000000000000000000000000000010] = 2
System.out.println(i << 2);
// 00[000000000000000000000000000001 ]
// [00000000000000000000000000000100] = 4
System.out.println(i << 3);
// 000[00000000000000000000000000001 ]
// [00000000000000000000000000001000] = 8
System.out.println(i << 4);
// 0000[0000000000000000000000000001 ]
// [00000000000000000000000000010000] = 16
i = 11; // [00000000000000000000000000001011]
System.out.println(i << 1); // 0[00000000000000000000000000010110] => 22
System.out.println(i << 2); // 00[00000000000000000000000000101100] => 44
System.out.println(i << 3); // 000[00000000000000000000000001011000] => 88
// 왼쪽 이동
// - 1비트 이동은 곱하기 2 한 것과 같은 효과를 준다.
// - 값을 배수로 증가시킬 때 곱하기 연산을 하는 것 보다
// 왼쪽 비트 이동 연산을 하는 것이 빠르기 때문에
// 실무에서는 이 비트 이동 연산을 자주 사용한다.
// - 비트 이동 => '2**이동비트'를 곱한 것과 같은 결과를 만든다.
}
}
- 시프트연산자 범위
- 시프트연산자의 최대 범위는 선언된 자료형의 크기와 동일하다 .
- 그 이상의 시프트는 (%)연산자로 나머지 만큼만 이동한다.
package com.eomcs.lang.ex05;
// # 비트 이동 연산자 : 비트 이동의 유효 범위
//
public class Exam0412 {
public static void main(String[] args) {
System.out.println(3 << 1);
// 000000000 00000000 00000000 00000011 = 3
// 0|000000000 00000000 00000000 00000011x = 비트이동
// 000000000 00000000 00000000 000000110 = 6
System.out.println(3 << 33); // 6
System.out.println(3 << 65); // 6
System.out.println(3 << 97); // 6
}
}
- 시프트연산자 효과
- 시프트 연산자는 2의 승수만큼 곱하고 나누는 효과를 준다.
- 산술연산은 연산레지스터를 경유하지만, 시프트연산은 바로 비트로 적용하여 속도가 빠르다.
- 연산자 우선순위는 낮기때문에 ()로 우선순위를 주어야 한다.
- 최하단 비트가 1일 경우, 나머지는 버림을 한다
ex) // 0001 >> 2는 0100으로 바뀐다.
package com.eomcs.lang.ex05;
//# 비트 이동 연산자 : >>, >>>, <<
//
public class Exam0421 {
public static void main(String[] args) {
// 음수일 경우,
// - 빈자리는 1로 채운다.
int i = 105; // [00000000000000000000000001101001]
System.out.println(i); // => 105
System.out.println(i >> 1);
// [ 0000000000000000000000000110100]1
// [00000000000000000000000000110100] => 52
//
int i = -87; // [11111111111111111111111110101001]
System.out.println(i); // => -87
System.out.println(i >> 1);
// [ 1111111111111111111111111010100]1
// [11111111111111111111111111010100]1 => -44
// 음수 값에 대해 오른쪽으로 비트 이동
// => 2**n으로 나눈 것과 같다.
// => 소수점이 있을 경우 그 수 보다 바로 밑 정수 값이 결과이다.
// => 왼쪽 빈자리가 부호비트로 채워진다.
}
}
6. 조건연산자
6.1 조건연산자의 기본종류
package com.eomcs.lang.ex05;
// # 조건 연산자 => ? :
//
public class Exam0510 {
public static void main(String[] args) {
// 조건연산자
// => 조건 ? 표현식1 : 표현식2
// => 조건이 참이면 표현식1을 실행하고,
// 조건이 거짓이면 표현식2를 실행한다.
//
int age = 20;
// 조건연산자의 왼쪽은 변수이어야 한다.
String a = "성년", b = "미성년";
//case1. 표현식으로 받는경우
String message = age > 18 ? a : b;
System.out.printf("나이 %d는(은) %s이다.\n", age, message);
//case2. 매개변수로 받는경우
test(age > 18 ? "성년" : "미성년");
static void test(String value) {
System.out.println(value + " 입니다.");}
}
}
- 표현식과 문장의 관계
- 표현식(expression) : 작업을 수행한 후 결과를 리턴하는 문장이다.
- 문장(statement) : 작업을 수행시키는 명령이다.
- 문장과 표현식의 관계 : statement 중에서 결과를 리턴하는 statement를 expression이라 부른다.
6.2 조건연산자을 사용할 수 없는경우
case 1.
int age = 20;
// (age > 18) ? "성년" : "미성년";
// 조건 연산자를 수행한 후 그 결과를 받을 변수를 받드시 선언해야 한다.
// => 선언하지 않으면 문법 오류!
case 2.
int age = 20;
// age > 18 ? System.out.println("성인이다.") : System.out.println("미성년자이다.");
// 표현식은 반드시 값을 리턴해야 한다.
case 3.
int age = 20;
// String str = age > 18 ? System.out.println("성인이다.") : System.out.println("미성년자이다.");
// 조건 연산자의 결과 값이 왼편의 변수 타입과 일치해야 한다.
// => 결과 값이 없으면 문법 오류!
7. 증감연산자
7.1 증감연산자의 기본종류
package com.eomcs.lang.ex05;
//# 증감 연산자 : 후위(post-fix) 증가 연산자
//
public class Exam0610 {
public static void main(String[] args) {
int i = 2;
//case1.
i++;
// int temp = i;
// i = i + 1;
// temp에 대한 호출이 없으면
// temp는 가상메모리에서 삭제
--i;
// i = i - 1;
// temp = i;
// temp는 가상메모리에서 삭제
}
}
- i++의 원리
- i++ : i 가 바로 증가하는 것이 아니라 레지스터에 i의 값을 임시로 저장하고, 레지스터의 i값을 사용하는 것이다.
- 이는 어셈블리어를 확인 하면 확인할 수 있다.
package com.eomcs.lang.ex05;
//# 증감 연산자 : 후위(post-fix) 증가 연산자
//
public class Exam0610 {
public static void main(String[] args) {
int i = 2;
i++; // i => 3
System.out.println(i++); // 3
// int temp = i; //<-- 임시 변수를 만들어 현재 i 값을 저장한다.
// i = i + 1;
// System.out.println(temp);
System.out.println(i); // 5
}
}
8. 할당연산자
8.1 할당연산자의 종류
- 할당연산자는 대입과 연산을 합친 문법이다.
- 축약형이라고 생각하면된다.
package com.eomcs.lang.ex05;
//# 할당(배정,대입) 연산자 : += -= *= /= %= &= |= ^= <<= >>= >>>=
//
public class Exam0710 {
public static void main(String[] args) {
int i = 2;
i = i + 20;
i += 20; // += 연산자를 사용하면 위의 코드를 축약할 수 있다.
System.out.println(i);
i = 2;
i *= 5;
System.out.println(i);
}
}
'개발자 꿈나무의 하루 > 01_Boot Camp' 카테고리의 다른 글
(네이버클라우드 부트캠프) 12일차 - Java프로그래밍 기초(표준입출력,String), 실습프로젝트(메서드) (1) | 2024.06.11 |
---|---|
(네이버클라우드 부트캠프) 11일차 - Java프로그래밍 기초(조건문,반복문,배열), 실습프로젝트 (0) | 2024.06.11 |
(네이버클라우드 부트캠프) 9일차 - 실습프로젝트 (1) | 2024.06.05 |
(네이버클라우드 부트캠프) 8일차 - Eclipse IDE설정 및 Java프로그래밍 기초(변수) (0) | 2024.06.04 |
(네이버클라우드 부트캠프) 7일차 - Java프로그래밍 기초(문자집합 & 수) (0) | 2024.06.03 |