자바의 정석

[자바의 정석] ch3. 연산자

주니어주니 2022. 8. 11. 18:51

1. 연산자와 피연산자

 

연산자: 연산을 수행하는 기호 ( +, -, *, / )

피연산자: 연산자의 연산 수행 대상 ( 변수, 상수, 리터럴, 수식 )

( x + 3 에서 x,3은 피연산자, +는 피연산자 )

 

 “모든 연산자는 연산결과를 반환한다.” ⇒ 연산결과를 반영하지 않으면 연산자가 아니다.

 


2. 연산자의 종류

 

종류 연산자 설명
산술 연산자 +   -    *    /    %    <<    >> 사칙 연산과 나머지 연산
비교 연산자 >  <  ≥  ≤  ==  ≠ 크고 작음, 같고 다름
논리 연산자 &&(and)    ||(or)   !(not) '그리고(&&)’와 ‘또는(||)’
대입 연산자 = 값을 좌변에 저장
기타 (type)   ?:   instanceof 형반환 연산자(type), 삼항 연산자(?:),
instanceof연산자

 


3. 연산자의 우선순위

하나의 식(expression)에 연산자가 둘 이상 있을 때, 어떤 연산을 먼저 수행할지를 자동 결정하는 것 ( 만약 먼저 연산하고 싶을 때는 괄호로 묶기 )

 

설명
-x + 3 단항 연산자(피연산자가 1개) 이항 연산자(피연산자 2개 )보다 우선순위가 높다. 그래서 x의 부호를 바꾼 다음 덧셈 수행.
여기서 ‘-’는 뺄셈 연산자가 아니라 부호 연산자.
   단항    >   이항
x + 3 * y 곱셈, 나눗셈 ( * / )     >     덧셈, 뺄셈 ( + - )
x + 3 > y - 2       산술 ( + - * / )     >     비교 ( < > )       
x > 3  &&  x < 5        비교 ( < > )       >      논리 ( &&  || )
result = x + y * 3; 대입 연산자는 연산자 중 우선순위가 가장 낮음.
x  <<  2 + 1                           산술 ( + - * / )      >     쉬프트 연산자 ( <<  >> )     
 'x << (2+1)'
data & 0xFF == 0    비교 연산 ( == )     >     비트 연산 ( & )
'data & (0xFF==0)'
x < -1   ||   x > 3   &&   x < 5 AND (&&)      >    OR (||)    

 

4. 연산자의 결합규칙

 

우선순위가 같은 연산자가 있을 때, 어떤 것을 먼저 계산할 것인가?

  • 대부분 왼쪽 → 오른쪽
  • 예외)   오른쪽 → 왼쪽  ( 대입, 단항 연산자 )

 

※ 연산자의 우선순위, 결합규칙

1. 산술( +, - )  >  산술( <<, >> )  >  비교( <, > )  >  비교( ==, != )  >  논리( &&, || )  >  삼항( ? : )  대입( = )

2. 단항(1) > 이항(2) > 삼항(3)

3. 단항 연산자와 대입 연산자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽

 


 

5. 증감 연산자 ++ -- (단항 연산자)

 

증가 연산자(++) : 피연산자의 값을 1 증가시킨다
감소 연산자(- -)  : 피연산자의 값을 1 감소시킨다

 

전위형 값이 참조되기 전에 증가 j = ++i;
후위형 값이 참조된 후에 증가  j = i++;

 

 

1)  '++i;' 와 'i++;'처럼 증감연산자가 수식이나 메서드 호출에 포함되지 않고 독립적인 하나의 문장으로 쓰인 경우
     : 전위형과 후위형의 차이가 없다. 

  • ++i;        // i의 값을 1 증가시킨다. 
  • i++;        // i의 값을 1 증가시킨다. 

 

2) 'j = ++i;' 같이 다른 연산자와 같이 쓰여 다른 수식에 포함되거나 메서드의 매개변수로 사용되는 경우

     : 전위형과 후위형의 결과가 다르다. 

    : 증가 연산자의 전위형은 값이 먼저, 후위형은 값이 나중에 증가

  • i=5, j=0; 일 때 

  • j = ++i;                    // i=6, j=6
    1) i의 값을 증가
    2) i의 값을 참조
    3) 연산결과를 j에 저장

  • j = i++;                   // i=6, j=5
    1) i의 값을 참조
    2) 연산결과를 j에 저장
    3) i의 값을 증가
 
