Java

[I Don't Know Java] 이것이 자바다 2

postbirds 2026. 3. 19. 16:45

 

제가 블로그에 [I Don't Know Java]를 붙여 작성하는 게시물이 있습니다.『이것이 자바다(신용권, 임경균 저)』를 2회독하며, Java 기본기를 갈고닦는 과정입니다. 이미 익숙한 내용이라도 다시금 문장으로 적으며 기초를 공고히 하기도 하고, 때로는 너무 당연하게 여겨 지나쳤던 디테일을 다시 골라내어 정리하고 있습니다. 아는 내용을 복습하는 의미도 있지만, 놓치고 있던 지점들을 새롭게 발견하는 즐거움이 있습니다. 이번 내용인 '[I Don't Know Java] 이것이 자바다 2'는 Ch03 연산자, Ch04 조건문과 반복문 내용을 담고 있습니다. 

 

연산자

산술 연산의 특징

  • 피연산자가 정수 타입(byte, short, char, int)이면 연산 결과는 int 타입이다.
  • 피연산자가 정수 타입이고, 그 중 하나가 long 타입이면 연산 결과는 long 타입이다.
  • 피연산자 중 하나가 실수 타입(float, double)이면 연산 결과는 실수 타입이다. 

오버플로우와 언더플로우

  • 오버플로우: 타입이 허용하는 최대값을 벗어나는 것
  • 언더플로우: 타입이 허용하는 최소값을 벗어나는 것
  • 정수 연산 시 오버플로우가 발생하면 해당 정수의 최소값으로 돌아간다. (실행 에러가 발생하지 않음)
  • 정수 연산시 언더플로우가 발생하면 해당 점수의 최대값으로 돌아간다. (실행 에러가 발생하지 않음)  

정확한 연산: 실수가 아니라 정수를 사용하자.

이진 부동소수점 방식(IEEE754)의 한계로인해, 실수는 정확한 값을 표현하지 못할 때가 있다. 

int a = 1;
double b = 0.1;
int c = 7;

// 0.3이 안 나옴
// 0.2999999.... 이 나옴
double result = a - b * c;

이를 해결하기 위해서는 데이터를 정수 타입으로 바꿔서 연산 후 다시 실수려 변환하는 습관이 필요하다. 금융권이나 정밀한 수치가 필요한 곳에서는 BigDecimal을 사용하거나 정수 기반 연산을 한다.

// 정확한 계산을 위해서 정수타입으로바꿔야 함. 
int a = 1;
int b = a * 10;
int c = 7;

double result = a - b * c; // 원하던 결과인 3이 나옴

 

나눗셈 : NaN과 Infinity 처리

  • ArithmeticException: 0으로 나누거나 0으로 나머지를 구할 때 발생한다.
  • Infinity: 0으로 나눌 때 (ex: 10 / 0)
  • NaN: 0으로 나머지를 구할 때 (ex: 10 % 0)

Infinity와 NaN은 이후 연산에 포함되어도 오류를 발생시키지 않는다. Infinity + 1 은 그대로 Infinity이기 
때문이다. 이를 확인하기 위해서는 아래와 같은 Double 클래스의 정적 메소드를 사용할 수 있다.  

Double.isInfinite(변수);
Double.isNan(변수);

 

비교 연산자: 피연산자의 타입이 다를 경우 디테일

  • 'A' == 65 (true): 문자가 유니코드 정수값으로 변환되어 비교된다.
  • 3 == 3.0 (true): 정수(int)가 실수(double) 타입인 3.0으로 변환된다.
  • 주의(예외 케이스)
    • 0.1f == 0.1 (false): float와 double의 정밀도 차이 때문에 false가 발생한다. true를 내기 위해선 양쪽 모두 float으로 캐스팅하면 된다.
  • 문자열 비교: 내용을 비교할 때 equals() 메소드를 사용한다. ==과 !=은 주소값을 비교한다.

논리 및 비트 연산자

  • 논리 연산자
    • XOR (^): 논리 연산에서 참(true)의 개수가 홀수 개일 때 참을 반환한다. (꿀팁)
    • And(&&), Or(||), Not(!)
  • 비트 이동 연산자(Shift)
    • a << b: a * b^2 
    • a >> b: a / b^2
    • a >>> b: 정수 a의 각 비트를 b 만큼 오른쪽으로 이동하고 왼쪽의 빈자리는 0으로 채운다.  
  • 비트 논리 연산자
    • And(&), Or(|), XOR(^), Not(~)

 

조건문과 반복문

if문, else if문은 Pass.

 

Switch문: Java11 이전, Java12, Java13 

  • Java 11 이전:  switch(변수){ ... } 형태로 사용되고, 변수에는 정수타입(byte, char, short, int, long)과 문자열(String)이 들어갈 수 있다. 
  • Java 12 이후: expressions을 사용할 수 있다. break를 없애고 {}를 사용할 수 있다.
    • expression: 평가되어 결과적으로 하나의 값을 만들어내는 리터럴, 변수, 연산자, 메소드 호출 등의 조합
  • Java 13 이후: 중괄호 블록을 사용할 경우 yield를 통해 값을 반환할 수 있다. 결과값이 결정되지 않는 상황을 방지하기 위해default가 반드시 존재해야 한다.  

Java 11 이전

// 변수에는 정수 타입(byte, char, short, int, long)과 문자열 타입(String) 변수를 사용할 수 있다.

switch(num){
	case 1:
    	...
    	break;
    case 2:
    	...
    	break;
    ...
    default:
    ...
}

switch(grade){
	case 'A':
    case 'a':
    	...
        break;
    ...
    default:
    	...
}

Java 12 이후

switch(grade){
	case 'A', 'a' -> {
    	...
    }
    case 'B', 'b' -> {
    
    }
    default -> {
    	...
    }
}

switch(grade){
	case 'A', 'a' -> System.out.println("...");
    case 'B', 'b' -> ...
    default -> ...;
}

// switch expressions을 사용하면 스위치된 값을 변수에 바로 대입 가능.
int a = switch(grade){
	case 'A' -> 100;
    ...
    default -> 10;
}

 

Java 13 이후

타입변수 = switch(grade){
	case "값1" -> 변수값;
    case "값2" -> {
    	...;
        yield 변수값; // Java13 이후 yield 사용 가능
    }
    default -> 변수값;
}

 

반복문의 루프 제어: Label

for, while, do-while의 기본 구조와 break, continue는 익숙하지만, Label을 사용하는 방식이다.

Outter: for(int i=0; i<10; i++) {
    for(int j=0; j<10; j++) {
        if(condition) break Outter; // 이름 붙은 바깥쪽 for문을 즉시 종료
    }
}
// 여기로!

위와 같은 형태로 쓰면, condition이 true가 되면 "여기로!"라고 표시된 곳으로 이동하게 된다. 스파게티 코드를 만들기에 좋은 도구같기도 하다...  

참고

  • Math.random():  0.0 이상 1.0 미만의 double 타입 난수 리턴함
    • 다양하게 응용해서 사용할 수 있다. 예: int num = (int) (Math.random() * n) + start; (start부터 n개의 정수 중 하나)

 

참고자료