공공부하자개발 · 영어 학습 노트
자바
초급자바의 뼈대0/8 완료
  • 00개발 환경과 첫 프로그램
  • 01변수, 데이터 타입, 형변환
  • 02연산자와 제어문
  • 031차원 · 2차원 배열
  • 04클래스, 객체, 생성자, 오버로딩
  • 05접근 제어자와 캡슐화
  • 06메서드 활용 패턴 (초급)
  • 07enum, 패키지와 import, static 과 final
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 초급 › 05 / 8

접근 제어자와 캡슐화

섹션 7진행 0 / 8
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

6. 연습 문제

문제 1

Product 클래스를 캡슐화하여 작성하세요. 필드: code(변경 불가), name, price, stock. 제약: 가격은 0 이상, 재고는 0 이상, 이름은 공백 불가. setPrice, rename, addStock(int), removeStock(int)(부족 시 IllegalStateException)를 구현하고 setStock은 만들지 마세요.

정답 보기
java
public class Product {
    private final String code;
    private String name;
    private int price;
    private int stock;

    public Product(String code, String name, int price, int stock) {
        if (code == null || code.isBlank()) throw new IllegalArgumentException("코드 필수");
        if (stock < 0) throw new IllegalArgumentException("재고는 0 이상");
        this.code = code;
        rename(name);
        setPrice(price);
        this.stock = stock;
    }

    public void rename(String name) {
        if (name == null || name.isBlank()) throw new IllegalArgumentException("이름 필수");
        this.name = name.trim();
    }

    public void setPrice(int price) {
        if (price < 0) throw new IllegalArgumentException("가격은 0 이상: " + price);
        this.price = price;
    }

    public void addStock(int qty) {
        if (qty <= 0) throw new IllegalArgumentException("입고 수량은 양수");
        stock += qty;
    }

    public void removeStock(int qty) {
        if (qty <= 0) throw new IllegalArgumentException("출고 수량은 양수");
        if (qty > stock) throw new IllegalStateException("재고 부족: " + stock + " < " + qty);
        stock -= qty;
    }

    public String getCode() { return code; }
    public String getName() { return name; }
    public int getPrice() { return price; }
    public int getStock() { return stock; }

    public static void main(String[] args) {
        Product p = new Product("P-1", " 키보드 ", 89_000, 10);
        p.removeStock(4);
        p.addStock(1);
        System.out.println(p.getName() + " " + p.getPrice() + "원 재고 " + p.getStock());
        // 출력: 키보드 89000원 재고 7
        try {
            p.removeStock(100);
        } catch (IllegalStateException e) {
            System.out.println(e.getMessage());     // 출력: 재고 부족: 7 < 100
        }
    }
}

문제 2

위도(-9090)와 경도(-180180)를 갖는 불변 GeoPoint를 record로 작성하고, 범위를 컴팩트 생성자에서 검증하세요. 두 지점의 직선 거리(단순 유클리드)를 반환하는 distanceTo 메서드를 추가하세요.

정답 보기
java
public class Exercise2 {
    record GeoPoint(double lat, double lon) {
        GeoPoint {
            if (lat < -90 || lat > 90) throw new IllegalArgumentException("위도 범위 오류: " + lat);
            if (lon < -180 || lon > 180) throw new IllegalArgumentException("경도 범위 오류: " + lon);
        }

        double distanceTo(GeoPoint o) {
            return Math.hypot(lat - o.lat, lon - o.lon);
        }
    }

    public static void main(String[] args) {
        GeoPoint seoul = new GeoPoint(37.5665, 126.9780);
        GeoPoint busan = new GeoPoint(35.1796, 129.0756);
        System.out.println(seoul);                              // 출력: GeoPoint[lat=37.5665, lon=126.978]
        System.out.printf("%.4f%n", seoul.distanceTo(busan));   // 출력: 3.1919
        System.out.println(seoul.equals(new GeoPoint(37.5665, 126.9780)));   // 출력: true
        try {
            new GeoPoint(91, 0);
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());                 // 출력: 위도 범위 오류: 91.0
        }
    }
}

문제 3

다음 클래스의 캡슐화 문제점을 4가지 이상 찾아 고치세요.

java
public class Student {
    public String name;
    public int[] scores;
    public Student(String name, int[] scores) { this.name = name; this.scores = scores; }
    public int[] getScores() { return scores; }
    public void setScores(int[] scores) { this.scores = scores; }
    public double average() {
        int s = 0; for (int v : scores) s += v; return (double) s / scores.length;
    }
}
정답 보기
java
import java.util.Arrays;

public class Student {
    // 문제 1: 필드가 public → private으로
    // 문제 2: 생성자가 배열을 그대로 저장 → 호출자가 나중에 바꾸면 내부 상태 변경 (방어적 복사 필요)
    // 문제 3: getScores가 내부 배열을 그대로 반환 → 외부에서 점수 조작 가능
    // 문제 4: setScores에 검증 없음 (null, 빈 배열, 0~100 범위)
    // 문제 5: average()가 빈 배열이면 0으로 나눔 → NaN
    private String name;
    private int[] scores;

    public Student(String name, int[] scores) {
        if (name == null || name.isBlank()) throw new IllegalArgumentException("이름 필수");
        this.name = name;
        setScores(scores);
    }

    public void setScores(int[] scores) {
        if (scores == null || scores.length == 0) throw new IllegalArgumentException("점수 최소 1개");
        for (int v : scores) {
            if (v < 0 || v > 100) throw new IllegalArgumentException("점수 범위 0~100: " + v);
        }
        this.scores = scores.clone();       // 방어적 복사 (입력)
    }

    public int[] getScores() { return scores.clone(); }   // 방어적 복사 (출력)
    public String getName() { return name; }

    public double average() {
        int s = 0;
        for (int v : scores) s += v;
        return (double) s / scores.length;   // setScores가 빈 배열을 막으므로 안전
    }

    public static void main(String[] args) {
        int[] raw = {80, 90, 100};
        Student st = new Student("Kim", raw);
        raw[0] = 0;                                          // 외부 배열 변경
        st.getScores()[1] = 0;                               // 반환 배열 변경
        System.out.println(Arrays.toString(st.getScores())); // 출력: [80, 90, 100]  (둘 다 영향 없음)
        System.out.println(st.average());                    // 출력: 90.0
        try {
            st.setScores(new int[]{101});
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());              // 출력: 점수 범위 0~100: 101
        }
    }
}
연습 문제
  • 문제 1
  • 문제 2
  • 문제 3
이전 섹션5 자주 하는 실수 (Tip)6 / 7다음 섹션7 정리