이세개발
article thumbnail
Published 2023. 4. 16. 21:59
이것이java다 3.4 이항 연산자 legacy

이항 연산자는 피연산자가 두 개인 연산자를 말하면 여기에는 산술 연산자 (+,-,*,/,%),문자열 연결 연산자 (+) , 대입연산자 (=,+=,-=,*=,/=,%=,&=,^=,|=,<<=,>>=,>>>=) , 비교 연산자 , 논리연산자, 비트논리연산자, 비트이동연산자 등이 있다.

 

종류

산술 연산자: +, -, *, /, %

문자열 연결 연산자: +

대입 연산자: =,  +=,  -=,  *=,  /=,  %=, &=, ^=, |=, <<=, >>=, >>>=

비교 연산자: <, <=, >, >=, ==, !=

논리 연산자: &&, ||, &, |, ^, !

비트 논리 연산자: &, |, ^

비트 이동 연산자: <<, >>, >>>

 
 
 

1. 산술 연산자 (+,-,*,/,%)

 

우리가 일반적으로 말하는 사칙연산인 더하기뺴기곱하기나누기와 나머지를 구하는 (%)연산자를 포함해서 산술연산자는 총 5 개이다. 이 산술 연산자는 boolean 타입을 제외한 모든 기본 타입에 사용할 수 있다.

 

특징은 피연산자들의 타입이 동일하지 않을 경우 long타입을 제외한 정수타입연산은 int 타입으로 산출되고, 피연산자중 하나라도 실수타입이면 실수 타입으로 산출된다.

---------------------------------------------------------

byte byte1 = 1;

byte byte2 = 2;

byte result1 = byte1 + byte2;         //컴파일 에러

int result2 = byte1 + byte2;

----------------------------------------------------------

int타입으로 나오는 이유는 자바가상기계 (JVM) 이 기본적으로 32비트 단위로 계산하기 때문이다. 다른예를 한번 보자

-----------------------------------------------------------------------------------------------------------

int int1 = 10;

int int2 = 4;

int result2 = int1 / int2 ;

double result3 = int1 / int2;

-----------------------------------------------------------------------------------------------------------

상식적으로 생각하면 int1 / int2 면 2.5 가 맞는데 연산결과는 소수점 이하 부분을 버리고 2만 산출된다, 그렇다고 result3 은 double 타입이니까 2.5가 저장될것이라고 생각하기 쉬운데 연산후의 결과가 2이므로 2를 실수화해서 double타입으로 저장을하기때문에 2.0이 저장된다. 만약 2.5를 산출결과로 얻고싶다면 피연산자중 최소 하나는 실수 타입이어야 한다.

 

------------------------------------------------------------------------------------------------------------------------------

int int1 = 10;

int int2 = 4;

int result2 = int1 / int2 ;                   // result2 = 2

double result3 = int1 / int2;              // result3 = 2.0

double result4 = (int1*1.0) / int2;          //result4 = 2.5              int1에 1.0 을 곱하여 실수타입으로 변환

double result5 = (double) int1 / int2;         //result5 = 2.5          int1을 double타입으로 강제변환

double result6 = int1 / (double) int2;         //result6 = 2.5          int2을 double타입으로 강제변환

-----------------------------------------------------------------------------------------------------------------------------

 

------------------------[ ArithmeticOperatorExample.java ] 산술 연산자 -----------------------------------------------------

public class ArithmeticOperatorExample {

public static void main(String[] args) {

int v1 = 5;

int v2 = 2;

 

int result1 = v1 + v2;

System.out.println("result1=" + result1);

 

int result2 = v1 - v2;

System.out.println("result2=" + result2);

 

int result3 = v1 * v2;

System.out.println("result3=" + result3);

 

int result4 = v1 / v2;

System.out.println("result4=" + result4);

 

int result5 = v1 % v2;

System.out.println("result5=" + result5);

 

double result6 = (double) v1 / v2;

System.out.println("result6=" + result6);

}

}

---------------------------------------------------------------------------------------------------------------------------------------------

Console

result1=7

result2=3

result3=10

result4=2

result5=1

result6=2.5

---------------------------------------------------------------------------------------------------------------------------------------------------

 

 

char 타입도 정수 타입이므로 산술 연산이 가능하다. 주의할 점은 char 타입이 산술연산이 될 경우 int 타입으로 변환되므로 산출 차입은 int 타입이다. 다음예제는 컴파일 에러가 발생한다.

-----------------------------------[ CharOperationExample.java ] char 타입 연산 ------------------------------------------------------------

