변수와 타입을 "선언하고 출력하는" 데서 한 걸음 나아가, 메서드 매개변수로 넘기고, 문자열에서 변환하고, 경계값에서 무슨 일이 생기는지 직접 확인합니다.
예제 1은 값을 변수에 담아 바로 출력했습니다. 같은 계산을 여러 상품에 반복하려면 매개변수를 받는 메서드로 빼는 편이 낫습니다. 매개변수도 변수이므로 타입을 정해야 하고, 곱셈 결과가 int를 넘을 수 있으면 long으로 받습니다.
public class OrderLine {
static String line(String product, int qty, int unitPrice) {
long subtotal = (long) qty * unitPrice; // 큰 수량·단가에 대비해 long으로 계산
return product + " x" + qty + " = " + subtotal + "원";
}
public static void main(String[] args) {
System.out.println(line("노트북", 1, 1_200_000));
System.out.println(line("마우스", 3, 25_000));
System.out.println(line("서버 라이선스", 2_000, 1_500_000)); // int였다면 오버플로우
// 출력: 노트북 x1 = 1200000원
// 출력: 마우스 x3 = 75000원
// 출력: 서버 라이선스 x2000 = 3000000000원
}
}예제 4의 정수 나눗셈 문제를 목적별로 다른 타입으로 풉니다. 소수점이 필요 없으면 정수 산술만으로, 화면 표시용이면 double, 정확한 반올림이 필요하면 BigDecimal을 씁니다.
import java.math.BigDecimal;
import java.math.RoundingMode;
public class RateThreeWays {
public static void main(String[] args) {
int sold = 37, total = 50;
int percent = sold * 100 / total; // 1) 정수 산술: 곱셈을 먼저
System.out.println("정수: " + percent + "%"); // 출력: 정수: 74%
double rate = (double) sold / total * 100; // 2) double: 캐스팅을 먼저
System.out.printf("double: %.1f%%%n", rate); // 출력: double: 74.0%
BigDecimal exact = BigDecimal.valueOf(sold * 100) // 3) BigDecimal: 자릿수와 반올림 방식 지정
.divide(BigDecimal.valueOf(total), 2, RoundingMode.HALF_UP);
System.out.println("BigDecimal: " + exact + "%"); // 출력: BigDecimal: 74.00%
// 나누어떨어지지 않는 값에서 차이가 드러남
sold = 33; total = 7;
System.out.println(sold * 100 / total); // 출력: 471
System.out.printf("%.2f%n", (double) sold / total * 100); // 출력: 471.43
System.out.println(BigDecimal.valueOf(sold * 100)
.divide(BigDecimal.valueOf(total), 2, RoundingMode.HALF_UP)); // 출력: 471.43
}
}파일이나 네트워크에서 읽은 데이터는 전부 문자열로 들어옵니다. 각 칸을 알맞은 타입으로 바꿔야 계산할 수 있고, 다시 내보낼 때는 문자열로 이어 붙입니다. 21억을 넘는 값을 Integer.parseInt로 바꾸면 NumberFormatException이 나므로 Long.parseLong을 씁니다.
public class CsvParse {
public static void main(String[] args) {
String line = "P-1001,250,1.75,false,5200000000";
String[] cols = line.split(",");
String code = cols[0];
int stock = Integer.parseInt(cols[1]);
double weight = Double.parseDouble(cols[2]);
boolean soldOut = Boolean.parseBoolean(cols[3]);
long sales = Long.parseLong(cols[4]); // int 범위 초과 → Long.parseLong
stock -= 50;
System.out.println(code + " 남은 재고 " + stock + ", 총 무게 " + stock * weight + "kg");
System.out.println("품절: " + soldOut + ", 누적 판매: " + sales);
// 출력: P-1001 남은 재고 200, 총 무게 350.0kg
// 출력: 품절: false, 누적 판매: 5200000000
String back = code + "," + stock + "," + weight + "," + soldOut + "," + sales; // 다시 문자열로
System.out.println(back);
// 출력: P-1001,200,1.75,false,5200000000
}
}예제 3의 오버플로우를 여러 타입에서 확인합니다. 정수끼리의 강제 캐스팅은 위쪽 비트를 잘라내고, 실수→정수 캐스팅은 범위 끝에서 멈추며, 실수의 0 나누기는 예외 대신 Infinity/NaN을 돌려줍니다. 규칙이 타입마다 다르니 결과를 외우기보다 한 번 눈으로 보는 것이 빠릅니다.
public class Boundaries {
public static void main(String[] args) {
System.out.println((byte) 200); // 출력: -56 (아래 8비트만 남김)
System.out.println((short) 70000); // 출력: 4464 (70000 - 65536)
System.out.println((int) 3_000_000_000L); // 출력: -1294967296
System.out.println((int) 1e20); // 출력: 2147483647 (실수→정수는 최댓값에서 멈춤)
System.out.println((long) -1e20); // 출력: -9223372036854775808
System.out.println((int) Double.NaN); // 출력: 0
System.out.println(Integer.MIN_VALUE - 1); // 출력: 2147483647 (아래로 넘치면 위에서 나옴)
System.out.println(Math.abs(Integer.MIN_VALUE)); // 출력: -2147483648 (양수로 만들 수 없음)
System.out.println((char) 44032); // 출력: 가 (유니코드 AC00)
System.out.println(1.0 / 0); // 출력: Infinity
System.out.println(0.0 / 0); // 출력: NaN
try {
System.out.println(1 / 0);
} catch (ArithmeticException e) {
System.out.println("정수 0 나누기: " + e.getMessage()); // 출력: 정수 0 나누기: / by zero
}
}
}2.9절의 "돈은 double 금지"를 실제 숫자로 확인합니다. double은 빠르지만 오차가 누적되고, 원 단위 long은 빠르고 정확하지만 소수점 표현을 직접 관리해야 하며, BigDecimal은 정확하고 편하지만 느리고 코드가 길어집니다. 소액 결제나 통계처럼 오차가 허용되면 long, 회계·정산이면 BigDecimal이 일반적인 선택입니다.
import java.math.BigDecimal;
public class MoneyStorage {
public static void main(String[] args) {
// 3,300원의 부가세 10% 포함 가격
double d = 3300 * 1.1;
long won = 3300 * 110 / 100; // 원 단위 정수로만 계산
BigDecimal bd = new BigDecimal("3300").multiply(new BigDecimal("1.1"));
System.out.println("double : " + d); // 출력: double : 3630.0000000000005
System.out.println("long(원) : " + won); // 출력: long(원) : 3630
System.out.println("BigDecimal: " + bd); // 출력: BigDecimal: 3630.0
// 0.1원을 1,000번 누적하면 오차가 눈에 보인다
double sumD = 0;
long sumL = 0; // 단위를 0.1원으로 잡아 정수로 누적
BigDecimal sumB = BigDecimal.ZERO;
for (int i = 0; i < 1000; i++) {
sumD += 0.1;
sumL += 1;
sumB = sumB.add(new BigDecimal("0.1"));
}
System.out.println(sumD + " / " + sumL / 10.0 + " / " + sumB);
// 출력: 99.9999999999986 / 100.0 / 100.0
}
}