* 증감 연산자가 포함된 식을 이해하기 어려울 때는 증감 연산자를 따로 떼어내면 이해가 쉬워짐

전위형의 경우, 위로 빼기
   j = ++i;  —>  ++i;       // 증가 후에
                           j = i;      // 참조하여 대입 
후위형의 경우, 밑으로 빼기
   j = i++;  —>  j = i;      // i를 먼저 참조하여 j에 대입 후에
                           i++;      // 증가

 


6. 부호 연산자 + -  (단항 연산자)

 

-’  : 피연산자의 부호를 반대로 변경

+’ : 아무 일도 하지 않는다. (실제 사용X)

-’,’+’ : 단항 연산자 = 피연산자가 1개

 


7. 형변환 연산자

 

형변환(形 type) : 변수 또는 상수의 타입을 다른 타입으로 변환하는 것

바꾸는 법 : (타입) 피연산자

  • double d = 85.4;       // 실수인데 정수로 바꾸고 싶어
    int score = (int) d;
    → int score = (int) 85.4;
    → int score = 85;     // *읽어온 값만 85로 바뀐거지 d의 값은 여전히 85.4

 

변환 수식 결과
int → char (char) 65 'A'
char → int (int) 'A' 65
float → int (int) 1.6f 1
int → float (float) 10 10.0f

 

<유니코드 문자표> (정수 ↔ 문자)

https://devlog-wjdrbs96.tistory.com/76

 

* 유니코드 
'0' = 48 

'A' = 65
'a' = 97

 


 

8. 자동 형변환 (형변환을 생략해도 자동으로 형변환 해줌)

 

자동 형변환
형변환을 하는 이유는 서로 다른 두 타입을 일치시키기 위해서인데,
형변환을 생략하면 컴파일러가 자동적으로 형변환 함.

 

 

float f = 1234;                  // OK 형변환의 생략 ( int<float 라서 가능 )  (=(float)1234;)

float f = (float) 1234;    // 자동 형변환 (컴파일러가 자동으로 타입이 일치하게 형변환 해줌)

 

int i = 3.14f;                     // 에러 ( int<float 라서 자동 형변환이 안됨. 값 손실 발생 → 수동으로 적어야 함 (명시적 형변환))

int i = (int) 3.14f;           //ok 수동 형변환

 

1) byte → int ( int의 범위 내에 있으면 가능 )

     byte b = 10;

     int i = b;                            // byte(1byte), int(4byte) → 생략가능(=자동 형변환)

 

2) int → byte

     int i2 = 300;

     byte b2 = (byte) i2;      // int(4byte)를 byte(1byte)에 담으려면 값손실 발생할 수도 있음 → 생략불가 (=수동 형변환)

  • 예외)
    byte b = 100;                 // OK. byte타입의 범위(-128 ~ 127) 의 값 대입
    byte b = (byte) 100;    // OK. byte타입으로 자동 형변환
    (큰값(int)→ 작은값(byte)이지만, 상수(리터럴)을 직접 넣은 거고 byte의 범위 내에 있기 때문에 자동으로 형변환 가능)

  • 예외)
    int i = 100;
    byte b = i;                 // 에러. 위에는 상수를 직접 입력했지만, 얘는 변수라서 무슨 수가 올지 모르기 때문에 수동 형변환 필요.
    byte b = (byte) i;   // OK. 

 

자동 형변환이 되는 범위

                                                  정수형 ←|→ 실수형
   byte   →  short   →    int   →   long    →  float     → double
(1 byte)     (2 byte)     (4 byte)    (8 byte)     (4 byte)      (8 byte)
                   char   ——↑
                
* shortchar는 둘다 2 byte지만, 범위가 달라서 자동형변환 X 
   short의 범위: ±3만 
   char의  범위: 0~6만
  

 


9.  사칙 연산자 ( + - * / )

