김영한의 실전 자바 - 기본편 강의 | 김영한 - 인프런
김영한 | 실무에 필요한 자바 객체 지향의 핵심 개념을 예제 코드를 통해 쉽게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문
www.inflearn.com
접근 제어자
자바는 `public`, `private`와 같은 접근 제어자(access modifier)를 제공한다. 접근 제어자를 사용하면 해당 클래스 외부에서 특정 필드나 메서드에 접근하는 것을 허용하거나 제한할 수 있다.
이런 접근 제어자가 왜 필요하는 지 예제를 통해 이유를 알아보자.
스피커 객체를 만들어보자.
스피커는 음량을 높이고, 내리고, 현재 음량을 확인할 수 있는 단순한 기능을 제공한다.
요구사항 대로 스피커의 음량은 100까지만 증가할 수 있다. 절대 100을 넘어가면 안된다.
public class Speaker {
private 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);
}
}
생성자를 통해 초기 음량 값을 지정할 수 있다.
`volumeUp()` 메서드를 보면 음량을 한 번에 10씩 증가한다. 단 음량이 100을 넘게 되면 더는 음량이 증가하지 않는다.
시간이 흘러 업그레이드된 스피커가 출시되었다. 새로운 개발자가 기존 코드를 이어받아 개발했지만, 요구사항을 잘 몰랐다. 코드를 실행하니 음량이 100을 넘지 않는 것을 보고 소리를 더 높이려 했다.
Speaker 클래스에서 volume 필드를 직접 사용할 수 있었다. 그래서 `volume` 필드의 값을 200으로 설정했고, 그 순간 스피커 부품에 과부하가 걸려 폭발했다.
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` 객체를 사용하는 사용자는 `Speaker`의 `volume` 필드와 메서드에 모두 접근할 수 있다.
앞서 `volumeUp()`과 같은 메서드를 만들어서 음량이 100을 넘지 못하도록 기능을 개발했지만 소용이 없다. 왜냐하면 `Speaker`를 사용하는 입장에서는 `volume` 필드에 직접 접근해서 원하는 값을 설정할 수 있기 때문이다.
이런 문제를 근본적으로 해결하기 위해서는 `volume` 필드의 외부 접근을 막을 수 있는 방법이 필요하다.
Speaker - volume 접근 제어자를 private으로 수정
package access;
public class Speaker {
private int volume; //private 사용
...
}
- `private` 접근 제어자는 모든 외부 호출을 막는다. 따라서 `private`이 붙은 경우 해당 클래스 내부에서만 호출할 수 있다.
그림을 보면 `volume` 필드를 `private`을 사용해서 `Speaker` 내부에 숨겼다. `volume` 필드는 이제 `Speaker` 내부에서만 접근할 수 있다.
다시 `SpeakerMain` 코드를 실행해보면 `speaker.volume = 200` 부분에서 오류가 발생한다.
volume has private access in access.Speaker
이게 `Speaker` 외부에서 `volume` 필드에 직접 접근하는 것은 불가능하다.
접근 제어자 종류
자바는 4가지 종류의 접근 제어자를 제공한다.
- `private`: 모든 외부 호출을 막는다.
- `default`(package-private): 같은 패키지 안에서 호출은 허용한다.
- `protected`: 같은 패키지 안에서 호출은 허용한다. 패키지가 달라도 상속 관계의 호출은 허용한다.
- `public`: 모든 외부 호출을 허용한다.
순서대로 `private`이 가장 많이 차단하고, `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`이다.
- `innerAccess()`가 있는데, 이 메서드는 내부 호출을 보여준다. 내부 호출은 자기 자신에게 접근하는 것이다. 따라서 `private`를 포함한 모든 곳에 접근할 수 있다.
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();
}
}
- 패키지 위치는 `package access.a`이다.
- `public`은 모든 접근을 허용하기 때문에 필드, 메서드 모두 접근 가능하다.
- `default`는 같은 패키지에서 접근할 수 있다. `AccessInnerMain`은 `AccessData`와 같은 패키지이다. 따라서 `default` 접근 제어자에 접근할 수 있다.
- `private`은 `AccessData` 내부에서만 접근할 수 있다. 따라서 호출 불가다.
- `AccessData.innerAccess()` 메서드는 `public`이다. 따라서 외부에서 호출할 수 있다.
- `innerAccess()` 메서드는 외부에서 호출되었지만 `innerAccess()` 메서드는 `AccessData`에 포함되어 있다. 이 메서드는 자신의 `private` 필드와 메서드에 모두 접근할 수 있다.
클래스 레벨
- 클래스 레벨의 접근 제어자는 `public`, `default`만 사용할 수 있다.
- `private`, `protected`는 사용할 수 없다.
- `public` 클래스는 반드시 파일명과 이름이 같아야 한다.
- 하나의 자바 파일에 `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 {
}
캡슐화
캡슐화(Encapsulation)는 객체 지향 프로그래밍의 중요한 개념 중 하나다. 캡슐화는 데이터와 해당 데이터를 처리하는 메서드를 하나로 묶어서 외부에서의 접근을 제한하는 것을 말한다. 캡슐화를 통해 데이터의 직접적인 변경을 방지하거나 제한할 수 있다.
쉽게 이야기해서 속성과 기능을 하나로 묶고, 외부에 꼭 필요한 기능만 노출하고 나머지는 모두 내부로 숨기는 것이다.
1. 데이터를 숨겨라
객체에는 속성(데이터)와 기능(메서드)가 있다. 캡슐화에서 가장 필수로 숨겨야 하는 것은 속성(데이터)이다.
객체 내부의 데이터를 외부에서 함부로 접근하게 두면, 클래스 안에서 데이터를 다루는 모든 로직을 무시하고 데이터를 변경할 수 있다. 결국 모든 안전망을 다 빠져나가게 된다. 따라서 캡슐화가 깨진다.
객체의 데이터는 객체가 제공하는 기능인 메서드를 통해서 접근해야 한다.
2. 기능을 숨겨라
객체의 기능 중에서 외부에서 사용하지 않고 내부에서만 사용하는 기능들이 있다. 이런 기능도 모두 감추는 것이 좋다.
우리가 자동차를 운전하기 위해 자동차가 제공하는 복잡한 엔진 조절 기능, 배기 기능까지 우리가 알 필요는 없다. 우리는 단지 엑셀과 핸들 정도의 기능만 알면 된다. 만약 사용자에게 이런 기능까지 모두 알려준다면, 사용자가 자동차에 대해 너무 많은 것을 알아야 한다.
정리하자면 데이터는 모두 숨기고, 기능은 꼭 필요한 기능만 노출하는 것이 좋은 캡슐화다.
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;
}
}
private
- `balance`: 데이터 필드는 외부에 직접 노출하지 않는다. `BankAccount`가 제공하는 메서드를 통해서만 접근할 수 있다.
- `isAmoutValid`: 입력 금액을 검증하는 기능은 내부에서만 필요한 기능이다. 따라서 `private`를 사용했다.
public
- `deposit()`: 입금
- `withdraw()`: 출금
- `getBalance()`: 잔고
`BankAccount`를 사용하는 입장에서는 단 3가지 메서드만 알면 된다. 나머지 복잡한 내용은 모두 `BankAccount`에 숨어있다.
'Java' 카테고리의 다른 글
[Java] try-with-resources (0) | 2024.11.24 |
---|---|
[Java] Stream (1) | 2024.11.17 |
[Java] static (0) | 2024.11.08 |
[Java] Enum이란? (0) | 2024.11.01 |
[AssertJ] Custom Exception (0) | 2024.10.31 |