public class Account {
private long balance;
public long getBalance() { return balance; }
public void setBalance(long balance) { this.balance = balance; } // 검증 우회 통로
}
account.setBalance(-1_000_000); // 잔액이 음수!private + 무조건 setter는 public 필드와 같습니다.
✅ setter를 지우고 deposit, withdraw 같은 업무 메서드에 검증을 넣습니다.
private int[] scores;
public int[] getScores() { return scores; }
int[] s = student.getScores();
s[0] = 100; // 학생 내부 점수가 바뀜✅ return scores.clone(); (컬렉션은 List.copyOf(list) 또는 Collections.unmodifiableList).
public final class Config {
private final String[] hosts;
public Config(String[] hosts) { this.hosts = hosts; } // 호출자가 나중에 hosts[0]을 바꾸면?
public String[] getHosts() { return hosts; }
}final은 hosts가 다른 배열을 가리키지 못한다는 뜻일 뿐, 배열 내용은 바뀝니다.
✅ 생성자에서 this.hosts = hosts.clone();, getter에서 return hosts.clone();.
public Member(int age) {
if (age < 0) throw new IllegalArgumentException();
this.age = age;
}
public void setAge(int age) { this.age = age; } // 검증 없음 → 나중에 -1 들어감✅ 검증을 setter에 두고 생성자가 setter를 호출하게 하거나, 검증을 private 메서드로 빼서 둘 다 호출합니다.
record User(String name) { }
User u = new User("Kim");
u.getName(); // 컴파일 오류: cannot find symbol✅ u.name(). record의 접근자는 필드 이름 그대로입니다.
record Cart(Item[] items) { }
// 상품을 추가하려면? items 배열을 바꿔야 하는데 필드는 final...record는 값 객체용입니다. 장바구니·계좌처럼 상태가 변하는 것은 일반 클래스로 캡슐화합니다.
✅ 예제 5처럼 클래스 ShoppingCart 안에 record Item을 요소로 두는 구조.
public class OrderService {
public int retryCount = 3; // "설정이니까 public"
public void internalCleanup() { } // "나중에 쓸지 몰라서 public"
}한번 public이 되면 누군가 의존하기 시작해서 다시 닫을 수 없습니다.
✅ private에서 시작. 정말 필요할 때 넓힙니다. 설정값은 생성자 매개변수나 private static final.