사칙 연산 + - * / 중

10 (int) / 4 (int)  =  2 (int)

원래는 2.5이지만, int / int = int 가 나와야 하기 때문에 소수점 이하는 버려짐

이때, a / b 둘중 하나만 (float)로 바꿔주면 답도 실수형(float)로 나옴

10 (int) / 4.0f (float)  →  10.0f (float) / 4.0f (float)  →  2.5f (float)

 

class OperatorEx5 { 
 public static void main(String args[]) { 
 	int a = 10; 
   	int b = 4;
    
    System.out.printf("%d / %d = %d%n", a, b, a/b);                 // 10 / 4 = 2
    System.out.printf("%d / %f = %f%n", a, (float)b, a/(float)b);   // 10 / 4.000000 = 2.500000

 

* 피연산자가 정수형인 경우, 나누는 수로 0을 사용할 수 없다. 
* 0.0f, 0.0d로 나누는 것은 가능하지만 그 결과는 무한대

 

  • System.out.println(3/0);        // 실행하면, 오류(ArithmeticException) 발생 !
  • System.out.println(3/0.0);     // Infinity가 출력됨

  • byte a = 10;
    byte b = 20;
    byte c = a + b;
    System.out.println(c);         // 에러. 명시적 형변환 필요 ( int를 byte로 변환하니까 )
    byte c = (byte) (a+b);      // 로 수정해야 함
    System.out.println(c);   // 30 

  • byte a = 10;
    byte b = 30;
    byte c = (byte) (a * b);
    System.out.println(c);      // 44 출력
    → 10*30의 결과는 300이지만, 300은 byte형의 범위를 넘어서 데이터 손실 발생.
          int (4byte)에서 byte (1byte)로 변환하는 것은 2진수 32자리(4*8bit)에서 8자리(1*8bit)로 변환하는 것
        상위 24자리를 잘라내면 값이 잘려버림 -> 300의 2진수(~100101100)에서 44의 2진수(00101100)로 바뀌어버림

  • int a = 1_000_000;
    int b = 2_000_000;
    long c = a * b;
    System.out.println(c);          // -1454759936 출력
    → long타입이 a*b의 결과를 저장하기에 충분하므로 잘 출력될 것 같지만,

        연산결과는 int타입이고, 'a*b'의 결과가 이미 int타입의 값의 최대값인 약 2*10^9을 넘으므로 오버플로우 발생.
        long타입으로 형변환 되어도 값이 변하지 않음.
    long c = (long)a * b;         // 변수 a 또는 b의 타입을 long으로 형변환해야 함.
    → System.out.println(c);   // 2000000000000L 출력

  • long a = 1_000_000 * 1_000_000;
    long b = 1_000_000 * 1_000_000L;
    System.out.println("a="+a);          // -727379968 출력 (오버플로우 발생)
    System.out.println("b="+b);          // 1000000000000 출력 (long타입 형변환)

  • char c1 = 'a';
    char c2 = c1+1;                 // 에러.
    char c2 = (char) c1+1;   // ok. 이거로 바꾸면 됨.
    → 수식에 변수가 들어가 있는 경우, 컴파일러가 미리 계산을 할 수 없기 때문에 형변환을 해줘야 함.

    char c2 = 'a'+1;                // ok. b 출력
    → 왜 형변환을 하지 않아도 char타입 b로 출력될까?

    : 리터럴 간의 연산이기 때문.
     상수 또는 리터럴 간의 연산은 산술 계산을 하는 것처럼 컴파일러가 미리 덧셈연산을 수행하기 때문에
     실행 시에는 덧셈 연산이 수행되지 않음. 덧셈연산결과인 문자 'b'를 변수 c2에 저장할 뿐.  

  • 문자 출력
    char c = 'a';
    for (int i=0; i<26; i++) {
    System.out.print(c++);      // abcdefghijklmnopqrstuwxyz
    → 'a'+1처럼 덧셈연산을 하는게 아니라 그냥 26개의 문자를 출력하는거니까 형변환x 

  • 소문자 -> 대문자 출력
    char lowerCase = 'a';
    char upperCase = (char) ('a'-32);
    System.out.print(upperCase);          // 'A' 
    * char형과 int형의 뺄셈연산 결과는 int형이므로, 연산 후 char형으로 다시 형변환 필수 ! 

  • 나눗셈 연산자를 이용해 소수점 셋째 자리까지만 빼내는 법 
    float pi = 3.141592f;
    float shortPi = (int) (pi*1000) / 1000f;    
    System.out.println(shortPi);           // 3.141 
    → 1) (pi*1000) = 3141.592f  : pi가 float이고 1000이 int니까 결과는 float 
         2) (int) (pi*1000) = 3141  : 연산 결과를 int로 형변환 (소수점 이하를 버리기 위함
         3) (int) (pi*1000) / 1000f  : int와 float 연산이므로, int가 float로 변환된 다음, float간 연산 수행
         4)  3.141f 

  • math.round 메서드 없이 소수점 넷째자리에서 반올림 하는 법
    double pi = 3.141592;
    double shortPi = (int) (pi*1000 + 0.5) / 1000.0
    System.out.println(shortPi);           // 3.142

    → 1) (pi*1000 + 0.5) = 3141.592 + 0.5 = 3142.092
         2) (int)(pi*1000 + 0.5) = 3142

         3) (int)(pi*1000 + 0.5) / 1000.0  : int와 double의 연산이므로 double로 변환됨 (1000f는 float) 

 