public class CharOperationExample {

public static void main(String[] args) {

char c1 = 'A' + 1;       //'B' 유니코드 66

char c2 = 'A';            //'A' 유니코드 65  

//char c3 = c2 + 1;    //컴파일 에러

     char c4 = (char) (c2 + 1);  

System.out.println("c1: " + c1);

System.out.println("c2: " + c2);

//System.out.println("c3: " + c3);

}

}

--------------------------------------------------------------------------------------------------------------------------------------------------------

Console

c1: B

c2: A

c4: B

--------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

오버플로우 탐지

 

산술연산을 할 떄 주의할 점은 연산 후의 산출값이 산출 타입으로 충분히 표현 가능한지 살펴봐야 한다. 산출 타입으로 표현할 수 없는 값이 산출되었을 경우, 오버플로우가 발생하고 쓰레기값(엉뚱한 값) 을 얻을 수 있기 떄문이다. 다음예제를 실행시켜 보자.

 

--------------------[ OverflowExample.java ] 오버플로우 -------------------------------------------------------

public class OverflowExample {

public static void main(String[] args) {

 

int x1 = 1000000;

int y1 = 1000000;

int z1 = x * y;

System.out.println(z1);

 

 

long x2 = 1000000;

long y2 = 1000000;

long z2 = x * y;

System.out.println(z2);

}

}

--------------------------------------------------------------------------------------------------------------------------

Console

-727379968

1000000000000

--------------------------------------------------------------------------------------------------------------------------

변수 x와y는 int 타입이고 x * y 역시 int 타입이므로 연산의 산출 타입은 int 타입이다. 위 코드는 컴파일 에러는 발생하지 않지만, 변수 z에는 올바른 값이 저장되지 않는다. 그 이유는 1000000*1000000 은 10^6*10^6 = 10^12 이 되어 int 타입에 저장될수 있는 값의 범위를 초가하게되어 -727379968 이라는 쓰레기값을 얻게 된다. 상기 예제가 올바른 값을 얻기 위해서는 변수 x 와 y 중 최소 하나라도 long 타입이 되어야 하고 변수z 가 long 타입이여야 한다.

바로 산술 연산자를 사용하지말고 메소드를 이용하는게 오버플로우를 탐지할 수 있기 때문에 메소드를 이용하는것이 좋다.

아직 배우지 않은 내용이지만 이런 코드가 필요하다는 것만 이해하고 넘어가도록 하자. 예외처리는 10장에서 배운다.

 

-----------------[ CheakOverflowExample.java ] 산술 연산 전에 오버플로우를 탐지 -------------------------------------------

public class CheckOverflowExample {

public static void main(String[] args) {

try {

int result = safeAdd(2000000000, 2000000000);

System.out.println(result);

} catch(ArithmeticException e) {

System.out.println("오버플로우가 발생하여 정확하게 계산할 수 없음");

}

}

 

public static int safeAdd(int left, int right)  {

if((right>0)) { 

if(left>(Integer.MAX_VALUE - right)) {

throw new ArithmeticException("오버플로우 발생");

}

} else {

if(left<(Integer.MIN_VALUE - right)) {

throw new ArithmeticException("오버플로우 발생");

}

}

return left + right;

}

}

-----------------------------------------------------------------------------------------------------------------------------------------
Console
오버플로우가 발생하여 정확하게 계산할 수 없음
-----------------------------------------------------------------------------------------------------------------------------------------
 
safeadd() 메소드는 두 개의 매개값을 산술 연산하기 전에 어떤 번위의 값인지 조사해서 연산 시 오버플로우가 생길 것 같으면 14라인과 18라인에서 ArithmeticException 예외를 발생시킨다. main()메소드는 예외가 발생될 경우 7라인에서 "오버플로우가 발생하여 정확하게 계산할 수 없음" 을 출력한다. safeAdd()메소드는 두 개의 매개값을 더해도 안전한 경우에만 더한 결과를 리턴한다.
 
 
 
정확한 계산은 정수 사용
 
