주문 금액, 회원 등급, 지역에 따라 배송비를 계산합니다. 단축 평가로 null을 방어하고, 얼리 리턴으로 중첩을 줄입니다.
public class ShippingFee {
static int calculate(String grade, int amount, String region) {
if (region == null || region.isBlank()) {
return -1; // 유효하지 않은 입력
}
if (grade != null && grade.equals("VIP")) { // null 방어 후 equals
return 0;
}
boolean isRemote = region.equals("제주") || region.equals("울릉");
int base = amount >= 50_000 ? 0 : 3_000;
int extra = isRemote ? 3_000 : 0;
return base + extra;
}
public static void main(String[] args) {
System.out.println(calculate("VIP", 10_000, "서울")); // 출력: 0
System.out.println(calculate("GOLD", 60_000, "서울")); // 출력: 0
System.out.println(calculate("GOLD", 30_000, "서울")); // 출력: 3000
System.out.println(calculate("GOLD", 30_000, "제주")); // 출력: 6000
System.out.println(calculate(null, 60_000, "제주")); // 출력: 3000
System.out.println(calculate("GOLD", 30_000, null)); // 출력: -1
}
}옛 switch와 새 switch를 나란히 비교하고, yield와 패턴 매칭을 보여 줍니다.
public class OrderStatus {
// 옛 방식: break 필수, 변수 밖에 선언
static String legacy(String status) {
String msg;
switch (status) {
case "PAID":
case "PREPARING":
msg = "준비 중";
break;
case "SHIPPED":
msg = "배송 중";
break;
case "DELIVERED":
msg = "배송 완료";
break;
default:
msg = "알 수 없음";
}
return msg;
}
// 새 방식: 값을 바로 반환, fall-through 없음
static String modern(String status) {
return switch (status) {
case "PAID", "PREPARING" -> "준비 중";
case "SHIPPED" -> "배송 중";
case "DELIVERED" -> {
String base = "배송 완료";
yield base + " (리뷰 작성 가능)";
}
default -> "알 수 없음";
};
}
// 패턴 매칭: 타입과 조건으로 분기
static String describe(Object event) {
return switch (event) {
case Integer qty when qty > 100 -> "대량 주문 " + qty + "개";
case Integer qty -> "일반 주문 " + qty + "개";
case String note -> "메모: " + note;
case null -> "이벤트 없음";
default -> "기타: " + event.getClass().getSimpleName();
};
}
public static void main(String[] args) {
System.out.println(legacy("PAID")); // 출력: 준비 중
System.out.println(modern("DELIVERED")); // 출력: 배송 완료 (리뷰 작성 가능)
System.out.println(modern("CANCELLED")); // 출력: 알 수 없음
System.out.println(describe(150)); // 출력: 대량 주문 150개
System.out.println(describe(3)); // 출력: 일반 주문 3개
System.out.println(describe("문 앞에 놓아주세요")); // 출력: 메모: 문 앞에 놓아주세요
System.out.println(describe(null)); // 출력: 이벤트 없음
System.out.println(describe(3.5)); // 출력: 기타: Double
}
}점수 배열에서 결시(-1)를 건너뛰고 평균과 최고점을 구합니다. 향상된 for로 읽고, 인덱스가 필요한 곳은 일반 for를 씁니다.
public class ScoreStats {
public static void main(String[] args) {
int[] scores = {88, -1, 92, 75, -1, 60, 99};
int sum = 0, count = 0, max = Integer.MIN_VALUE, maxIndex = -1;
for (int i = 0; i < scores.length; i++) {
if (scores[i] < 0) {
continue; // 결시 건너뛰기
}
sum += scores[i];
count++;
if (scores[i] > max) {
max = scores[i];
maxIndex = i;
}
}
double avg = count == 0 ? 0 : (double) sum / count;
System.out.printf("응시 %d명, 평균 %.1f, 최고 %d점(%d번)%n", count, avg, max, maxIndex);
// 출력: 응시 5명, 평균 82.8, 최고 99점(6번)
// 등급 분포: 향상된 for
int a = 0, b = 0, c = 0;
for (int s : scores) {
if (s < 0) continue;
if (s >= 90) a++;
else if (s >= 80) b++;
else c++;
}
System.out.println("A=" + a + " B=" + b + " C이하=" + c);
// 출력: A=2 B=1 C이하=2
}
}문자열 로그를 줄 단위로 읽다가 "FATAL"을 만나면 즉시 중단합니다. 언제 끝날지 모르는 반복에는 while이 맞습니다.
public class LogScanner {
public static void main(String[] args) {
String log = """
INFO 서버 시작
WARN 디스크 80%
ERROR DB 연결 재시도
FATAL DB 연결 실패
INFO 이 줄은 읽지 않음
""";
String[] lines = log.split("\n");
int idx = 0, errorCount = 0;
while (idx < lines.length) {
String line = lines[idx++];
if (line.startsWith("FATAL")) {
System.out.println("치명적 오류 발견, 스캔 중단: " + line);
break;
}
if (line.startsWith("ERROR")) {
errorCount++;
}
}
System.out.println("읽은 줄=" + idx + " ERROR=" + errorCount);
// 출력: 치명적 오류 발견, 스캔 중단: FATAL DB 연결 실패
// 출력: 읽은 줄=4 ERROR=1
// do-while: 최소 1회 실행 — 재시도 로직
int attempt = 0;
boolean connected;
do {
attempt++;
connected = attempt >= 3; // 3번째에 성공한다고 가정
System.out.println("연결 시도 " + attempt + (connected ? " 성공" : " 실패"));
} while (!connected && attempt < 5);
// 출력: 연결 시도 1 실패
// 출력: 연결 시도 2 실패
// 출력: 연결 시도 3 성공
}
}2차원 배열에서 첫 번째 빈 좌석을 찾으면 두 반복문을 한 번에 탈출합니다.
public class SeatFinder {
public static void main(String[] args) {
boolean[][] seats = {
{true, true, true},
{true, false, true}, // (1,1)이 빈 좌석
{false, false, false},
};
int foundRow = -1, foundCol = -1;
search:
for (int r = 0; r < seats.length; r++) {
for (int c = 0; c < seats[r].length; c++) {
if (!seats[r][c]) {
foundRow = r;
foundCol = c;
break search; // 바깥 for까지 탈출
}
}
}
System.out.println("첫 빈 좌석: " + foundRow + "행 " + foundCol + "열");
// 출력: 첫 빈 좌석: 1행 1열
// 비트 연산으로 권한 관리
final int READ = 1, WRITE = 2, DELETE = 4;
int perm = READ | WRITE;
System.out.println("쓰기 가능: " + ((perm & WRITE) != 0)); // 출력: 쓰기 가능: true
System.out.println("삭제 가능: " + ((perm & DELETE) != 0)); // 출력: 삭제 가능: false
perm |= DELETE;
perm &= ~WRITE;
System.out.println(Integer.toBinaryString(perm)); // 출력: 101
}
}