10. 산술변환

연산 전에 피연산자의 타입을 일치시키는 것

 

1. 두 피연산자의 타입을 같게 일치시킨다. ( 큰 타입으로 일치(값 손실을 막기 위해))
long (8) + int (4)            →       long (8) + long (8)             → long (8)
float (4) + int (4)           →       float (4) + float (4)             → float (4)
double (8) + float (4)  →       double (8) + double (8)    double (8)


2. 피연산자의 타입이 int보다 작은 타입이면, int로 변환된다. (byte, char, short)
byte (1) + short (2) → int + int → int
char (2) + short (2) → int + int → int

  byte : -128~127
  char : 0 ~ 6만
  short : ±3만             ⇒ 조금만 계산해도 범위가 넘어서 오버플로우 발생 

 

ex)

‘2’ (char) - ‘0’ (char)  ⇒  50 (int) - 48 (int) ⇒  2

‘0’=48  ~  ‘9’=57 인데,

‘2’ - ’0’  =  50 - 48  =  2   ⇒  (문자에서 숫자로 변환되게 숫자가 설정되어 있음)

‘3’ - ’0’  =  51 - 48  =  3

 

예제)

int a = 1_000_000;      // 1백만 = 10의 6제곱

int b = 2_000_000;     // 2백만 = 10의 6제곱

long c = a * b                // -1454... 음수가 나와버림 → 오버플로우 발생

a * b = 10의 12제곱.  int의 범위는 10의 9제곱. ( int 연산 결과가 int의 범위를 넘어버림 )

그래서 long에 저장하려고 했지만 a*b의 값은 int임.

⇒ long c = (long) a*b 혹은 둘 중 하나에 형변환을 해줌

 


11. 반올림 Math.round()

실수를 소수점 첫째자리에서 반올림하고 그 결과를 정수로 반환

 

long result = Math.round(4.52);          // result = 5

 

  • 소수점 넷째자리에서 반올림 하고 싶을 때
    double pi = 3.141592;
    double shortPi = Math.round(pi*1000)/1000.0; 
    System.out.println(shortPi);                             // 3.142
    Math.round( pi * 1000 ) / 1000.0                 // pi * 1000 곱해줌 = 3141.592

    → Math.round( 3141.592 ) / 1000.0                // 여기서 소수점 첫째자리에서 반올림 후 정수반환 = 3142
    → 3142 / 1000.0                                                     // 1000.0으로 나눠줌!!!  (int / double)
    → 3.142                                                                     // double
    ( 만약, 1000.0 이 아니라 1000(int)로 나눴다면 3.142 가 아니라 3 (int) 가 나옴 )


  • 3.141을 얻고 싶을 때
    double pi = 3.141592;

    double shortPi = (int) (pi*1000) /1000.0 ;
    System.out.println(shortPi);          // 3.141
    pi * 1000                                            // 3141.592

    (int) (pi * 1000)                               // 3141 ( (int)pi*1000 로 하면 3000이 나옴. 괄호 씌우기)
    → (int) (pi * 1000) / 1000.0             // 실수형으로 반환하기 위해 1000.0 으로 나눠주기



