Encapsulation is one of the four foundational principles of object-oriented design. It is the practice of grouping data (variables) and behavior (methods) that operate on that data into a single unit (typically a class) and restricting direct access to the internal details of that class.
In simple terms:
Encapsulation = Data hiding + Controlled access
Think of a bank account as a vault inside a bank. You don't walk into the vault and change the numbers yourself.
Instead, you interact with it through a well-defined interface, the ATM.
The ATM provides limited but specific operations:
deposit()withdraw()checkBalance()You can’t directly access or modify the bank’s internal data.
The bank might change how it stores information, applies interest, or validates transactions but none of that affects how you use the ATM.
That’s encapsulation in action — hiding internal complexity and exposing only what’s necessary.
In a well-encapsulated design, external code doesn’t need to know how something is done, it only needs to know what can be done.
Encapsulation isn’t just about data protection, it’s about designing systems that are robust, secure, and easy to maintain.
Sensitive data (like a bank balance or password) should not be exposed directly. Encapsulation keeps this data private and accessible only through controlled methods.
It ensures that data can only be modified in controlled, predictable ways.
For example, you can prevent invalid deposits or withdrawals by validating input inside methods.
Because internal details are hidden, you can change the implementation (e.g., how data is stored or validated) without affecting the code that depends on it.
By preventing external tampering, encapsulation reduces the risk of inconsistent or invalid system states.
Encapsulation is primarily implemented using two language features:
Keywords like private, protected, and public are used to control the visibility of attributes and methods.
private: Accessible only within the same class. This is the primary tool for data hiding.public: Accessible from anywhere. This creates the controlled public interface.These are public methods that provide controlled, indirect access to private attributes.
getBalance()): Provides read-only access to an attribute.setAmount()): Allows modifying an attribute, often with validation logic built-in.Let’s look at two practical examples that demonstrate how encapsulation helps you protect internal state, enforce business rules, and expose only what’s necessary.
In a banking system, you want users to deposit and withdraw funds, but you must prevent direct manipulation of the account balance. Here’s how encapsulation helps:
balance is marked private, so no external class can access or modify it directly.deposit() and withdraw() are public entry points that validate user input before updating the state.getBalance() allows read-only access, without revealing the underlying variable or letting external code change it.This ensures the account remains in a valid state at all times and business rules are enforced through controlled interfaces.
Let’s take a more realistic example. You’re building a PaymentProcessor class that handles credit card transactions.
You don’t want raw card numbers to be stored or visible anywhere in the system.
PaymentProcessor class accepts a card number and amount, but internally masks the card number to protect user privacy. Again, encapsulation allows us to hide implementation details while offering a clean interface.
processPayment().This design secures sensitive data and centralizes masking logic in one place, making the system safer and easier to maintain.
Encapsulation focuses on how to protect and control access to data within a class. But what if we extend that idea. Not just hiding data, but also hiding complexity itself?
That’s where Abstraction comes in.
In the next chapter, we’ll explore how abstraction helps you design systems that expose only the essential details while concealing unnecessary complexity, making your code cleaner, more intuitive, and easier to work with.