연산자
연산자: 연산을 수행하는 것( + , - , / , % )
예를 들면, 3 + 4 있으면
연산자: +
피연산자는: 3, 4 된다.
피연산자는 변수, 상수, 리터럴, 수식 등이 올 수 있다.
연산자에는 우선순위가 있다. 우선순위가 높은 순으로 설명하면,
1. 단항연산자: - , + , ++ , -- , ! , (Type), 부호연산자( + , - )
- 단항은 단항 연산자 > 이항 연산자 > 삼함 연산자 순으로 단항 연산자가 우선순위가 가장 높다.
2. 산술연산자: 곱셉 / 나눗셈( * , / ) 우선하고 다음 덧셈 / 뺄샘( + , - ) 다음 쉬프트 연산자( >> , << )
3. 비교연산자: < , > , <= , >= , ==, !=, instanceof
4. 논리연산자: &&, & , || , | 여기서 && 와 || 함께 있는 연산에서는 && 과 우선 적용되고
그 다음 || 적용된다. 만약 내가 || 를 우선 적용하고 싶다면 (괄호)를 넣어주면 된다.
예) ( x+y > 0 || y+e < 0 ) && con! = null
5. 삼항 연산자: ?:
6. 대입연산자: =, +=, -=, /=, *=, %=, <<=, >>=, &=, ^, |= 으로 우선순위가 가장 낮다.
* 비트연산자( & , | , ^ )는 비교연산자(&& , || ) 보다 우선순위가 낮다.
예) num & num2 == 0 이 수식은 num & (num2 == 0) 와 같다.
연산자의 결합 규칙
1. 연산자 대부분 왼쪽에서 오른쪽으로 이동한다.
예를 들면, 3+3+4+2+5+1 있으면 3부터 시작해서 3+3+4+2 순으로 왼쪽에서 오른쪽으로 이동한다.
2. 오른쪽에서 왼쪽으로 이동할 경우(단항 연산자 및 대입연산자)
예를 들면, x=w=s=5 이면 s=5를 먼저 수행하고 다음 w=5, x=5를 수행한다(오른쪽에서 왼쪽)
연산자의 우선순위와 결합법칙은 3가지만 기억
1. 산술 > 비교 > 논리 > 대입 , 대입은 제일 마지막에 수행된다.
2. 단항(1) > 이항(2) > 삼항(3). 단항 연산자의 우선순위가 이항 연사자보다 높다.
3. 단항 연산자와 대입 연산자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽이다.
산술변환
산술변환 (연산 직전에 발생하는 자동형변환)
- 이항연산자는 피연산자 타입이 일치해야 계산이 가능하다
1. 두 피연산자의 타입을 같게 일치시킨다.(보다 큰 타입으로 일치)
이때 두개의 피연산자 중 더 큰 타입으로 타입을 일치시키는데 그 이유는 작은 타입으로 계산할 경우
오버플로어가 발생해서 값에 손실이 발생할 수 있기 때문이다.
- 만약 타입이 다르면 두 피연산자 중 큰 타입으로 맞춘다.
- 큰 타입으로 맞춰야지 값 손실을 예방할 수 있다(넉넉하게)
//int = char + int -> int + int
int abc = 'a'+ 1;
//double= float + double -> double + double
double acs = 2.2f + 2.2;
//float = long + float -> float + float
float $ed = 100000000000L + 12.3f;
단, 쉬프트 연산자( >> , << ), 증감 연산자( ++ , -- )는 산술변환이 발생하지 않는다.
2. 피연산자가 int 보다 작으면(byte, short, char) int로 변환된다.
byte + short -> int + int -> int
char + short -> int + int -> int
계산식 중에 int로 바뀌는 이유는 byte와 short, char 같은 경우 연산 중 오버플로우가 쉽게 발생하기때문에
int 이하의 범위의 타입은 연산 중에 int로 다 변환된다.
* int / int 를 할때 소수점이 발생하면 소수점은 버림된다.
예) 10 / 3 = 3.3333..이면 int는 정수만 받기 때문에 아래에 있는 소수점은 버림되고 3으로 출력된다.
* 먄약 실수값을 얻고 싶으면 실수를 사용할 수 있는 타입을 넣어 산술변환할 수 있도록 해준다.
예) 10/ 3f = 3.3333333
int a1=10/3;
System.out.println("a1="+a1); // a1= 3
float a2=10/3f;
System.out.println("a2="+a2); // a2= 3.33333f
증감연산자 (++, --)
- 증감연산자는 정수, 실수 모두 사용할 수 있지만 상수는 안된다(상수는 바꿀 수 없는 수 final)
- (중요) 대부분의 연산자는 피연산자의 값을 읽어서 연산에 사용할 뿐, 피연산자의 타입이나 값을 변경시키지 않는다.
- 오직 대입연산자와 증감연산자만 피연산자의 값을 변경한다.
- 증감연산자(++) : 피연산자의 값을 1씩증가
- 감소연산자(--) : 피연산자의 값을 1씩 감소
- 증감연산자는 전위형, 후위형으로 나눌 수 있다.
타입 | 설명 | 예 |
전위형 | 값이 참조되기 전에 증사시킨다. | j = ++i; |
후위형 | 값이 참조된 후에 증가시킨다. | j= i++; |
* 증감 연산자가 독립적으로 사용된경 전위형과 후위형의 차이가 없다.
int i=0;
int j=i++; // i를 먼저 j에게 대입한 후 i를 1 증가 , j=0
j=--i; // j에게 i를 대입하기 전에 i를 1 감소 시킴 , j=0
부호연산자( + , - )
- 부호연산자는 산술연산자( + , - )와 다르다.
- 부호연산자의 + 는 양수, - 는 음수를 식별하는 것이다.
- 산술연산자의 + 는 덧셈, - 는 뺄샘이다.
형변환 연산자
형변환이란: 변수 또는 상수의 타입을 다른 타입으로 변환하는 것
(타입) 피연산자
float a =123.33f
int b = (int) a // 123
char a = 'a';
int b = (int) a //65
자동 형변환
컴파일러가 자동으로 형변환해주는 것을 자동 형변환이라고 한다.
1. 변수 > 리터럴 : 변수의 범위가 리터럴 범위보다 넓으면 자동형변환을 해준다.
float s = 12345; // 리터럴 값이 int 유형이다. 그러나 오류가 발생하지 않는다.
그 이유는 int보다 float가 더 범위가 크기 때문에 아래와 같이 자동 형변환을 한다.
float s = (float)12345;
그래서 변수 > 리터럴 이면 자동형변환 가능 한다.
2. 변수 < 리터럴 : 변수의 범위가 리터럴 범위보다 적으면 Error 발생한다.
int a = 3.231; // 에러 발생 리터럴 값이 double 유형이라 더 범위가 넓다.
이럴때는 자동 형변환이 되지 않기 때문에 강제 형변환을 해주어야한다.
왜냐하면 자동으로 형변환을 하게 되면 값손실이 발생할 수 있기 때문이다.
그래서 강제로 형변환을 해주어야한다.
변수가 리터럴 값의 범위보다 크면 아래와 같이 자동 형변환된다.
그래서 변수가 int면 int보다 범위가 적은 short, char, byte는 자동 형변환을 해준다.
단, short와 char은 자동 형변환이 되지 않는다.
그 이유는 short와 char는 표현할 수 있는 개수만 같다.
short는 음수도 표현하고, char는 양의 정수만 표현한다.
그래서 서로가 표현하는 값의 범위가 다르기 때문에 자동 형변환의 대상들이 아니다.
아래의 코드는 변수가 byte이고 리터럴이 12 인 int인데 에러가 발생하지 않는다.
그 이유는 컴퓨터가 12가 byte의 범위인 -128 에서 127 까지의 범위인걸 알고 있다.
byte a = 12;
그래서 위와 같이 자동형변환을 예외적으로 해준다.
여기서 예외적이라는 의미는 12값은 사실상 값은 byte 범위지만 int 타입의 리터럴이다.
그래서 byte보다 값의 범위가 넓은 int 타입의 리터럴이라도 해당 값이 byte 범위 안에 있으면 에러가 발생하지 않는다.
그래서 순수 값 12의 int타입의 리터럴을 넣었을떄 예외적으로 자동형변환을 해준다.
이는 short에도 동일하게 작용한다.
그러나, 직접 int 타입의 리터럴 값을 넣는 것이 아닌 int 변수를 넣으면 컴퓨터가 int 타입으로 인식하게되어 에러가 발생한다.
그래서 int 타입의 값이 byte 범위 안에 들어가도 int 변수로 직접 넣을 경우 컴퓨터가 int로 인식하게 되어 에러 발생
int i = 11;
byte d = i; // 에러, int타입을 byte 타입으로 대입
byte d = (byte) i; // OK.
byte가 리터럴 값이 아니라 숫자의 범위(-128 에서 127)가 넘어가면 에러가 발생함
(중요) 이때 강제 형변환을 하면 byte b에 값을 넣을 수 있지만 byte가 품을 수 있는 범위가 맞춰서 들어가기에
값 손실이 발생한다.
byte b = 333;
byte b = (byte)333;
아래의 코드는 int a , b를 곱한 값을 long 타입으로 받았는데도 음수가 나온다.
그 long 타입으로 연산 값을 받을 뿐이지 a * b 연산결과가 long 타입으로 바뀌는 건 아니다.
왜냐하면 계산식 안에 long 타입이 없고 최대로 큰 값이 int 타입이기에 계산된 결과는 int 타입으로 맞춰서 나오기 때문에
오버플로우 된 값 그대로 출력된다.
int a = 1_000_000;
int b = 2_000_000;
long c = a * b // -1454759936
그럼 위의 코드를 정확한 계산을 받기 위해서는 연산 중에 제일 큰 값인 long 타입을 추가해준다.
아래와 같이 a 값을 강제 형변환해서 long 타입으로 바꾼다. 그럼 최대범위인 long 타입으로 맞춰 연산한다.
int a = 1_000_000;
int b = 2_000_000;
long c = (long)a * b // 2000000000000
반올림 - Math.round()
실수를 소수점 첫째자리에서 반올림한 정수를 반환
double pi = 3.141592;
double shortPi = Math.round(pi*1000) / 1000.0;
System.out.println(shortPi) // 3.142
위의 코드에서 1000.0으로 나눈 이유는 Math.round(pi*1000) 정수이기에 정수/정수는 정수이기 때문에
나머지 소수점 값을 출력하지 않고 버린다.
우리가 출력하고자 하는 값은 3.142이고 소수점을 표현하기 위해서는 연산식 중에 double 타입이 필요하다.
자동형변환을 통해 double 타입으로 소수점 값도 출력할 수 있게 된다.
나머지 연산자 %
오른쪽 피연산자로 나누고 남은 나머지를 반환
나누는 피연산자는 0이 아닌 정수만 허용(부호는 무시됨)
System.out.println(11 % 4); // 11을 4로 나눈 나머지 3 출력됨
System.out.println(11 % -4) // 똑같이 3을 출력함. 즉 오른쪽 피연사자의 부호는 무시된다.
산술연산자(사칙연산자: +, - , * , / )
사칙연산자는 * (곱셈), / (나눗셈)이 + (덧셈), - (뺄셈) 보다 우선순위가 높다.
- 4 / 0 = 에러(0으로 4를 나눌 수 없기 때문에 에러가 발생한다)
- 부동소수점 0.0f, 0.0d로 결과는 무한값을 로 나온다.
- int / int = 5 / 3 = 1 (int로 계산할 경우 소수점은 버림이 된다)
- 즉, 내가 소수점 값을 표현하기 위해서는 산술변환을 하기 위해 해당 피연산자 타입을 double, float로 변경해준다.
-(중요) 올바른 연산결과를 얻기 위해서는 두 피연산자 중 어느 한 쪽을 실수형으로 형변환해야 한다. 그래야만 다른 한 쪽도 같이 실수형으로 자동 형변환되어 결국 실수형의 값을 결과로 얻는다.
- double / int = 5.0 / 3 = 1.666666...
byte q1=11;
byte q2=22;
byte ac=q1+q2; // 에러 발생 (int형 보다 범위가 작은 변수의 연산은 산술연산(자동형 변환)을 하기 때문임)
따라서, byte ac =(byte)(q1+q2) 해주어야한다. 만약 q1+q2 값이 byte 범위를 넘어가게 되면 오버플로우 발생되며 값이 손실된다. 그래서 연산시에는 넓은 범위를 기준으로 산술 변환되는 이유이다.
* 중요: 예를 들어, 계산 중에 int * int 의 값이 오버플로우가 되면 값 손실이 발생한다.
아래의 그 예시이다.
int a = 1000000;
int result1 = a * a / a;
1000000 * 1000000 / 1000000
-727379968(int와 int 계산 중 오버 플로우가 발생함) / 1000000
-727
int result2 = a / a * a;
1000000 / 1000000 * 1000000
1 * 1000000
10000000
문자는 실제로 해당 문자의 유니코드(부호없는 정수)로 바뀌어 저장되므로 문자간의 사칙연산은 정수간의 연산과 동일하다.
('2' - '0') -> (50 - 48) -> 2
아래의 코드는 ++a는 에러가 발생하지 않는다
(그 이유는 ++ / -- (증감연산자)는 타입을 형변환하지 않아도 계산할 수 있기 때문이다.)
아래의 코드에서 a+1 에러가 발생하는 이유는 a가 리터럴 값이 아닌 char형의 변수이기 때문에 형변환이 되서 큰 범위인 int로 산술변환되기 때문이다.
char a = 'a';
char c = ++a; // OK
char d = a+1; // 에러 발생
문자 리터럴과 정수 리터럴 연산
char ch='c'+'b'+1 이렇게 했을때 char 타입('c', b')은 int 보다 작기 때문에 문자 리터럴은 int로 산술 변환되고 char ch는 int 값보다 범위가 작기 때문에 int 값을 담을 수 없어 에러가 생긴다고 생각할 수 있다
(char ch = char + char + int -> char ch= int + int + int). 하지만 에러는 발생하지 않는다.
(중요) 그 이유는 자바에서 문자 리터럴과 정수 리터럴은 합치면 자동으로 문자 리터럴로 형변환(char)해주기 때문이다.
문자리터럴 = 문자 리터럴 + 정수 리터럴(문자리터럴로 자동 형변환)
컴파일러가 미리 덧셈연산을 수행하기 때문에 실행 시에는 덧셈 연산이 수행되지 않는다.ㅋㅌ
char ch='c'+'b'+1;//문자 리터럴 + 정수 리터럴 연산하면 자동으로 char 형태로 항변환해준다.
컴파일러가 미리 덧셈연산을 수행하기 때문에 실행 시에는 덧셈 연산이 수행되지 않는다.
그래서 컴파일 에러가 발생되지 않는다. 코드가 실행전에 이미 덧셈을 완료해서
실행 후에는 char c1 ='b' 이기 때문이다.
컴파일 전의 코드 | 컴파일 후의 코드 |
char c1 = 'a' + 1 | char c1 = 'b' |
수식에 변수가 들어가 있는 경우에는 컴파일러가 미리 계산을 할 수 없기 때문에 아래의 오른쪽 코드와 같이 형변환을 해주어야 한다.
즉, 리터럴의 연산이면 해당 값을 코드 실행되기 전에 연산을 미리 수행할 수 있지만, 연산식에 변수가 들어가면 미리 계산할 수 가 없기 때문에 형변환해주는 것이다.
비교연산자
(중요) 비교연산자도 이항연산자(피연산자 / 연산자 / 피연산자)이기 때문에 피연산자 다를 경우 자료형 범위가 넓은 타입으로 자동형 변환 후 비교한다
1. 대소비교 연산자(< , > , <= , >=)
- 사용범위: 기본형(boolean 제외 모든 유형), 참조형(X)
2. 등가비교 연산자( == 두 값이 같은지 , != 두 값이 다른지)
- 사용범위: 기본형, 참조형(객체 주소 비교) 모두 가능
- 기본형: 값 비교
- 참조형: 객체의 주소값 비교
* 비교 연산 사용시 중간에 공백이 있으면 안됨(예: > =, = =, ! =)
3. 이항 연산자이기에 연산을 수행하기 전에 형변환함.
- 100.12f == 100.12
- float == double
- double == double
- 100.12 == 100.12
- true
4. 실수형 등가 비교(float / double)
실수형 등가비교시 보이는 값이 같아도 정밀도가 고려된 실제 값은 같을 수도 다를 수도 있다.
//실수형 등가비교
float b1=30.0f;
double b2=30.0d;
System.out.printf("b1=%19.17f%n",b1); // b1=30.00000000000000000
System.out.printf("b2=%19.17f%n",b2); // b2=30.00000000000000000
System.out.println("b1=b2: "+(b1==b2)); // true
float b3= 33.3f;
double b4= 33.3d;
System.out.printf("b3=%19.17f%n",b3); //b3=33.29999923706055000
System.out.printf("b4=%19.17f%n",b4); //b4=33.30000000000000000
System.out.println("b3=b4: "+(b3==b4)); //false
위에 보이는 코드를 구체적으로 설명해보면...
b1,b2는 타입은 다르고 보이는 값은 각각 30.0f, 30.0d 이다.
이 값의 구체적은 값을 알아보기 위해 소수점 17번째까지 출력했을때
똑같이 30.000000000000 나왔다. 이 둘은 double, float 변수로 정밀도가 각각 15자리, 7자리라서 실제 값이 다를 수 있지만 같을 수도 있다. 하지만 30.0f, 30.0d 경우에는 실제 값이 같게 나왔다. b1==b2는 true이다.
반면,
b3,b4는 33.3f 33.3d는 표면적인 값은 같아 보여도 실제 소수점 17번째까지 출력했을때는
float b3은 33.29999923706055000
double b4는 33.30000000000000000
나왔다. 즉, 해당 타입들의 정밀도가 다르기 때문에 2진수로 변환하는 반올림하는 과정에 오차발생으로 인한 실제 값이 다를 수 있고 같을 수 있다.
이를 고려했을때 33.3f과 33.3d 실제 값은 상이하게 출력될 수 있다.
따라서 b3, b4는 b1, b2와 달리 서로의 값이 다르게 출력되었고 b3==b4는 false이다.
이를 통해 우리가 알아야될 부분은 ............
1. float와 double은 정밀도와 용량이 다르다(float : 정밀도 7자리, 4byte , double : 정밀도 15자리, 8byte)
2. 메모리 용량이 부담이 되어도 정확한 값을 원하면 double 타입, 정확한 값은 아니지만 메모리를 적게 차지하고 싶으면 float 생각해 볼 수 있다.
3. float와 double은 정밀도가 다르기 때문에 2진수로 변환하고 반올림하는 과정에서 발생한 오차로 인해 실제 값이 같을 수도 다를 수도 있다. 따라서 float, double 변수의 표현 값이 똑같아 보여도 실제 값은 다를 수도 or 같을 수도 있다(주의)
*표현 값(개발자에게 보여지는 값) : 예시) float a1=30.0d에서 30.0d를 말함.
*실제 값(소수점 17번째까지 고려한 값) printf("%19.17f", a1) // a1 = 30.00000000000000000
5. 문자열 비교
(중요) 문자열을 비교할때는 == 보다는 equals
String 객체이기 때문에 == 사용할때는 객체의 주소값을 비교하는 반면,
equals 메소드는 객체의 주소값이 아닌 값을 비교한다. 그리고 같으면 true, 다르면 false를 반환함.
String ab1="abc";
String ab2="abc";
System.out.println("ab1==ab2: "+(ab1==ab2)); //ab1==ab2: true
System.out.println("ab1.equals(ab2): "+(ab.equals(ab2)) //ab1.equals(ab2): true
String ab3=new String("abc");
String ab4=new String("abc");
System.out.println("ab3==ab4: "+(ab3==ab4)); //ab3==ab4: false
System.out.println("ab3.equals(ab4): "+(ab3.equals(ab4)) //ab3.equals(ab4): true
문자열 상수 (String literals)는 Java에서 특별히 처리되는 방식이 있습니다.
일반적으로 문자열 리터럴은 동일한 값을 가지는 경우 하나의 인스턴스를 생성하여 메모리를 공유합니다(같은 주소를 공유함)
이것은 "문자열 풀 (string pool)"이라고도 불리는 메모리 영역에 저장됩니다.
따라서, 아래의 코드에서:
String ab1 = "abc";
String ab2 = "abc";
(중요) ab1과 ab2는 동일한 문자열 "abc"를 가리키는 참조 변수가 됩니다(ab1, ab2가 참조하는 인스턴스 주소 값이 같다.)
이들은 같은 주소 값을 가리키기 때문에, ab1 == ab2 비교는 true를 반환합니다.
하지만 주의해야 할 점은, 이 동작은 문자열 리터럴에 대해서만 적용됩니다.
즉, ab1과 ab2가 같은 문자열 리터럴을 가리키는 경우에만 메모리가 공유됩니다.
다음과 같이 객체 생성을 통해 문자열을 만들면 메모리 공유가 일어나지 않습니다:
String ab3 = new String("abc");
String ab4 = new String("abc");
위의 경우 ab3과 ab4는 서로 다른 주소 값을 가리키게 됩니다.
(중요) new 연산자를 사용하여 문자열을 생성하면 문자열 풀(string pool)에 저장되지 않기 때문에, 각각 다른 인스턴스를 생성하게 됩니다.
따라서 ab3 == ab4 비교는 false를 반환합니다.
(중요)요약하자면, 문자열 상수 (String literals)에 대해서는 메모리 공유가 일어나며, 같은 리터럴을 가리키는 문자열 변수들은 동일한 주소 값을 가지게 됩니다. 그러나 new를 사용하여 객체를 생성하면 문자열 풀(string pool)에 저장되지 않으며 항상 새로운 인스턴스가 생성됩니다.
*대소문자 구별없이 비교할때에는 equalsIgnoreCase() 사용하면 됩니다.
논리연산자(&&, || , !)
&&(and), ||(or), !(not)
1. &&는 양쪽 값 둘다 true면 true
- x > 0 && x < 10 --> true && true --> (true) , false && true --> (false)
- 첫번째 비교연산자가 false이면 두번째 비교연산자는 비교하지 않음
- 속도를 빠르게 하려면 첫번째 비교연산자가 false일 확률이 높은 것을 선택한다.
2. ||(or)은 한쪽만 true 이면 true
- ( true || false --> true) , (false || true --> true) , (false || false --> false)
- 이것도 마찬가지로 첫번째 비교연산자 항을 true로 설정하면 첫번째 비교연산자가 true면 뒤에 항을 거치지 않기 때문에 속도가 빨라짐.
3. &&가 ||보다 우선순위 높음
- 예를들면, x%7==0 || x%4==0 && x%28==0 이렇게 있을때 (괄호) 를 체크하지 않아도 아래와 같이 && 먼저 연산함
x%7==0 || (x%4==0 && x%28==0)
- 내가 만약 || 관련된 것부터 연산하고 싶으면 아래와 같이 || 주변 항을 괄호()로 묶어주면 됨
(x%7==0 || x%4==0) && x%28==0
4. 논리 부정 연산자 !
! 사용하면 반대로 인지하게 됨
단항 연산자는 오른쪽에서 왼쪽으로 순서로 계산됨
!false -> true
!!false -> !true -> true
!!!true -> !!false -> !true -> false
비트 연산자(&, | , ^ , ~ , << , >>)
피연산자로 실수는 허용하지 않는다. 정수(문자 포함)만 허용한다.
| (OR연산자) : 피연산자 중에 하나라도 1이면 , 1 얻고 아니면 0을 얻는다.
& (AND연산자) : 피연산자 중 둘다 1이면, 1을 얻고 아니면 0을 얻는다.
^ (XOR 연산자) : 피연산자 중 서로 다르면 1, 서로 같으면 0을 얻는다.
~ (비트 전환 연산자) : 2진수로 변환했을때 1은 0으로 0은 1로 바꾼다. ! 논리부정연산자 기능과 유사함
그래서 비트 연산자는 '1의 보수' 연산자라고도 한다.
6. << , >> (쉬프트 연산자)
- 2진수로 표현했을때 <<(왼쪽으로), >>(오른쪽으로) 이동한다.
- 좀 더 구체적으로 설명하면, 16 >> 4 일땐 16을 2진수로 바꾸면 00010000 2의 4승 자리로 갈 수 있다.
- 00010000 여기서 오른쪽으로 4칸 줄어들고 앞 부분은 0으로 채운다
- 즉, 00000001 이렇게 된다.
- 음수일때는 0 대신 1로 채운다. 앞자리의 1은 음수를 나타내기 때문이다.
예를들면, -16 >> 2 는 11000100 표현할 수 있다.
- 그 와 반대로 16 << 2 는 왼쪽으로 2칸 이동한다.
- 00010000 -> 01000000 이렇게 이동되며 왼쪽으로 이동하며 0을 채워간다.
- 쉬프트 연산자는 피연산자 간의 산술변환을 필요가 없는 구조이다.
예를 들면 10 >> 2 일때 여기서 말하는 2는 단지 몇번 이동하는지를 말한다.
- 쉬프트는 곱셈,나눗셈보다 속도는 빠르나 가독성이 떨어짐으로 가독성보다 속도를 요구할때 사용되는 것이 좋다.
x << n은 x * 2^n의 결과와 같다.
x >> n은 x / 2^n의 결과와 같다.
<< 연산자를 사용하는 것이 나눗셈(/) 또는 곱셉(*) 연산자를 사용하는 것 보다 더 빠르다.
프로그램의 실행 속도도 중요하지만 프로그램을 개발할 때 코드의 가독성도 중요하다.
쉬프트 연산자보다 곱셈 또는 나눗셈 연산자를 주로 사용하고, 보다 빠른 실행속도가 요구되어지는 곳만 쉬프트 연산자를 사용하는 것이 좋다.
조건연산자
- 조건식 ? 식1 : 식2 (삼항연산자에 속한다)
- 참이면 식1이 도출되고 거짓이면 식2가 도출한다.
- 식1, 식 2 이 두 피연산자의 타입이 다른 경우, 이항 연산자처럼 산술 변환이 발생하다.
예를 들면, int abc = x > y ? x : y 일 때 abc의 값은 x가 y보다 크면 x를 작으면 y를 도출한다.
- 조건 연산자의 결합규칙은 오른쪽에서 왼쪽이다. 왼쪽 <------------ 오른쪽
int abc = x > y ? x : ( x==y ? z : y ) 일때 오른쪽인 ( x==y ? z : y ) 계산한다.
가독성을 높이기 위해 () 괄호를 사용했지만 결합규칙이 오른쪽에서 왼쪽이기에 괄호를 붙이지 않아도 동일하게 동작한다.
대입 연산자( = ,+=, *= , -= , /=)
lvalue(left value) : 대입 연사자의 왼쪽 피연산자(저장할 수 있는 공간이여야한다. 예: 변수, 배열 등)
rvalue(right value) : 대입 연산자의 오른쪽 피연산자
x = 3
x(lvalue) = 3(rvalue)
- 대입 연산자는 변수에 해당값을 저장하는데 사용되며, 연산자 중에 우선순위가 가장 낮다.
- 연산 진행 방향은 오른쪽에서 왼쪽이다. 왼쪽 <--------- 오른쪽
- 예를 들면, x=g=e=1이면 e=1 대입되고 그 후 g=1 , x=1순으로 오른쪽에서 왼쪽으로 대입된다.
복합 대입 연산자(+=, *= , -= , /= 등)
예시를 들어 설명하면,
i += 1 --> i = i+1 이 둘이는 같은 의미를 뜻한다.
따라서,
i -= 1 --> i = i - 1
i *= 2 --> i = i * 2
i %= 2 --> i = i % 2
i ^= 10 --> i = i ^ 10
i >>= 2 --> i = i >> 2
이와 같이 볼 수 있다
(중요) i *= 5 + k + j --> i = i * (5 + k + j) 이다. 즉 5 + k + j 를 먼저 연산한 뒤에 i 를 곱하는 형식이다.
i = i * 5 + k + j 이와 같이 해석 하면 안된다(주의).
본 글은 [자바의 정석 3판 - 저자 남궁성] 의 책을 참고해서 작성했다.
해당 책에 더 자세한 내용을 볼 수 있다.
혹여나 틀린 부분이나 궁금한 부분 있으시면 댓글 달아주세요.
'java' 카테고리의 다른 글
[java] 배열 (0) | 2023.08.12 |
---|---|
[java] 조건문, 반복문 (0) | 2023.08.10 |
[정규표현식과 Pattern] (0) | 2023.03.14 |
[Java: 변수] (0) | 2023.03.08 |
[Java 의 특징] (0) | 2023.03.06 |
댓글