12. 나머지연산자 %

왼쪽 피연산자를 오른쪽 피연산자로 나누고 남은 나머지를 반환

주로 짝수, 홀수 또는 배수 검사 등에 주로 사용

 

10 / 8    → 1  (몫)

10 % 8  → 2  (나머지)

 

 

나누는 수는 0을 사용할 수 없다! 0이 아닌 정수만 허용

나누는 수로 음수도 허용됨. 그러나 부호는 무시.

피연산자의 부호는 모두 무시하고, 결과값에 왼쪽 피연산자(나눠지는 수)의 부호를 붙이면 된다.

 

-10 % 8    → -2

 10 % -8    →  2

-10 % -8   -2

 

public class ScannerEx { 

	public static void main(String[] args) {
		int x = 10;
		int y = 8;
		
		System.out.printf("%d을 %d로 나누면, %n",x,y);
		System.out.printf("몫은 %d이고, 나머지는 %d입니다.",x/y,x%y);
		
		}
	}

 


13. 비교 연산자

두 피연산자를 비교해서 true 또는 false 를 반환

비교 연산자도 이항 연산자이므로 형변환을 통해 두 피연산자의 타입을 같게 맞춤! 

 

비교 연산자 결과 
> 좌변이 크면 true, 아니면 false
< 좌변이 작으면 true, 아니면 false
>= 좌변이 크거나 같으면 true, 아니면 false
<= 좌변이 작거나 같으면 true, 아니면 false
== 두 값이 같으면 true, 아니면 false
!= 두 값이 다르면 true, 아니면 false

 

  • System.out.printf( "10 == 10.0f       \t    %b%n",      10 == 10.0f );                     // true
    → 10       == 10.0f      (int타입과 float타입을 float타입으로 맞춰줌) 
    → 10.0f  == 10.0f
    → true


  • System.out.printf( "'0' == 0               \t    %b%n",      '0' == 0 );                            // false
    → '0' == 0
    → 48 == 0
    → false
    (int 보다 작은애들은 int로 변환됨. 범위가 작아서 오버플로우 방지 )

  • System.out.printf( "'A' == 65              \t    %b%n",     'A' == 65 );                         // true
    → 'A' == 65
    → 65 == 65
    → true

  • System.out.printf( "'A'  >  'B'              \t    %b%n",     'A'  >  'B' );                         // false
    → 'A'  >  'B'
    → 65  >  66
    → false

  • System.out.printf( "'A' + 1  !=  'B'      \t    %b%n",     'A' +1  !=  'B' );                  // false
    → 'A' + 1  !=  'B'
    → 65 + 1  !=  66
    → false

  • 실수형의 경우
    float       f     =   0.1f;

    double   d    =   0.1;
    double   d2  =   (double) f;  일 때

    • System.out.printf( "10.0 == 10.0f       %b%n",      10.0 == 10.0f );                     // true
      → 10.0f는 오차없이 저장할 수 있는 값이라서 double로 형변환 해도 그대로 10.0 
      정밀도가 달라도 .0의 경우는 소수점 뒤가 0이기 때문에 float형과 double형이 같다. 
      → true

    • System.out.printf( "0.1 == 0.1f             %b%n",     0.1 == 0.1f );                         // false
      → 정밀도 차이로 오차 발생
      → float       f = 0.1f;     // f에 0.100000000149011612 저장
           double  d = 0.1;     // d에 0.100000000000000001 저장 
      float를 double로 형변환하면 32bit에서 64bit로 늘어난 빈자리를 0으로 채울 뿐이므로, 
          값은 전혀 변하지 않는다. 

    • System.out.printf( " f  = %19.17f%n",    f );                         // f = 0.10000000149011612

    • System.out.printf( " d = %19.17f%n",    d );                       // d = 0.10000000000000000

    • System.out.printf( " d2 = %19.17f%n",    d2 );                  // d2 = 0.10000000149011612

    • System.out.printf( " d == f           %b%n",     d == f );                         // false
      → d == (double) f 
      0.10000000000000000 == (double) 0.10000000149011612
      0.10000000000000000 ==0.10000000149011612
      false

    • System.out.printf( " d == d2       %b%n",     d == d2 );                      // false
      → d == d2 
      0.10000000000000000 == (double)0.10000000149011612
      0.10000000000000000 ==0.10000000149011612
      false

    • System.out.printf( " d2 == f         %b%n",     d2 == f );                       // true
      → double d2 == (double) f ;      // f의 값이 d2에 저장
      0.10000000149011612 == 0.10000000149011612
      false

    • float과 double의 값을 비교하려면? 
      System.out.printf( " (float)d == f      %b%n",     (float) d2 == f );                       // true

      double을 float으로 형변환 한다. 
       (float) d == f
      → (float) 0.10000000000000000 == 0.10000000149011612
      0.10000000149011612 == 0.10000000149011612
      true

