ReviseAlgo Logo

OOP Fundamentals

Encapsulation

Data hiding and state protection

Last Updated: June 25, 2026 18 min read

Encapsulation is the OOP principle that binds data fields and the methods operating on that data into a single class unit, while restricting direct access to the object's components. Encapsulation is achieved via Data Hiding to prevent unintended state corruption.

1. Learning Objectives

  • Explain data hiding and its role in protecting object state invariants.
  • Differentiate between the four access modifiers: private, default, protected, public.
  • Understand the concept of defensive copying when exposing mutable attributes.

2. Problem Statement

Without encapsulation, an object's internal variables are exposed to the entire program. Any class can run bankAccount.balance = -99999.00; directly, corrupting the bank's ledger without validation checks, audit logging, or security credentials verification.

3. Real-world Analogy

Think of a Vending Machine. The soda cans and snacks are locked inside the glass case (data hiding). You cannot reach in and grab a bottle directly.

Instead, you must interact through the public panel (public methods): insert money, select the product number. The machine validates your input (price checks), updates its internal state (reduces inventory), and dispenses the item safely.

4. Theory

Encapsulation is established through:

  • Access Modifiers: Set boundaries of field availability.
  • Getters & Setters: Standardized methods to retrieve and modify state under controlled conditions.
  • Defensive Copying: When exposing lists or reference objects, return copies rather than direct pointers so clients cannot modify internal collections.

5. Visual Diagrams (UML & Memory structures)

Class Diagram

BankAccount
- accountNumber: String
- balance: double
+ deposit(amount: double)
+ withdraw(amount: double)
+ getBalance(): double

Object Diagram

account: BankAccount
accountNumber = "ACC-101"
balance = 1200.00

Memory Diagram

Stack (Locked Reference)

acc = @0x2b3c
points to

Heap space (Shielded Variables)

@0x2b3c:
[Private] balance: 1200.00 (Exposed only via methods)

Object Lifecycle

An encapsulated object goes through constructor checks, maintaining its validation rules during all runtime mutations, until it is dereferenced and garbage collected.

6. Syntax Explanation

  • Java: Uses keywords private (accessible inside class only), default (package access), protected (package and subclass access), and public (accessible everywhere).
  • Python: Lacks keyword barriers. Standardizes private attributes with a double underscore prefix __balance (which triggers name mangling).
  • C++: Organizes members using access specification blocks: private:, protected:, public:.

7. Step-by-Step Implementation

Let's design a secure Bank Account Transaction tracker:

  • Step 1: Declare the BankAccount class with private properties accountNumber and balance.
  • Step 2: Declare a public constructor. Block invalid initial values.
  • Step 3: Write deposit(amount) and withdraw(amount) methods containing logical state assertions.
  • Step 4: Expose a public read-only getter method for the balance variable. Do not write a generic setter for the balance variable.

8. Complete Code (Mini Project)

9. Code Walkthrough

In BankAccount, the balance property has no setter. A client can only mutate the balance value by calling deposit() or withdraw(). These mutator methods validate parameters and log execution history, protecting the object's internal state.

10. Execution Flow

  • Trigger the constructor, initializing variables and adding creation logs.
  • Invoke deposit(amount). The method confirms amount > 0 before updating the balance.
  • Invoke withdraw(amount). The method confirms amount <= balance before updating the balance.

11. Internal Working

When clients request getTransactionLogs(), the class wraps the internal ArrayList reference inside an unmodifiable collection view. This ensures that any attempt by client classes to append elements to the logs list directly will trigger a runtime exception.

12. Complexity Analysis

  • Time Complexity: $O(1)$ to query balances or execute basic deposits/withdrawals.
  • Space Complexity: $O(T)$ where $T$ represents the number of transactions recorded in the logs array.

13. Best Practices

  • Defensive Copying: Never return direct mutable pointers to internal lists or arrays.
  • Validate parameters early: Throw exceptions at the start of method calls if arguments are invalid.

14. Common Mistakes

  • Writing boilerplate getters and setters automatically for every field in the class (this defeats encapsulation).
  • Setting class instance variables to public to save writing accessor methods.

15. Interview Questions

Q: What is the risk of not using encapsulation?
Answer: Code becomes fragile. External classes can directly corrupt internal variable states, resulting in unpredictable runtime behaviors, data inconsistencies, and high technical debt.

16. Practice Exercises

  • Easy: Add a helper method to return total transaction count.
  • Medium: Add an interestRate field and create a method to calculate and apply interest. Ensure rate values remain positive and within bounds (e.g. 0% to 20%).
  • Hard: Implement a daily transaction withdrawal limit check. Withdrawals exceeding this limit must be blocked, even if the account has a sufficient balance.

17. Challenge Problem

Design an encapsulated SessionTokenManager where tokens are validated and purged automatically if client queries occur past expiration limits.

18. Summary

  • Encapsulation binds data and method operations, hiding internal states.
  • Data hiding prevents direct, external variable manipulation.
  • Defensive copying protects internal collections from external mutations.

19. Cheat Sheet

Modifier Class Level Package Level Subclass Level Global Level
private Yes No No No
default Yes Yes No No
protected Yes Yes Yes No
public Yes Yes Yes Yes

20. Quiz

1. What is data hiding in encapsulation?

A) Encrypting databases at rest
B) Restricting direct external access to an object's internal variables (Correct)
C) Compiling the code without variable names

2. Which access modifier allows access within the class and package, and by subclasses?

A) private
B) protected (Correct)
C) public

3. What is defensive copying?

A) Copying files onto backup servers
B) Returning a copy of a mutable internal attribute to prevent clients from mutating original states (Correct)
C) Duplicating constructors inside subclasses

4. Why shouldn't you automatically create setters for every class variable?

A) Setters reduce memory allocation speed
B) It exposes internal variables to arbitrary mutations, violating encapsulation (Correct)
C) Setters are deprecated in Java 21

5. Which modifier permits visibility only inside the active declaring class?

A) protected
B) private (Correct)
C) default

6. What type of collection is returned in Java to prevent client modifications?

A) Unmodifiable / Read-only Wrapper (Correct)
B) Static global array
C) Local Stack vector

7. What is the scope of protected access in Java?

A) Only the declaring class
B) Package-level and subclasses (Correct)
C) Accessible from any package globally

8. How does name mangling work in Python private fields?

A) It deletes variables on compile checks
B) It renames attributes with a class-name prefix to reduce namespace collisions (Correct)
C) It converts variables to strings

9. What exception is thrown when modifying a collection returned via 'Collections.unmodifiableList()'?

A) NullPointerException
B) UnsupportedOperationException (Correct)
C) IllegalArgumentException

10. What is the default access modifier scope in Java?

A) Package-Private (Correct)
B) Private
C) Public

21. Next Lesson Preview

In the next lesson, we will explore Abstraction to learn how to hide complex backend implementations and expose clean, simplified interfaces to the client!