[Java] 김영한의 실전 자바 기본편 - #6 접근 제어자
[김영한의 실전 자바 - 기본편↗️]을 듣고 정리한 글입니다.
1. 접근 제어자
1.1 접근 제어자가 필요한 이유
자바에서 제공하는 public과 private 같은 접근 제어자는 클래스의 필드나 메서드에 대한 접근을 제한하는 역할을 한다.
스피커 예제에서 볼 수 있듯이, 외부에서 volume
필드에 직접 접근할 수 있도록 허용하면 음량이 100을 초과할 수 있고, 이는 스피커의 부품에 손상을 줄 수 있다.
잘못된 코드 예: 접근 제어자가 없는 경우
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);
}
}
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();
}
}
실행결과
현재 음량: 90
음량을 10 증가합니다.
현재 음량: 100
음량을 증가할 수 없습니다. 최대 음량입니다.
현재 음량: 100
volume 필드 직접 접근 수정
현재 음량: 200
- 위 코드에서
volume
필드는 접근 제어자가 없기 때문에 외부에서 직접 수정할 수 있다. - 이는
volume
값을 임의로200
과 같은 잘못된 값으로 설정할 수 있게 만들어 시스템을 고장 낼 수 있다.
1.2 private 접근 제어자로 문제 해결
이 문제를 해결하려면 volume 필드를 private으로 설정하여 외부에서 직접 접근할 수 없도록 막아야 한다.
이를 통해 개발자가 설정한 규칙(예: 음량은 100을 넘지 않음)을 보장할 수 있다.
package access;
public class Speaker {
private int volume; // volume 필드를 private으로 설정
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);
}
}
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();
// 필드에 직접 접근을 시도 (오류 발생)
// speaker.volume = 200; // 컴파일 오류 발생
speaker.showVolume();
}
}
실행 결과
현재 음량: 90
음량을 10 증가합니다.
현재 음량: 100
음량을 증가할 수 없습니다. 최대 음량입니다.
현재 음량: 100
private
접근 제어자로 인해,volume
필드에 외부에서 접근할 수 없게 되었다.- 이제
volume
필드는 스피커 클래스 내부에서만 수정될 수 있으며,volumeUp()
메서드를 통해서만 음량을 조절할 수 있게 되었다.
2. 접근 제어자의 종류와 역할
2.1 접근 제어자의 종류
자바에는 다양한 접근 제어자가 있으며, 각각의 접근 수준은 다음과 같다.
- private: 해당 클래스 내에서만 접근 가능.
- default (접근 제어자를 지정하지 않은 경우): 같은 패키지 내에서만 접근 가능.
- protected: 같은 패키지 또는 상속받은 클래스에서 접근 가능.
- public: 모든 클래스에서 접근 가능.
➡️ 즉, 중요한 필드나 메서드는 private
으로 선언하여 클래스 외부에서 직접 접근하지 못하도록 하고, public
메서드를 통해서만 필드에 접근하도록 설계해야 한다.
2.2 접근 제어자의 사용 위치
클래스 레벨
public
,default
만 사용할 수 있다.private
과protected
는 클래스 선언에 사용할 수 없다.
필드, 메서드, 생성자
- 모든 접근 제어자(private, default, protected, public)를 사용할 수 있다.
- 필드나 메서드의 접근을 제어하여 외부 클래스나 상속받은 클래스에서 접근을 제한하거나 허용할 수 있다.
2.3 예제 코드로 접근 제어자 확인
패키지:
access.a
package access.a;
public class AccessData {
public int publicField; // public 필드
int defaultField; // default 필드
private int privateField; // private 필드
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();
// innerAccess 메서드로 private 필드와 메서드 접근 가능
data.innerAccess();
}
}
실행 결과
publicMethod 호출 1
defaultMethod 호출 2
내부 호출
publicMethod 호출 100
defaultMethod 호출 200
privateMethod 호출 300
패키지:
access.b
package access.b;
import access.a.AccessData;
public class AccessOuterMain {
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();
// public 메서드인 innerAccess는 호출 가능
data.innerAccess();
}
}
실행 결과
publicMethod 호출 1
내부 호출
publicMethod 호출 100
defaultMethod 호출 200
privateMethod 호출 300
3. 접근 제어자 사용 - 클래스 레벨
클래스 레벨에서는 public, default 두 가지 접근 제어자를 사용할 수 있다.
3.1 클래스 레벨의 접근 제어자 규칙
- public
- 클래스가 public 접근 제어자를 가지면, 어디서든 접근이 가능하다.
- 이 클래스는 반드시 파일 이름과 동일해야 하며, 하나의 자바 파일에는 public 클래스가 하나만 있어야 한다.
- default (package-private)
- 접근 제어자를 명시하지 않으면 default가 적용된다.
- default 클래스는 같은 패키지 내에서만 접근이 가능하며, 다른 패키지에서는 접근할 수 없다.
- private 및 protected
- 클래스 레벨에서 사용할 수 없다. (단, 내부 클래스의 경우 private와 protected 사용 가능)
3.2 클래스 레벨 접근 제어자 예시
public
클래스와default
클래스의 예
PublicClass
는public
접근 제어자를 가지고 있기 때문에 외부 패키지에서도 접근할 수 있다.DefaultClass1
과DefaultClass2
는default
접근 제어자이므로 같은 패키지 내에서만 접근이 가능하다.
// 파일명: PublicClass.java
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 {
}
3.3 외부에서 클래스 접근하기
같은 패키지에 있는 PublicClassInnerMain에서는 default 클래스에 접근이 가능하다.
package access.a;
public class PublicClassInnerMain {
public static void main(String[] args) {
PublicClass publicClass = new PublicClass();
DefaultClass1 class1 = new DefaultClass1();
DefaultClass2 class2 = new DefaultClass2();
}
}
그러나 다른 패키지에 있는 PublicClassOuterMain에서는 default 클래스에 접근할 수 없다.
package access.b;
import access.a.PublicClass;
public class PublicClassOuterMain {
public static void main(String[] args) {
PublicClass publicClass = new PublicClass();
// 다른 패키지에서는 default 클래스에 접근 불가
// DefaultClass1 class1 = new DefaultClass1(); // 오류
// DefaultClass2 class2 = new DefaultClass2(); // 오류
}
}
4. 캡슐화
4.1 캡슐화 개념
캡슐화는 객체 지향 프로그래밍에서 중요한 개념이다. 데이터와 그 데이터를 처리하는 메서드를 하나의 객체로 묶고, 외부에서 불필요한 접근을 제한하여 객체의 무결성을 유지하는 것을 말한다.
캡슐화를 통해 객체의 내부 상태를 보호하고, 외부에서의 무분별한 변경을 방지할 수 있다.
4.2 캡슐화의 두 가지 핵심 원칙
- 데이터를 숨겨라
- 객체의 데이터를 직접 노출하지 않고, 메서드를 통해서만 접근하도록 한다.
- 이를 통해 잘못된 데이터 접근이나 수정으로 인한 오류를 방지할 수 있다.
- 기능을 숨겨라
- 외부에서 불필요하게 알 필요가 없는 기능은 숨기고, 꼭 필요한 기능만 노출한다.
- 이를 통해 객체의 복잡도를 낮추고, 사용자가 객체를 쉽게 사용할 수 있게 한다.
4.3 캡슐화 예시
다음은 잘 캡슐화된 BankAccount 클래스이다.
balance
필드는private
로 선언되어 외부에서 직접 접근할 수 없다.deposit()
,withdraw()
,getBalance()
메서드는public
으로 선언되어 외부에서 사용할 수 있는 기능이다.isAmountValid()
메서드는 외부에서 사용할 필요가 없는 내부적인 검증 로직이므로private
로 선언되어 외부에서 호출할 수 없다.
package access;
public class BankAccount {
private int balance;
public BankAccount() {
balance = 0;
}
// public 메서드: 입금
public void deposit(int amount) {
if (isAmountValid(amount)) {
balance += amount;
} else {
System.out.println("유효하지 않은 금액입니다.");
}
}
// public 메서드: 출금
public void withdraw(int amount) {
if (isAmountValid(amount) && balance - amount >= 0) {
balance -= amount;
} else {
System.out.println("유효하지 않은 금액이거나 잔액이 부족합니다.");
}
}
// public 메서드: 잔고 조회
public int getBalance() {
return balance;
}
// private 메서드: 금액 유효성 검사
private boolean isAmountValid(int amount) {
return amount > 0;
}
}
package access;
public class BankAccountMain {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.deposit(10000);
account.withdraw(3000);
System.out.println("balance = " + account.getBalance());
}
}
댓글남기기