14. 문자열의 비교

문자열 비교에는 비교 연산자 == 대신 equals()라는 메서드 사용해야 함

비교 연산자는 두 문자열이 완전히 같은 것인지 비교할 뿐, 문자열의 내용이 같은지 비교하기 위해서는 equals() 사용

 

원래 String은 클래스이므로, new를 사용해서 객체를 생성해야 함 (∵클래스는 객체를 생성해야 함) 
String str = new String("abc"); 
근데 String만 특별히 new를 사용하지 않고 간단히 쓸 수 있게 허용
String str = "abc"; 

 

public class OperatorEx22 { 

	public static void main(String[] args) {
		String str1 = "abc";
		String str2 = new String("abc");
		
		System.out.printf("\"abc\"==\"abc\" ? %b%n","abc"=="abc");
		System.out.printf(" str1==\"abc\" ? %b%n",str1=="abc");
		System.out.printf(" str2==\"abc\" ? %b%n",str2=="abc");
		
		System.out.printf("str1.equals(\"abc\") ? %b%n",str1.equals("abc"));
		System.out.printf("str2.equals(\"abc\") ? %b%n",str2.equals("abc"));
		System.out.printf("str2.equals(\"ABC\") ? %b%n",str2.equals("ABC"));
		
		System.out.printf("str2.equalsIgnoreCase(\"ABC\") ? %b%n",str2.equalsIgnoreCase("ABC"));

	}
}
"abc"=="abc" ? true
 str1=="abc" ? true
 str2=="abc" ? false
 
str1.equals("abc") ? true
str2.equals("abc") ? true
str2.equals("ABC") ? false

str2.equalsIgnoreCase("ABC") ? true

 

str2와 "abc"의 내용이 같은데도 '=='로 비교하면, false가 나옴.

내용은 같지만 서로 다른 객체이기 때문.

그러나 'equals()'는 객체가 달라도 내용이 같으면 true를 반환. 

그래서 문자열을 비교할 때는 항상 equals()를 사용 ! 

+ 대소문자를 구별하지 않고 비교하고 싶을 때 : equalsIgnoreCase() 

 


15. 논리 연산자 && || 

15-1. 논리 연산자

 

조건식을 연결할 때 사용하는 연산자

 

