package access;
public class Speaker {
int volume;
Speaker(int volume) {
this.volume = volume;
}
void volumeUp() {
if (volume >= 100) {
System.out.println("음량을 증가할 수 없습니다. 최대 음량입니다.");
} else {
volume += 10;
System.out.println("음량을 10 증가합니다.");
}
}
void volumeDown() {
volume -= 10;
System.out.println("volumeDown 호출");
}
void showVolume() {
System.out.println("현재 음량: " + volume);
}
}
위 처럼 Speaker 클래스의 volume은 100을 넘어서는 안되는 상황이다.
package access;
public class SpeakerMain {
public static void main(String[] args) {
Speaker speaker = new Speaker(90);
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
//필드에 직접 접근
System.out.println("volume 필드 직접 접근 수정");
speaker.volume = 200;
speaker.showVolume();
}
}
하지만 위 코드처럼 Speaker 클래스를 사용할 때, volume에 직접 접근해서 값을 바꿔버리면 volumeUp 메서드를 만들어도 의미가 없다.
그래서 상황에 따라 특정 필드나 메서드에 접근하는 것을 제어해야한다.
→ 그래서 접근제어자가 필요한 것이다.
package access;
public class Speaker {
private int volume;
Speaker(int volume) {
this.volume = volume;
}
// ...
}
package access;
public class SpeakerMain {
public static void main(String[] args) {
Speaker speaker = new Speaker(90);
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
//필드에 직접 접근
System.out.println("volume 필드 직접 접근 수정");
speaker.volume = 200; // -> 컴파일 에러 발생!
speaker.showVolume();
}
}
speakr.volume=200;
코드에서 컴파일에러가 발생하며 직접 접근하지 못하게 된다.private
: 모든 외부 호출을 막는다.default
(package-private): 같은 패키지안에서 호출은 허용한다.protected
: 같은 패키지안에서 호출은 허용한다. 패키지가 달라도 상속 관계의 호출은 허용한다.public
: 모든 외부 호출을 허용한다.package access.a;
public class AccessData {
public int publicField;
int defaultField;
private int privateField;
public void publicMethod() {
System.out.println("publicMethod 호출 " + publicField);
}
void defaultMethod() {
System.out.println("defaultMethod 호출 " + defaultField);
}
private void privateMethod() {
System.out.println("privateMethod 호출 " + privateField);
}
public void innerAccess() {
System.out.println("내부 호출");
publicField = 100;
defaultField = 200;
privateField = 300;
publicMethod();
defaultMethod();
privateMethod();
}
}
package access.a;
public class AccessInnerMain {
public static void main(String[] args) {
AccessData data = new AccessData();
//public 호출 가능
data.publicField = 1;
data.publicMethod();
//같은 패키지 default 호출 가능
data.defaultField = 2;
data.defaultMethod();
//private 호출 불가
//data.privateField = 3;
//data.privateMethod();
data.innerAccess();
}
}
public
과 default
접근제어자만 사용할 수 있다package access.a;
public class PublicClass {
public static void main(String[] args) {
PublicClass publicClass = new PublicClass();
DefaultClass1 class1 = new DefaultClass1();
DefaultClass2 class2 = new DefaultClass2();
}
}
class DefaultClass1 {
}
class DefaultClass2 {
}
PublicClass
클래스는 public
이기에 내/외부 패키지에서 모두 호출 가능DefaultClass1
, DefaultClass2
는 default
이기에 내부 패키지에서만 호출 가능강의를 들으며 생긴 의혹
- 위 코드에서 main 메서드 안에서 바로 PublicClass를 생성하는 모습을 볼 수 있다. 평소에 저런 모습의 코드는 보지 못했었기에 테스트를 해보았다.
package practice; public class Practice { int a; Practice(int a){ this.a = a; } public static void main(String[] args) { Practice practice = new Practice(10); System.out.println(practice.a); // 출력값 : 10 } }
- main 메서드도 결국 클래스에서 생성된 하나의 메서드라는 관점을 다시금 알게 되었다. 단지 java 실행 시, 작동의 시작점인 메서드라는 것일 뿐!
command + N
캡슐화는 쉽게 이야기해서 “속성”과 “기능”을 하나로 묶고, 외부에 꼭 필요한 기능만 노출하고 나머지는 모두 내부로 숨기는 것이다.
예시 코드
package access;
public class BankAccount {
private int balance;
public BankAccount() {
balance = 0;
}
// public 메서드: deposit
public void deposit(int amount) {
if (isAmountValid(amount)) {
balance += amount;
} else {
System.out.println("유효하지 않은 금액입니다.");
}
}
// public 메서드: withdraw
public void withdraw(int amount) {
if (isAmountValid(amount) && balance - amount >= 0) {
balance -= amount;
} else {
System.out.println("유효하지 않음 금액이거나 잔액이 부족합니다.");
}
}
// public 메서드: getBalance
public int getBalance() {
return balance;
}
private boolean isAmountValid(int amount) {
// 금액이 0보다 커야함
return amount > 0;
}
}
만약 balance를 노출하면 어떻게 될까?
만약 isAmouontValid를 노출하면 어떻게 될까?
package access.ex;
public class ShoppingCartMain {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
Item item1 = new Item("마늘", 2000, 2);
Item item2 = new Item("상추", 3000, 4);
cart.addItem(item1);
cart.addItem(item2);
cart.displayItems();
}
}
package access.ex;
public class Item {
private String name;
private int price;
private int quantity;
// 나머지 코드를 완성하시오
}
package access.ex;
public class ShoppingCart {
private Item[] items = new Item[10];
private int itemCount;
// 나머지 코드를 완성하시오
}
장바구니 상품 출력
상품명:마늘, 합계:4000
상품명:상추, 합계:12000
전체 가격 합:16000
package access.ex;
public class Item {
private String name;
private int price;
private int quantity;
public Item(String name, int price, int quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
public String getName() {
return name;
}
public int getTotalPrice() {
return price * quantity;
}
}
Item
의 가격과 수량을 곱하면 각 상품별 합계를 구할 수 있다. price
와 quantity
를 외부에 반환한 다음에 외부에서 곱해서 상품별 합계를 구해도 되지만, getTotalPrice()
메서드를 제공하면 외부에서는 단순히 이 메서드를 호출하면 된다.package access.ex;
public class ShoppingCart {
private Item[] items = new Item[10];
private int itemCount;
public void addItem(Item item) {
if (itemCount >= items.length) {
System.out.println("장바구니가 가득 찼습니다.");
return;
}
items[itemCount] = item;
itemCount++;
}
public void displayItems() {
System.out.println("장바구니 상품 출력");
for (int i = 0; i < itemCount; i++) {
Item item = items[i];
System.out.println("상품명:" + item.getName() + ", 합계:" + item.getTotalPrice());
}
System.out.println("전체 가격 합:" + calculateTotalPrice());
}
private int calculateTotalPrice() {
int totalPrice = 0;
for (int i = 0; i < itemCount; i++) {
Item item = items[i];
totalPrice += item.getTotalPrice();
}
return totalPrice;
}
}
calculateTotalPrice()
: 이 메서드 내부에서만 사용되므로 private
접근 제어자를 사용한다.