Last Updated: February 12, 2026
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.
Here's why that matters in practice:
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: access modifiers that control visibility, and getters/setters that provide controlled access to private data.
Access modifiers are keywords that control which parts of your code can see and interact with a class's fields and methods. The three most common are:
private: Accessible only within the same class. This is the primary tool for hiding data.protected: Accessible within the same class and its subclasses. Useful when child classes need access to parent data.public: Accessible from anywhere. This is what you use for the controlled interface.The general rule is simple: make everything private by default, then selectively expose what needs to be public.
Here's a minimal example showing the difference:
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.Here's an example where a setter prevents invalid data from ever entering the object:
Now let's see a complete encapsulated class with proper validation, controlled access, and business rules. The BankAccount class keeps balance private and only allows modifications through deposit() and withdraw(), each of which enforces its own rules.
Notice what's happening here:
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. The raw card number must never be stored or visible anywhere in the system. If a developer accidentally logs the payment object or inspects it in a debugger, they should only see a masked version.
The masking logic, the amount, and the processing flow are all internal details that the caller doesn't need to worry about.
PaymentProcessor and call processPayment(). No need to call maskCardNumber() first, no need to worry about storing the original number safely.This is encapsulation applied to security: sensitive data enters the class, gets transformed into a safe representation, and the original is never exposed.