||    ( OR 결합 )   어느 한쪽이 true 이면 true 결과
&& ( AND 결합 ) 양쪽 모두 true 여야 true 결과

 

  • x는 10보다 크고, 20보다 작다.
    x > 10  &&  x < 20
    10 < x  &&  x < 20
    ( 10 < x < 20 은 X)

 

  • i는 2의 배수 또는 3의 배수이다.
    ( 2의 배수: 어떤 수가 2로 나누었을 때 나머지가 0 )
    i % 2 == 0 || i % 3 == 0

    ex) i의 값이 8일 때, 연산 과정
    → i % 2 == 0  ||  i % 3 == 0
    → 8 % 2 == 0 || 8 % 3 == 0
    →       0 == 0  ||  2 == 0
    →           true  ||  false
    →                true 

 

  • i는 2의 배수 또는 3의 배수이지만 6의 배수는 아니다.
    괄호로 묶지 않으면 &&가 먼저 계산 ( 우선순위: && > || ) 
    ( i % 2 == 0 || i % 3 == 0 )  &&  i  % 6 != 0

 

  • 문자 ch는 숫자(’0’~’9’)이다.
    ‘0’ < = ch && ch < = ‘9’ (자주쓰임) 
    ( ‘0’~’9’가 연속적으로 배치되어 있으므로 가능)

    ex) ch = ‘5’ 일 때, 
    → '0' <= '5'   &&   '5' <= '9' 

    → 48 <= 53  &&  53 <= 57
    →        true   &&   true
    →                true
'0' '1' '2' '3' '4' '5' '6' '7' '8' '9'
48 49 50 51 52 53 54 55 56 57

 

  • 문자 ch는 대문자 또는 소문자이다.
    ( ‘a’ < = ch && ch < = ‘z’ ) || ( ‘A’ < = ch && ch < = ‘Z’ ) 
    (’a’~’z’, ‘A’~’Z’가 연속적 배치라 가능)

 

public class OperatorEx22 { 

	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);
		
		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);
		
		ch = '1';
		System.out.printf("ch='%c', '0'<=ch && ch<='9' = %b%n", ch, '0'<=ch && ch<='9');
		
		ch = 'a';
		System.out.printf("ch='%c', 'a'<=ch && ch<='z' = %b%n", ch, 'a'<=ch && ch<='z');
		
	}
}
x=15, 10<x && x<20 = true
x= 6, x%2==0 || x%3==0 && x%6!=0 = true
x= 6, (x%2==0 || x%3==0) && x%6!=0 = false
ch='1', '0'<=ch && ch<='9' = true
ch='a', 'a'<=ch && ch<='z' = true

 

%2d : 2자리보다 작으면 여백을 추가해서 2자리를 확보
%% : % (퍼센트) 출력 

 

 

 

import java.util.*;           //Scanner클래스를 사용하기 위해 추가

public class OperatorEx22 { 

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		char ch = ' ';
		
		System.out.printf("문자를 하나 입력하세요. > ");
		
		String input = scanner.nextLine();
		ch = input.charAt(0);
		
		if('0'<=ch && ch<='9') { 
			System.out.printf("입력하신 문자는 숫자입니다.%n");
		}
		
		if('a'<=ch && ch<='z' || 'A'<=ch && ch<='Z') {
			System.out.printf("입력하신 문자는 영문자입니다.%n");
		}
		

	}
}
문자를 하나 입력하세요. > 1
입력하신 문자는 숫자입니다.

 

 

15-2. 효율적인 연산 (short circuit evaluation)

 

OR 연산    ( || )    : 좌측 피연산자가 'true(참)'이면, 우측 피연산자는 계산하지 않는다. (하나가 참이면, 결과가 참)
AND 연산 ( && ) : 좌측 피연산자가 'false(거짓)'이면, 우측 피연산자는 계산하지 않는다. (하나만 거짓이어도, 결과가 거짓) 

∴ OR연산 '||'의 경우, 연산결과가 '참'일 확률이 높은 피연산자를 왼쪽에 놓아야 빠른 연산 가능 

 

public class OperatorEx22 { 

	public static void main(String[] args) {
		int a = 5;
		int b = 0;
		
		System.out.printf("a=%d, b=%d%n", a,b);
		System.out.printf("a!=0 || ++b!=0 = %b%n", a!=0 || ++b!=0);
		System.out.printf("a=%d, b=%d%n", a,b);
		System.out.printf("a==0 && ++b!=0 = %b%n", a==0 && ++b!=0);
		System.out.printf("a=%d, b=%d%n", a,b);
		
	}
}
a=5, b=0
a!=0 || ++b!=0 = true
a=5, b=0
a==0 && ++b!=0 = false
a=5, b=0

