June 17, 2026 in fundamentals4 minutes
A intermediate-level guide to Encapsulation: before-and-after java code and diagrams for a CS student.
Imagine you are building a banking application. You have an object that holds a user’s balance, and you want to make sure no one ever accidentally sets that balance to a negative number. However, if the variable holding that balance is visible to every other part of your program, any developer (or even another piece of code) could bypass your business logic and change the value directly.
When an object’s internal state can be modified from the outside without any oversight, you lose control over the integrity of your data. This leads to “corrupted state,” where an object exists in a way that should be impossible according to your rules—like an account with negative money when your bank policy forbids it.
class BankAccount {
public double balance;
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
}In the diagram below, notice how the balance field is marked as public. This means any external entity can reach directly into the BankAccount and overwrite its value at will.
classDiagram
class BankAccount {
+double balance
}
Encapsulation is the technique of bundling data (variables) and the methods that operate on that data into a single unit, known as a class, while restricting direct access to some of the object’s components.
Think of a bank teller window. You cannot walk behind the counter and grab money from the vault yourself; you must interact with the teller through the service window. The teller acts as an intermediary who verifies your identity and ensures your transaction is valid before anything changes in the vault. In programming, these “tellers” are public methods, and the “vault” is your private data.
To achieve this, we use access modifiers:
private: This keyword hides a field so it can only be accessed or changed by code within the same class.public: This keyword makes a method available to any other class that has a reference to your object.To implement encapsulation properly, we hide our variables using private and provide controlled access through public methods. These methods act as gatekeepers. When someone wants to change the state (e.g., adding money), they call a method like deposit(). Inside that method, we write logic to validate the request before actually updating the private variable.
class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
if (initialBalance < 0) {
throw new IllegalArgumentException("Initial balance cannot be negative");
}
this.balance = initialBalance;
}
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Deposit amount must be positive");
}
balance += amount;
}
}In this improved version, the balance is protected. The only way to modify it is through the deposit method or the constructor, both of which perform validation checks. If an invalid amount is provided, the object refuses to change its state and instead throws an error.
sequenceDiagram
participant User
participant BA as BankAccount
User ->> BA: deposit(amount)
alt amount > 0
BA ->> BA: balance += amount
BA -->> User: void
else amount <= 0
BA -->> User: throw IllegalArgumentException
end
A common mistake is thinking that encapsulation simply means adding “getters” (methods to read a value) and “setters” (methods to update a value). If your setter looks like this: public void setBalance(double balance) { this.balance = balance; }, you have not actually implemented encapsulation effectively because you haven’t added any validation rules. You have simply made a private variable public through a different name.
True encapsulation is about protecting the object’s integrity. If your method accepts data that would break the business rules of your application, the encapsulation has failed its primary purpose: managing complexity and preventing invalid states.
Encapsulation is an essential tool for managing complexity in large systems. By restricting how data is modified, you ensure that if a bug occurs regarding a variable’s value, you only have one place to look: the methods inside that specific class. This prevents “side effects,” where changing code in one part of a program unexpectedly breaks an unrelated part because they were both sharing direct access to the same variable.
However, encapsulation is not a replacement for security; it is a tool for code correctness and maintainability. Over-engineering every single variable with complex validation when the data is internal and trivial can sometimes lead to unnecessary boilerplate code. Use it primarily when your object has rules that must never be violated.
private fields and public methods.Direct access allows the object to enter an invalid state.
BankAccount account = new BankAccount(100.0);
account.balance = -500.0; // Corrupts state: balance is now -400.0Encapsulation ensures the object remains in a valid state.
BankAccount account = new BankAccount(100.0);
account.deposit(-50.0); // Throws IllegalArgumentException