Java > Object-Oriented Programming (OOP) > Encapsulation > Encapsulation in Practice

Encapsulation Example: Bank Account

This example demonstrates encapsulation using a BankAccount class. It hides the internal state (balance) and exposes controlled access through methods.

BankAccount Class Definition

The BankAccount class encapsulates the balance and accountNumber attributes. These are declared as private, meaning they can only be accessed directly from within the BankAccount class. Access to these attributes is controlled through public methods: getBalance(), getAccountNumber(), deposit(), and withdraw(). The deposit and withdraw methods include validation to ensure data integrity (e.g., preventing negative deposits or withdrawals exceeding the balance). The Main class demonstrates how to create and interact with a BankAccount object.

public class BankAccount {
    private double balance;
    private String accountNumber;

    public BankAccount(String accountNumber, double initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }

    public double getBalance() {
        return balance;
    }

    public String getAccountNumber() {
        return accountNumber;
    }

    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposited: " + amount + ", New balance: " + balance);
        } else {
            System.out.println("Invalid deposit amount.");
        }
    }

    public void withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            System.out.println("Withdrawn: " + amount + ", New balance: " + balance);
        } else {
            System.out.println("Insufficient funds or invalid withdrawal amount.");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount("12345", 1000.0);
        System.out.println("Account Number: " + account.getAccountNumber());
        System.out.println("Initial Balance: " + account.getBalance());

        account.deposit(500.0);
        account.withdraw(200.0);
        account.withdraw(1500.0);
    }
}

Concepts Behind the Snippet

Encapsulation is a fundamental OOP principle that involves bundling data (attributes) and methods that operate on that data into a single unit (a class), and hiding the internal state of the object from the outside world. This promotes data integrity and reduces complexity. Private attributes and public getter/setter methods are key elements.

Real-Life Use Case

Consider a software system for managing employee records. Employee salary information should be encapsulated within the Employee class and only accessible through methods like getSalary() and updateSalary(), potentially with appropriate authorization checks. This prevents unauthorized access and modification of sensitive data.

Best Practices

Always use private access modifiers for instance variables unless there's a specific reason to expose them directly. Provide getter and setter methods (accessors and mutators) to control how the data is accessed and modified. Include validation logic in setter methods to ensure data integrity.

Interview Tip

Be prepared to explain the benefits of encapsulation, such as data hiding, improved code maintainability, and reduced coupling. Also, be ready to discuss scenarios where encapsulation might not be strictly necessary (e.g., in very simple classes with limited functionality).

When to Use Them

Use encapsulation whenever you want to protect the internal state of an object and control how it's accessed and modified. It's particularly important when dealing with sensitive data or when you want to enforce specific business rules.

Memory Footprint

Encapsulation itself doesn't directly affect memory footprint. The memory footprint is determined by the data types of the instance variables. However, the methods defined to access and manipulate the encapsulated data will contribute to the overall size of the class in memory, although this contribution is generally small.

Alternatives

While encapsulation is a core OOP principle, alternatives might include using data structures with limited access control (e.g., immutable objects) or using functional programming paradigms where data transformation is emphasized over mutable state.

Pros

  • Data Hiding: Protects internal state from unauthorized access.
  • Improved Maintainability: Changes to the internal implementation of a class don't affect code that uses the class, as long as the public interface remains the same.
  • Reduced Coupling: Reduces dependencies between classes, making the code more modular and easier to test.
  • Data Integrity: Allows for validation logic to be included in setter methods.

Cons

  • Increased Code Complexity: Requires writing getter and setter methods, which can add to the overall code length.
  • Potential Performance Overhead: Accessing data through methods might be slightly slower than accessing it directly, although this is usually negligible.

FAQ

  • What is the difference between public, private, and protected access modifiers?

    • Public: Accessible from anywhere.
    • Private: Accessible only from within the same class.
    • Protected: Accessible from within the same class, subclasses, and other classes in the same package.
  • Why should I use getters and setters instead of directly accessing instance variables?

    Getters and setters allow you to control how the data is accessed and modified. You can add validation logic, implement lazy loading, or perform other actions when the data is accessed or modified. This provides greater flexibility and control over the object's state.