* 변수 b에 증감연산자 '++'을 사용해서 b의 연산이 처리될 때 값이 증가되도록 했는데 b의 값이 계속 0인 이유 

  ' || (OR) ' 의 경우, 좌측 피연산자(a!=0)가 참이라서 우측 연산자(b연산) 계산 X 

  ' && (AND) ' 의 경우, 좌측 피연산자(a==0)가 거짓이라서 우측 연산자(b연산) 계산 X 

 

 

15-3. 논리 부정 연산자  !  (NOT)  

 

true를 false로, false는 true로 바꾼다.
단항 연산자 (+대입연산자)는 ← (오른쪽에서 왼쪽) 방향으로 연산

 

x ! x 
true false
false  true 
  • !!b
    → 피연산자와 가까운 것부터 연산
    → boolean b = true; 
    → !!true
    → !false
    true 

  • 문자 ch는 소문자가 아니다
    ch < 'a'  ||  ch > 'z'
     =  ! ( 'a' <= ch  &&  ch <= 'z' ) 

 


 

16. 비트 연산자 & | ^ ~ << >> 

비트 연산자 : 피연산자를 비트단위로 논리 연산

 

 

16-1. OR, AND, XOR 연산자 | & ^  

 

|   (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

 

16-2. 비트 전환 연산자 ~

 

x ~x 
1 0
0 1

 

 

16-3. 쉬프트 연산자 << >>

 

10진수 피연산자의 2진수를 왼쪽 또는 오른쪽으로 n비트만큼 이동

x << n   =   x * 2ⁿ
x >> n   =   x / 2
  • 8 << 1  =  8 * 2¹  =  16
  • 8 >> 1  =  8 / 2¹  =  4 

 


17. 조건 연산자 ? : 

조건식 ? 식1 : 식2  (조건식이 이면 식1, 거짓이면 식2)  

 

  • result = (x > y) ? x : y ;
    (x > y 이면)
    result = x ; 
    (x < y 이면)
    result = y ; 

  • x의 값이 양수면 1,  0이면 0,  음수면 -1 반환하기
    result =  x > 0  ?  1  :  ( x == 0  ?  0  :  -1 ) ; 


  • 조건 연산자를 이용해 변수의 절대값을 구한 후, 부호를 붙여 출력하기
public class OperatorEx22 { 

	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, x);
		System.out.printf("y=%c%d%n", signY, y);
		System.out.printf("z=%c%d%n", signZ, z);
		
	}
}

18. 대입 연산자 =  op=

18-1. 대입 연산자 =

오른쪽 피연산자를 왼쪽 피연산자에 저장 후 저장된 값을 반환

 

System.out.println(x=3);  //변수x에 3이 저장되고

System.out.println(3);      //연산결과인 3이 출력된다

 

* 연산방향은 ←

  • x = y = 3 ;
    → y = 3
    x = y 

18-2. lvalue와 rvalue

 

lvalue  :  대입 연산자의 왼쪽 피연산자 (left value) 
rvalue  :  대입 연산자의 오른쪽 피연산자 (right value) 

 

  • x = 3
    x : lvalue  ( 변수, 배열 등 값을 변경할 수 있는 저장공간 ( 리터럴, 상수 X ) )
    3 : rvalue ( 변수, 식, 상수 등 모두 가능 )

  • int i = 0;
    3 = i + 3;                       //에러. lvalue가 값을 저장할 수 있는 공간이 아니다.

    i + 3 = i;                        //에러. lvalue의 연산결과가 리터럴 (i+3 → 0+3 → 3 )

  • final int MAX = 3;     //변수 앞에 키워드 final을 붙이면 상수. (상수=값을 한번 저장하면 변경x)
    MAX = 10;                  // 에러. 상수(MAX)에 새로운 값을 저장할 수 없다.

 

18-3. 복합 대입 연산자 op=

 

op= =
i += 3; i = i + 3; 
i -= 3; i = i - 3;
i *= 3; i = i * 3;
i /= 3; i = i / 3;
i %= 3;  i = i % 3;
i <<= 3; i = i << 3;
i >>= 3; i = i >> 3;
i &= 3; i = i & 3;
i ^= 3; i = i ^ 3;
i |= 3; i = i | 3;
i *= 10 + j; i = i * (10+j );