본문 바로가기

Java

Java 캡슐화 (Encapsulation)

 

캡슐화란?

캡슐화( Encapsulation)는 데이터(속성)와 메서드(행위)를 하나의 단위로 묶어 외부에서의 접근을

제한하는 객체지향 프로그래밍의 주요 개념 중 하나이다.

 

 

  • 데이터 보호와 접근 제어를 통해 데이터의 무결성을 보장한다.
  • 외부 객체는 캡슐화된 객체 내부의 데이터를 직접 수정하거나 접근할 수 없으며,
    객체가 제공하는 메서드(인터페이스)를 통해서만 상호작용 가능하다.

 

캡슐화의 특징

 

  • 데이터 은닉:
    • 외부에서 객체의 **필드(변수)**에 직접 접근하지 못하도록 제한한다.
    • 일반적으로 필드는 private으로 선언하고, 필요한 경우 메서드를 통해 접근한다.

  • 메서드를 통한 접근:
    • 데이터를 조작하거나 읽는 데 필요한 메서드(Getter, Setter)를 제공한다.
    • 메서드 내부에서 입력값 검증 및 데이터 처리 로직을 추가할 수 있다.
  • 독립성:
    • 객체 내부 구현을 숨기고, 외부에 필요한 기능만 노출하여 코드의 독립성을 높인다.
    • 내부 구현 변경 시 외부 코드에 영향을 주지 않는다.

 

 

캡슐화의 장점

 

 

데이터 무결성 보장:

  • 잘못된 데이터를 입력하거나 수정하는 것을 방지한다.
  • Setter 메서드에서 유효성 검사를 수행할 수 있다.

 

코드 변경 용이성:

  • 클래스 내부 구현이 변경되어도 외부에 제공되는 인터페이스(Getter, Setter)가 동일하면
    외부 코드에 영향을 미치지 않는다.

데이터 은닉:

  • 외부 객체는 캡슐화된 데이터에 직접 접근할 수 없으므로 데이터를 보호할 수 있다.

 

객체 지향 원칙 준수:

  • 정보 은닉(Information Hiding)모듈화(Modularity)를 통해 객체지향 설계 원칙을 구현한다.

 

재사용성 증가:

  • 캡슐화된 클래스는 독립적이고 안정적이므로 재사용 가능성이 높아진다.

 

 

 

 

캡술화 구현

 

  • 필드(변수) 접근 제한:
    • private 접근 제어자를 사용해 데이터를 숨긴다.

  • Getter와 Setter 메서드 제공:
    • public 메서드로 외부에서 데이터를 읽거나 수정할 수 있도록 한다.
    • 메서드 내부에서 데이터 유효성을 검사할 수 있다.

 

  • 단일 책임 원칙 (SRP):
    • 캡슐화는 클래스의 내부 구현과 외부 인터페이스를 분리해 한 가지 책임만 가지도록 설계한다.

  • 개방-폐쇄 원칙 (OCP):
    • 클래스 내부를 수정하지 않고도 새로운 기능을 추가할 수 있다.

 

 

 

캡슐화 전: 데이터 노출

public class Person {
    public String name; // 외부에서 직접 접근 가능
    public int age;
}

public class Main {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "Alice"; // 데이터 직접 수정
        person.age = -5;       // 잘못된 데이터 입력
        System.out.println(person.name + ", " + person.age);
    }
}

 

 

 

 

 

캡슐화 적용 후: 데이터 보호

public class Person {
    private String name; // 데이터 은닉
    private int age;

    // Getter
    public String getName() {
        return name;
    }

    // Setter
    public void setName(String name) {
        this.name = name;
    }

    // Getter
    public int getAge() {
        return age;
    }

    // Setter (유효성 검사 추가)
    public void setAge(int age) {
        if (age > 0) { // 나이는 0보다 커야 함
            this.age = age;
        } else {
            System.out.println("Invalid age!");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Person person = new Person();

        // 데이터 설정
        person.setName("Alice");
        person.setAge(-5); // 잘못된 데이터, 유효성 검사로 방지

        // 데이터 출력
        System.out.println(person.getName() + ", " + person.getAge());
    }
}

 

 

 

Getter와 Setter의 추가 활용

 

입력값 검증

public void setAge(int age) {
    if (age > 0 && age < 150) { // 유효한 나이 범위 확인
        this.age = age;
    } else {
        throw new IllegalArgumentException("Age must be between 1 and 150");
    }
}

 

 

읽기 전용 필드

필드 값을 읽을 수만 있고 변경할 수 없도록 Getter만 제공.

public String getName() {
    return name;
}

 

 

쓰기 전용 필드

필드 값을 외부에서 설정만 가능하고 읽을 수 없도록 Setter만 제공.

public void setPassword(String password) {
    this.password = password;
}

 

 

실제 예제 (은행 계좌)

public class BankAccount {
    private String accountNumber; // 계좌번호
    private double balance;       // 잔액

    // 생성자
    public BankAccount(String accountNumber) {
        this.accountNumber = accountNumber;
        this.balance = 0; // 초기 잔액은 0
    }

    // Getter
    public String getAccountNumber() {
        return accountNumber;
    }

    public double getBalance() {
        return balance;
    }

    // 입금 메서드 (입금 금액 유효성 검사)
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        } else {
            System.out.println("Invalid deposit amount!");
        }
    }

    // 출금 메서드 (잔액 부족 확인)
    public void withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
        } else {
            System.out.println("Insufficient balance or invalid amount!");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount("123-456-789");

        account.deposit(1000); // 1000 입금
        account.withdraw(500); // 500 출금
        System.out.println("Balance: " + account.getBalance()); // 출력: 500
    }
}