정확하게 계산해야 할 떄는 부동소수점(실수) 타입을 사용하지 않는 것이 좋다. 다음 예제를 보자. main() 메소드는 사과 1개를 0.1 단위 10조각으로 보고, 그 중 7조각(0.7) 을 뺀 3 조각 (0.3) 을 result 변수에 저장한다. 당연히 result 변수는 0.3일 것이다.
------------[ AccuracyExample.java ] 정확하게 계산할 때에는 부동소수점 타입을 사용하지 않는다. ------------------------------------------------------------
public class AccuracyExample1 {
public static void main(String[] args) {
int apple = 1;
double pieceUnit = 0.1;
int number = 7;
 
double result = apple - number*pieceUnit;
 
System.out.println("사과 한개에서 ");
System.out.println("0.7 조각을 빼면, ");
System.out.println(result + " 조각이 남는다.");
}
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Console
사과 한개에서 
0.7 조각을 빼면, 
0.29999999999999993 조각이 남는다.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
그러나 출력된 결과를 보면 result 변수의 값은 0.299999999999999993이 되어 정확히 0.3이 되지 않는다. 이것은 이진 포맷의 가수를 사용하는 부동수수점 타입(float, double) 은 0.1 을 정확히 표현할 수 없어 근사치로 처리하기 때문이다. 정확한 계산이 필요하다면 정수 연산으로 변경해서 다음과 같이 샐행해야 한다.
------------[ AccuracyExample.java ] 정확하게 계산할 때에는 부동소수점 타입을 사용하지 않는다. ------------------------------------------------------------
public class AccuracyExample2 {
public static void main(String[] args) {
int apple = 1;
 
int totalPieces = apple * 10;
int number = 7;
int temp = totalPieces - number;
 
double result = temp/10.0; 
 
System.out.println("사과 한개에서 ");
System.out.println("0.7 조각을 빼면, ");
System.out.println(result + " 조각이 남는다.");
}
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Console
사과 한개에서 
0.7 조각을 빼면, 
0.3 조각이 남는다.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
 
 
NaN과 Infinity 연산
 
/또는 % 연산자를 사용할 때도 주의할 점이 있다. 좌측 피 연산자가 정수 타입인 경우 나누는 수인 우측 피연산자는 0을 사용할 수 없다. 만일 0으로 나누면 컴파일은 정상적으로 되지만, 실행 시 ArithmeticException (예외) 이 발생한다.
---------------------------------------------------------------
5 / 0 -> ArithmeticException 예외 발생
5 % 0 -> ArithmeticException 예외 발생
----------------------------------------------------------------
자바는 프로그램 실행 도중 예외가 발생하면 실행이 즉시 멈추고 프로그램은 종료된다. ArithmeticException 이 발생했을 경우 프로그램이 종료되지 않도록 하려면 예외 처리를 해야 한다. 예외 처리는 예외가 발생되었을 경우, catch 블록을 실행하도록 하는 것이다. 자세한 내용은 10장에서 학습한다.
 
-------------------------------------------------------------------------------------------------
try {                               //y가 0일 경우
//int z = x / y ;           //ArithmeticException 발생
int z = x % y ;
System.out.println("z : " + z);
} catch(ArithmeticException e) {                //예외처리
System.out.println("0으로 나누면 안됨");
}
------------------------------------------------------------------------------------------------
그러나 실수 타입인 0.0 또는 0.0f 로 나누면 ArithmeticException이 발생하지 않고, / 연산의 결과는 Infinity(무한대) 값을 가지며, % 연산의 결과는 NaN(Not a Number) 을 가진다.
------------------------------------------------------------------------------------------------
5 / 0.0 -> Infinity
5 % 0.0 -> NaN
--------------------------------------------------------------------------------------------------
 
주의할 점은 /, % 연산의 결과가 Infinity 또는 NaN 이 나오면 다음 연산을 수행해서는 안된다. 왜냐하면 이 값과 산술 연산을 하면 어떤 수와 연산하더라도 Infinity와 NaN이 산출되어 데이터가 엉망이 될 수 있다.
-----------------------------------------------------------
Infinity + 2 -> Infinity
NaN + 2 -> NaN
-----------------------------------------------------------
프로그램 코드에서 / 와 % 연산의 결과가 Infinity 또는 NaN인지 확인하려면 Double.isInfinite() 와 Double.isNaN() 메소드를 이용하면 된다. 이 메소드들은 double 타입의 값을 매개값으로 받아서 이 값이 Infinity 또는 NaN이라면 true를 리턴하고, 그렇지 않다면 false를 리턴한다. 다음 예제에서 6라인과 7라인을 번갈아가면 주석 처리 후 실행해보길 바란다.
 
-----------------------------------------[ InfinityAndNaNCheckExample.java ] Infinity와 NaN--------------------------------------------
public class InfinityAndNaNCheckExample {
public static void main(String[] args) {
int x = 5;
double y = 0.0;
 
double z = x / y;
//double z = x % y;
 
System.out.println(Double.isInfinite(z));
System.out.println(Double.isNaN(z));
 
//잘못된 코드
System.out.println(z + 2);
 
//알맞은 코드
if(Double.isInfinite(z) || Double.isNaN(z)) { 
System.out.println("값 산출 불가"); 
} else { 
System.out.println(z + 2); 
}
 
//---------------------------------------------------
 
/*int x = 5;
int y = 0;
 
try {
//int z = x / y;
int z = x % y;
System.out.println("z: " + z);
} catch(ArithmeticException e) {
System.out.println("0으로 나누면 안됨");
}*/
}
}
-----------------------------------------------------------------------------------------------------------------------------------
Console
true
false
Infinity
값 산출 불가
------------------------------------------------------------------------------------------------------------------------------------
 
 
 
입력값의 NaN 검사
 
부동소수점(실수) 을 입력받을 때는 반드시 NaN 검사를 해야 한다. 다음 예제를 보자. 3라인에서 부동소수점으로 변환이 가능한 문자열을 입력받는다고 가정해보자. 악의성 있는 사용자는 숫자로 변환이 안되는 "NaN"을 입력할 수도 있다.
-----------------------------[ InputDataCheckNaNExample1.java ] "NaN" 문자열의 문제점-----------------------------------------
public class InputDataCheckNaNExample1 {
public static void main(String[] args) {
String userInput = "NaN";
double val = Double.valueOf( userInput );
 
double currentBalance = 10000.0;
 
currentBalance += val;
System.out.println(currentBalance);
}
}
-------------------------------------------------------------------------------------------------------------------------------------------
Console
NaN
-------------------------------------------------------------------------------------------------------------------------------------------
"NaN" 문자열은 Double.valueOf() 메소드에 의해 double 타입으로 변환되면 NaN이 된다. 따라서 val에는 NaN이 저장된다. 문제는 NaN은 산술 연산이 가능하다는 점이다. NaN과 어떠한 수가 연산되면 결과는 NaN이 산출되어 데이터가 엉망이 된다. 6라인에서 currentBalance에 10000.0 을 저장하고 8라인에서 NaN이 산출되어 데이터가 엉망이 된다. 6라인에서 currentBalance에 10000.0을 저장하고 8라인에서 NaN과 더하기 연산을 수행하면 currentBalance는 NaN이 저장되어 원래 데이터가 없어져 버린다. 그렇기 때문에 사용자로부터 문자열을 입력받을 때에는 반드시 "NaN" 인지를 조사하고 만약 "NaN"이라면 NaN과 산술 연산을 수행해서는 안 된다. 이전 예제를 수정하면 다음과 같다.
 
-----------------------------[ InputDataCheckNaNExample2.java ] "NaN" 을 체크하고 연산 수행-----------------------------------------
public class InputDataCheckNaNExample2 {
public static void main(String[] args) {
String userInput = "NaN";
double val = Double.valueOf(userInput);
 
double currentBalance = 10000.0;
 
if(Double.isNaN(val)) {
System.out.println("NaN이 입력되어 처리할 수 없음");
val = 0.0;
 
currentBalance += val;
System.out.println(currentBalance);
}
}
----------------------------------------------------------------------------------------------------------------------------------------------------
Console
NaN이 입력되어 처리할 수 없음
10000.0
----------------------------------------------------------------------------------------------------------------------------------------------------
8라인의 if 조건식에는 Double.isNaN() 메소드를 이용해서 변수 val의 값이 NaN인지를 검사한다. 주의할 점은 NaN인지 조사를 할 때 ==연산자를 사용하면 안 된다. NaN은 !=연산자를  제외한 모든 비교 연산자를 사용할 경우 false 값을 리턴하기 때문이다. NaN인지 검사를 하려면 반드시 Double.isNaN()을 사용해야 한다. Double.isNaN() 메소드는 매개값이 NaN이라면 ture를 리턴한다. if 조건식이 ture가 되면 9~10라인이 실행되고, 변수 val은 NaN 대신 0.0이 된다. 그래서 13라인에서 currentBalance 값과 0.0이 연산되어 currentBalance는 원래 값을 유지하게 된다.
 
 
 
2.문자열 연결 연산자 (+)
 
문자열 연결 연산자인 +는 문자열은 서로 결합하는 연산자이다. + 연산자는 산술 연산자, 부호 연산자인 동시에 문자열 연결 연산자이기도 하다. 피연산자중 한 쪽이 문자열이면 + 연산자는 문자열 연결 연산자로 사용되어 다른 피연산자를 문자열로 변환하고 서로 결합한다. 예를 들어 아래 코드에서 변서 str1에는 문자열 "JDK6.0" 이 저장되고, str2는 문자열 "JDK6.0 특징" 이 저장된다.
-----------------------------------------------------------------------
String str1 = "JDK" + 6.0 ;
String str2 = str1 + "특징" ;
-----------------------------------------------------------------------
 
간혹 + 연산자가 산술 연산자인지 문자열 연결 연산자인지 판단하기 어려운 경우가 있다. 예를들어 다음 연산의 결과는 무엇일까?
------------------------------------------------------------------------------------------------------
"JDK" + 3 + 3.0 ;
------------------------------------------------------------------------------------------------------
문자열과 숫자가 혼합된 + 연산식은 왼쪽에서부터 오른쪽으로 연산이 진행된다. 따라서 "JDK" + 3 이 먼저 연산되어 "JDK3" 이라는 문자열이 되고, 이것을 다시 3.0과 연산하여 "JDK33.0" 이라는 문자열 결과가 나온다. 하지만 다음의 경우는 어떨까?
-------------------------------------------------------------------------------------------------------
3 + 3.0 + "JDK" ;
-----------------------------------------------------------------------------------------------------
3 + 3.0이 먼저 연산되어 6.0 이라는 실수값이 되고 이것을 "JDK"와 연산하여 "6.0JDK" 라는 결과가 나온다. 어떤 것이 먼저 연산되느냐에 따라 다른 결과가 나오므로 주의할 필요가 있다.
 
-------------------------[ StringConcatExample.java ] 문자열 연결 연산자------------------------------------------------------------------
public class StringConcatExample {
public static void main(String[] args) {
String str1 = "JDK" + 6.0;
String str2 = str1 + " 특징";
System.out.println(str2);
 
String str3 = "JDK" + 3 + 3.0;
String str4 = 3 + 3.0 + "JDK";
System.out.println(str3);
System.out.println(str4);
}
}
----------------------------------------------------------------------------------------------------------------------------------------------------
Console
JDK6.0 특징
JDK33.0
6.0JDK
----------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

 

3. 비교 연산자 (<,<=,>,>=,==,!=)

 

비교연산자는 대소 (<,<=,>,>=) 또는 동등 (==,!=) 을 비교해서 boolean 타입인 ture/false 를 산출한다. 대소 연산자는 boolean 타입을 제외한 기본 타입에 사용할 수 있고, 동등 연산자는 모든 타입에 사용될 수 있다. 비교연산자는 흐름제어문인 조건문(if) 반복문(for,while) 에서 주로 이용되어 실행 흐름을 제어할 때 사용된다.

 

 

만약 피연산자가 char 타입이면 유니코드 값으로 비교 연산을 수행한다. 예를 들어 'A' 의 유니코드는 65이고 'B' 의 유니코드는 66 이므로 비교 연산자는 65와 66 을 비교하게 된다.

('A' < 'B') --> ( 65 < 66 )

 

--------------------------------[ CompareOperatorExample1.java ] 비교 연산자------------------------------------------------------------------------------

public class CompareOperatorExample1 {

public static void main(String[] args) {

int num1 = 10;

int num2 = 10;

boolean result1 = (num1 == num2); 

boolean result2 = (num1 != num2); 

boolean result3 = (num1 <= num2);

System.out.println("result1=" + result1);

System.out.println("result2=" + result2);

System.out.println("result3=" + result3);

 

char char1 = 'A';

char char2 = 'B';

boolean result4 = (char1 < char2);

System.out.println("result4=" + result4);

}

}

--------------------------------------------------------------------------------------------------------------------------------------------------------------
Console
result1=true
result2=false
result3=true
result4=true
----------------------------------------------------------------------------------------------------------------------------------------------------------------

 

비교 연산자에서도 연산을 수행하기 전에 타입 변환을 통해 피연산자의 타입을 일치시킨다. 예를 들어, 'A'==65는 'A'가 int 타입으로 변환되어 65 가 된 다음 65==65로 비교한다. 마찬가지로 3==3.0에서 3은 int 타입이고 3.0은 double타입이므로 int 타입인 3 을 보다 큰 타입인 double타입으로 변환한 다음 3.0==3.0 으로 비교한다.

-----------------------------------------------------------------------------------------------------------------------------------------

'A' == 65  -> ture

3 == 3.0 -> ture

-----------------------------------------------------------------------------------------------------------------------------------------

그러나 한가지 예외가 있는데, 0.1 == 0.1f 와 같은 경우를 보자. 정상적이라면 0.1f가 좌측 피연산자의 타입인 double로 변환되어 0.1==0.1이 되고 ture가 산출되어야 하지만, 이 결과값은 false가 산출된다.

 

-----------------------------------------------------------------------------------------------------------------------------------------

그 이유는 이진 포맷의 가수를 사용하는 모든 부동소수점 타입은 0.1을 정확히 표현할 수가 없어서 0.1f는 0.1의 근사값으로 표현되어 0.10000000000149011612와 같은 값이 되기 때문에 0.1 보다 큰 값이 되어 버린다. 해결책은 다음 예제와 같이 피연산자를 모두 float타입으로 강제 타입 변환한 후에 비교연산을 하든지, 정수로 변환해서 비교하면 된다.

 

----------------------------------------[ CompareOperatorExample2.java ] 비교 연산자---------------------------------------------------------------------

public class CompareOperatorExample2 {

public static void main(String[] args) {

int v2 = 1;

double v3 = 1.0;

System.out.println(v2 == v3); //true

 

double v4 = 0.1;

float v5 = 0.1f;

System.out.println(v4 == v5); //false

System.out.println((float)v4 == v5); //true

System.out.println((int)(v4*10) == (int)(v5*10)); //true

}

}

------------------------------------------------------------------------------------------------------------------------------------------------------------------

Console

true

false

true

true

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

String 타입의 문자열을 비교할 때에는 대소(<,<=,>,>=) 연산자를 사용할 수 없고, 동등(==,!=) 비교연산자는 사용할 수 있으나 문자열이 같은지, 다른지를 비교하는 용도로는 사용되지 않는다. 기본타입 (byte,char,short,int,long,float,double,boolean) 인 변수의 값을 비교할 때에는 == 연산자를 사용하지만 참조 타입인 String 변수를 비교할 때 == 연산자를 사용하면 원하지 않는 결과가 나올 수도 있다. 다음 코드를 보면서 이해해 보자.

-------------------------------------------------------------------------------------------------------

String strVar1 = "정한교" ;

String strVar2 = "정한교" ;

String strVar3 = new String("정한교") ;

------------------------------------------------------------------------------------------------------

자바는 문자열 리터럴이 동일하면 동일한 String 객체를 참조하도록 되어 있다. 그래서 변수 strVar1 과 strVar2 는 동일한 String 객체의 번지값을 가지고 있다. 그러나 변수 strVar3 은 객체 생성 연산자인 new 로 생성한 새로운 String 객체의 번지값을 가지고 있다.

이럴경우 변수 strVar1과 strVar2의 ==연산은 ture를 산출하고 strVar1이나2 와 strVar3의 ==연산은 false를 산출한다. == 연산자는 변수에 저장된 값만 비교하기 때문에 이러한 결과가 나온다.

---------------------------------------------------------------------------------

strVar1 == strVar2   ->  ture

strVar2 == strVar3   -> false

---------------------------------------------------------------------------------

동일한 String 객체이건 다른 String 객체이건 상관없이 String 객체의 문자열만을 비교하고 싶다면 == 연산자 대신에 equals() 메소드를 사용해야 한다. equals() 메소드는 원본 문자열과 매개 값으로 주어진 비교 문자열이 동일한지 비교한 후 ture 또는 false를 리턴한다.

---------------------------------------

boolean result = str1.equals(str2)

---------------------------------------

strVar1.equals(strVar2) ->ture

strVar2.equals(strVar3) ->ture

-----------------------------------------

 

-------------------------------[ StringEqualsExample.java ] 문자열 비교-----------------------------------------------------------

public class StringEqualsExample {

public static void main(String[] args) {

String strVar1 = "정한교";

String strVar2 = "정한교";

String strVar3 = new String("정한교");

 

System.out.println( strVar1 == strVar2);

System.out.println( strVar1 == strVar3);

System.out.println();

System.out.println( strVar1.equals(strVar2));

System.out.println( strVar1.equals(strVar3));

}

}

-----------------------------------------------------------------------------------------------------------------------------------------
Console
true
false
 
true
true
-----------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

 

4. 논리 연산자 (&&, ||, &, |, ^, !)

 

논리 연산자는 논리곱(&&), 논리합(||), 배타적 논리합(^), 그리고 논리 부정(!) 연산을 수행한다. 논리 연산자의 피연산자는 boolean 타입만 사용할 수 있다. 다음은 논리 연산자의 종류와 기능을 설명한 표이다.

 

&&와 &는 산출 결과는 같지만 연산 과정이 조금 다르다. &&는 앞의 피연산자가 false라면 뒤의 피연산자를 평가하지 않고 바로 false라는 산출 결과를 낸다. 왜냐하면 하나라도 false라면 전체 연산식은 false이기 떄문이다. 그러나 & 는 두 피연산자 모두를 평가해서 산출 결과를 낸다. 따라서 & 보다는 &&가 더 효율적으로 동작한다. || 와 | 도 마찬가지이다. || 는 앞의 피연산자가 ture라면 뒤의 피연산자를 평가하지 않고 바로 ture라는 산출 결과를 낸다. 왜냐하면 하나라도 ture이면 전체 연산식은 ture이기 때문이다. 그러나 | 는 두 피연산자 모두를 평가해서 산출 결과를 낸다. 왜냐하면 하나라도 ture 이면 전체 연산식은 ture 이기 때문이다. 그러나 | 는 두 피연산자 모두를 평가해서 산출결과를 낸다. 따라서 | 보다는 || 가 더 효율적으로 동작한다. 논리 연산은 흐름 제어문인 조건문, 반복문 등에서 주로 이용된다. 다음 예제처럼 if문의 괄호 () 안에는 조건식이 오는데, 조건식은 비교 연산자와 논리 연산자들이 주로 사용된다. if 문의 조건식이 ture라면 블록을 실행하고 false라면 블록을 실행하지 않는다.

 

 

----------------------- [ LogicalOperatorExample.java ] 논리 연산자 ------------------------------------------------------------------------

public class LogicalOperatorExample {

public static void main(String[] args) {

int charCode = 'A';

 

if( (charCode>=65) & (charCode<=90) ) {

System.out.println("대문자 이군요");

}

 

if( (charCode>=97) && (charCode<=122) ) {

System.out.println("소문자 이군요");

}

 

if( !(charCode<48) && !(charCode>57) ) {

System.out.println("0~9 숫자 이군요");

}

 

int value = 6;

 

if( (value%2==0) | (value%3==0) ) {

System.out.println("2 또는 3의 배수 이군요");

}

 

if( (value%2==0) || (value%3==0) ) {

System.out.println("2 또는 3의 배수 이군요");

}

}

}

--------------------------------------------------------------------------------------------------------------------------------------------
Console
대문자 이군요
2 또는 3의 배수 이군요
2 또는 3의 배수 이군요

--------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

 

 

 

5. 비트 연산자 (&, | ,^ , ~, <<, >>, >>>>)

 

비트 연산자는 데이터를 비트 단위로 연산한다. 즉 0과 1이 피연산자가 된다. 그렇기 떄문에 0과1로 표현이 가능한 정수 타입만 비트연산을 할 수 있다. 비트연산자는 기능에 따라 비트 논리 연산자 (&, | ,^, ~) 와 비트 이동 연산자 (<<, >>, >>>>) 로 구분한다. 일반 논리 연산자가 ture와 false를 연산한다면 비트 논리 연산자는 0과 1을 연산한다. 비트이동 연산자는 비트를 좌측 또는 우측으로 이동하는 연산자이다.

 

1) 비트 논리 연산자 (&, |, ^)

 

비트 논리 연산자에는 &, |, ^, ~ 가 있다. &, |, ^ 연산자는 피연산자가 boolean 타입일 경우에는 일반 논리 연산자이고, 피연산자가 정수 타입일 경우에는 비트 논리 연산자로 사용된다. ~ 는 단항 연산자에서 이미 소개가 되었다. 다음은 비트 논리 연산자의 종류와 기능을 설명한 표이다.

 

 

비트연산자는 피연산자를 int 타입으로 자동 타입 변환한 후 연산을 수행한다. 그렇기 때문에 bytem short, char타입을 비트 논리 연산하면 그 결과는 int 타입이 된다. 그래서 다음은 컴파일 에러가 난다.

 

----------------------------------------

byte num1 = 45;

byte num2 = 25;

byte result = num1 & num2 ;  //컴파일 에러  -> int result = num1 & num2 ;

------------------------------------------------------------------------------------------

 

------------------------------------[ BitLogicExample.java ] 비트 논리 연산자 ----------------------------------------------------------------

public class BitLogicExample {

public static void main(String[] args) {

System.out.println("45 & 25 = " + (45 & 25));

System.out.println("45 | 25 = " + (45 | 25));

System.out.println("45 ^ 25 = " + (45 ^ 25));

System.out.println("~45 = " + (~45));

 

System.out.println(toBinaryString(45));

System.out.println("&");

System.out.println(toBinaryString(25));

System.out.println("||");

System.out.println(toBinaryString(45&25));

}

 

public static String toBinaryString(int value) {

String str = Integer.toBinaryString(value);

while(str.length() < 32) {

str = "0" + str;

}

return str;

}

}

--------------------------------------------------------------------------------------------------------------------------------------

Console

45 & 25 = 9

45 | 25 = 61

45 ^ 25 = 52

~45 = -46

00000000000000000000000000101101

&

00000000000000000000000000011001

||

00000000000000000000000000001001

--------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

2) 비트 이동 연산자

 

비트 이동(shift) 연산자는 정수 데이터의 비트를 좌측 또는 우측으로 밀어서 이동시키는 연산을 수행한다. 다음은 비트 이동 연산을 수행하는 연산자의 종류와 기능을 설명한 표이다.

 

 

좌측 이동 연산자 (<<) 를 사용하여 정수1 을 3비트만큼 왼쪽으로 이동시켜 보자.

 

---------------------

int result = 1 << 3;

----------------------

===== 2^3

32비트 전체를 왼쪽으로 3비트 이동할 때 맨 왼쪽 3비트는 밀려서 버려지고, 맨 오른쪽에 새로 생기는 3비트는 0 으로 채우면 2^3 값을 얻게 된다. 따라서 result 변수에는 8이 저장된다. 이번에는 우측 이동 연산자 (>>) 를 사용하여 정수 -8을 3비트만큼 오른쪽으로 이동시켜 보자.

 

-------------------------

int result = -8 >> 3;

-------------------------

===== -1

32비트 전체를 오른쪽으로 3비트 이동할 때 맨 오른쪽 3비트는 밀려서 버려지고, 맨 왼쪽에 새로 생기는 3비트는 최상위 부호 비트(MSB) 와 동일한 값으로 채워진다. 따라서 -8은 최상위 부호 비트가 1 이므로 맨 왼쪽 빈 공간은 1로 채워진다. 32비트가 모두 1이면 정수-1이므로 변수 result에는 -1이 저장된다. 이번에는 우측 이동 연산자(>>>) 를 사용하여 정수 -8을 3비트만큼 오른쪽으로 이동시켜보자

 

---------------------

int result =-8 >>> 3;

-----------------------

===== 5336870911     ( 00011111 11111111 11111111 11111111)

32비트 전체를 오른쪽으로 3비트 이동할 때 맨 오른쪽 3비트는 밀려서 버려지고, 맨 왼쪽에 새로 생기는 3비트는 무조건 0 으로 채워진다. 이렇게 변환된 이진수를 십진수로 변환하면 536870911 값을 얻을 수 있다.

 

 

----------------------------------[ BitShiftExample.java ] 비트 이동 연산자 -------------------------------------------------------

package sec04.exam05_bit;

public class BitShiftExample {

public static void main(String[] args) {

System.out.println("1 << 3 = " + (1<<3));

System.out.println("-8 >> 3 = " + (-8>>3));

System.out.println("-8 >>> 3 = " + (-8>>>3));

 

System.out.println(toBinaryString(1));

System.out.println("<< 3");

System.out.println(toBinaryString(1<<3));

}

 

public static String toBinaryString(int value) {

String str = Integer.toBinaryString(value);

while(str.length() < 32) {

str = "0" + str;

}

return str;

}

}

---------------------------------------------------------------------------------------------------------------------------------

Console

1 << 3 = 8

-8 >> 3 = -1

-8 >>> 3 = 536870911

00000000000000000000000000000001

<< 3

00000000000000000000000000001000

---------------------------------------------------------------------------------------------------------------------------------

 

 

 

6. 대입 연산자 (=, +=, -=, *=, /=, %=, &=, ^=, |=, <<=, >>=, >>>=)

 

대입 연산자는 오른쪽 피연산자의 값을 좌측 피연산자인 변수에 저장한다. 오른쪽 피연산자는 리터럴 및 변수, 그리고 다른 연산식이 올 수 있다. 단순히 오른쪽 피연산자의 값을 변수에 저장하는 단순 대입 연산자가 있고, 정해진 연산을 수행한 후 결과를 변수에 저장하는 복합 대입 연산자도 있다. 다음은 대입 연산자의 종류를 설명한 표이다.

 

대입 연산자는 모든 연산자들 중에서 가장 낮은 연산순위를 가지고 있기 떄문에 제일 마지막에 수행된다. 그리고 연산의 진행방행이 오른쪽에서 왼쪽이기 때문에 a=b=c=5; 는 다음 순서로 연산된다.

5=c  ,   c=b  ,   b=a

 

 

 

 

------------------------------------[ AssignmentOperatorExample.java ] 대입 연산자----------------------------------------------------------------------

package sec04.exam06_assignment;

 

public class AssignmentOperatorExample {

public static void main(String[] args) {

int result = 0;

result += 10;

System.out.println("result=" + result);

result -= 5;

System.out.println("result=" + result);

result *= 3;

System.out.println("result=" + result);

result /= 5;

System.out.println("result=" + result);

result %= 3;

System.out.println("result=" + result);

}

}

-------------------------------------------------------------------------------------------------------------------------------------------------------------
Console
result=10
result=5
result=15
result=3
result=0

-------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

profile

이세개발

@print(name)

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!