LLD
Machine Coding
Interview Course
Java โ€ข Interview Prep
๐Ÿงฑ OOP Fundamentals/

Encapsulation: Protecting Your Data

Lesson 2 of 8

Encapsulation: Protecting Your Data


What is it? (The Analogy)

Think about your TV and its remote control. When you want to change the channel, you press a button on the remote. You do NOT open the back of the TV, poke at the circuit boards, and manually reroute the electron flow to switch channels. That would be insane (and you would probably electrocute yourself). The TV hides its internal complexity and gives you a simple, safe interface -- the remote control buttons.

That is encapsulation in a nutshell: hiding the internal details of an object and only exposing a controlled interface to the outside world. The object's data (fields) are kept \private\ -- locked away inside. The only way the outside world can read or modify that data is through \public\ methods (getters and setters) that YOU control. This means you can add rules, validation, and protection around your data.

Why does this matter? Because if anyone can reach in and change your data directly, chaos follows. Imagine if anyone walking by could open your bank vault and rearrange the cash. With encapsulation, there is a teller (a method) who checks your ID, validates the amount, and THEN updates the balance. Control. Safety. Sanity.


Why do we need it?

Let's feel the pain first. Here is a BankAccount class with NO encapsulation:

java
1// TERRIBLE DESIGN -- no encapsulation!
2public class BankAccount {
3    public String ownerName;
4    public double balance;     // Anyone can touch this!
5    public String accountNumber;
6}
7
8// Meanwhile, in some other part of the code...
9BankAccount account = new BankAccount();
10account.ownerName = "Alice";
11account.balance = 10000.0;
12
13// DANGER! Nothing stops this:
14account.balance = -99999.99;    // Negative balance?! The bank is now PAYING Alice?!
15account.accountNumber = "";      // Empty account number? Sure, why not!
16account.ownerName = null;        // No name? Total chaos in reports!

There is ZERO protection. Any code anywhere in the program can set the balance to whatever it wants. There is no validation, no rules, no safety net. In a real banking system, this could mean millions of dollars lost.

Now watch what encapsulation does:

java
1// GOOD DESIGN -- encapsulated!
2public class BankAccount {
3    private String ownerName;
4    private double balance;
5    private String accountNumber;
6
7    // The ONLY way to deposit money -- with validation!
8    public void deposit(double amount) {
9        if (amount <= 0) {
10            throw new IllegalArgumentException("Deposit amount must be positive!");
11        }
12        balance += amount;
13    }
14
15    // The ONLY way to withdraw -- with checks!
16    public void withdraw(double amount) {
17        if (amount <= 0) {
18            throw new IllegalArgumentException("Withdrawal amount must be positive!");
19        }
20        if (amount > balance) {
21            throw new IllegalArgumentException("Insufficient funds!");
22        }
23        balance -= amount;
24    }
25
26    // Read-only access to balance
27    public double getBalance() {
28        return balance;
29    }
30}

Now nobody can set the balance to -99999. They MUST go through \deposit()\ or \withdraw()\, where we enforce the rules. The data is safe.


How it works -- Step by Step

  1. **Make fields \private\** -- This locks them inside the class. No outside code can directly read or write them.
  2. **Provide \public\ getter methods** -- These allow controlled READ access. You decide what to expose.
  3. **Provide \public\ setter methods (if needed)** -- These allow controlled WRITE access with validation.
  4. Add validation logic -- Inside setters and other methods, check that incoming data makes sense before accepting it.
  5. Expose behavior, not data -- Instead of \setBalance()\, provide \deposit()\ and \withdraw()\. The interface should reflect ACTIONS, not raw data manipulation.

Java Access Modifiers (from most restrictive to least):

ModifierSame ClassSame PackageSubclassEverywhere
\private\YesNoNoNo
(default)YesYesNoNo
\protected\YesYesYesNo
\public\YesYesYesYes

Let's Build It Together

Let's build a Pizza Order system with proper encapsulation!

java
1public class PizzaOrder {
2
3    // ALL fields are private -- locked away!
4    private String customerName;
5    private String size;           // "Small", "Medium", "Large"
6    private List<String> toppings;
7    private boolean isDelivered;
8    private double price;
9
10    // Constructor -- the ONLY way to create a PizzaOrder
11    public PizzaOrder(String customerName, String size) {
12        // Validation right from the start!
13        if (customerName == null || customerName.trim().isEmpty()) {
14            throw new IllegalArgumentException("Customer name cannot be empty!");
15        }
16        setSize(size);  // Reuse the validated setter!
17
18        this.customerName = customerName;
19        this.toppings = new ArrayList<>();
20        this.isDelivered = false;
21        recalculatePrice();
22    }
23
24    // GETTER -- read-only access to the name
25    public String getCustomerName() {
26        return customerName;
27    }
28
29    // GETTER for size
30    public String getSize() {
31        return size;
32    }
33
34    // SETTER with VALIDATION -- cannot set an invalid size!
35    public void setSize(String size) {
36        if (!size.equals("Small") && !size.equals("Medium") && !size.equals("Large")) {
37            throw new IllegalArgumentException(
38                "Size must be Small, Medium, or Large. Got: " + size
39            );
40        }
41        this.size = size;
42        recalculatePrice();
43    }
44
45    // Adding a topping -- we control the maximum!
46    public void addTopping(String topping) {
47        if (toppings.size() >= 5) {
48            throw new IllegalArgumentException("Maximum 5 toppings allowed!");
49        }
50        if (topping == null || topping.trim().isEmpty()) {
51            throw new IllegalArgumentException("Topping name cannot be empty!");
52        }
53        toppings.add(topping);
54        recalculatePrice();
55    }
56
57    // Return a COPY of the list -- so nobody can modify our internal list!
58    public List<String> getToppings() {
59        return new ArrayList<>(toppings);  // Defensive copy!
60    }
61
62    public double getPrice() {
63        return price;
64    }
65
66    public boolean isDelivered() {
67        return isDelivered;
68    }
69
70    // Behavior method -- not just a setter, but a meaningful action
71    public void markDelivered() {
72        if (isDelivered) {
73            throw new IllegalStateException("Order is already delivered!");
74        }
75        isDelivered = true;
76        System.out.println("Order for " + customerName + " has been delivered!");
77    }
78
79    // PRIVATE helper method -- internal logic nobody else needs to see
80    private void recalculatePrice() {
81        double basePrice = switch (size) {
82            case "Small" -> 8.99;
83            case "Medium" -> 11.99;
84            case "Large" -> 14.99;
85            default -> 0;
86        };
87        price = basePrice + (toppings.size() * 1.50);
88    }
89
90    @Override
91    public String toString() {
92        return size + " pizza for " + customerName
93            + " | Toppings: " + toppings
94            + " | Price: $" + String.format("%.2f", price)
95            + " | Delivered: " + isDelivered;
96    }
97}
java
1public class PizzaDemo {
2    public static void main(String[] args) {
3        PizzaOrder order = new PizzaOrder("Mario", "Large");
4        order.addTopping("Mushrooms");
5        order.addTopping("Pepperoni");
6        order.addTopping("Extra Cheese");
7
8        System.out.println(order);
9        // Large pizza for Mario | Toppings: [Mushrooms, Pepperoni, Extra Cheese]
10        // | Price: $19.49 | Delivered: false
11
12        System.out.println("Price: $" + order.getPrice());  // $19.49
13
14        // Try to break it? NOPE!
15        // order.price = 0;            // COMPILE ERROR -- price is private!
16        // order.setSize("Gigantic");  // RUNTIME ERROR -- invalid size!
17
18        order.markDelivered();
19        // order.markDelivered();  // RUNTIME ERROR -- already delivered!
20    }
21}

The Big Aha! Notice how \recalculatePrice()\ is \private\. The outside world never calls it directly -- it runs automatically whenever something changes. This is encapsulation at its finest: the object manages its own internal consistency. You cannot forget to update the price because the object does it for you.


Visual Mental Model

java
1        THE OUTSIDE WORLD
2        (other classes, main method, etc.)
3              |
4              |  Can only use PUBLIC methods
5              v
6    +-------------------------------+
7    |        PizzaOrder             |
8    |  +-------------------------+  |
9    |  |   PUBLIC INTERFACE      |  |  <-- The "remote control"
10    |  |   (what you CAN touch)  |  |
11    |  |                         |  |
12    |  |  + getCustomerName()    |  |
13    |  |  + getSize() / setSize()|  |
14    |  |  + addTopping()         |  |
15    |  |  + getPrice()           |  |
16    |  |  + markDelivered()      |  |
17    |  +-------------------------+  |
18    |                               |
19    |  +-------------------------+  |
20    |  |   PRIVATE INTERNALS     |  |  <-- The "circuit board"
21    |  |   (what you CANNOT)     |  |
22    |  |                         |  |
23    |  |  - customerName         |  |
24    |  |  - size                 |  |
25    |  |  - toppings             |  |
26    |  |  - price                |  |
27    |  |  - isDelivered          |  |
28    |  |  - recalculatePrice()   |  |
29    |  +-------------------------+  |
30    +-------------------------------+

Real-World Analogy Recap

Encapsulation is like an ATM machine. You can check your balance, deposit money, and withdraw cash through a clean, simple interface (the screen and buttons). But you cannot reach inside the machine and grab the cash directly. The ATM validates every request -- is the PIN correct? Is there enough money? Is the withdrawal amount within the daily limit? The internal mechanics (counting bills, updating records, printing receipts) are hidden away. You interact through a controlled, safe interface.


Common Mistakes & Gotchas

  • Making everything public "for convenience": This is the #1 beginner mistake. Every public field is a potential source of bugs. Start with \private\ and only open up what is truly needed.
  • Exposing mutable objects through getters: If your getter returns a \List\, the caller can modify your internal list! Always return a defensive copy: \return new ArrayList<>(toppings);\
  • Creating setters for everything: Not every field needs a setter. Does it make sense for someone to change an order's customer name after creation? Probably not. Only create setters for fields that genuinely need to be mutable.
  • Forgetting validation in setters: A setter without validation is barely better than a public field. The whole point is to enforce rules!
  • Confusing access modifiers: Remember, "default" (no keyword) is NOT the same as \private\. Default allows access from any class in the same package.

Interview Tip

Interviewers often ask: "What is encapsulation and why is it important?" A great answer: "Encapsulation is the practice of bundling data with the methods that operate on it, and restricting direct access to the data. This protects internal state from invalid modifications, allows the class to maintain its own invariants, and makes the code more maintainable because internal implementation can change without affecting outside code." If you mention defensive copying and invariant protection, you will stand out.


Quick Quiz

  1. Why does the \getToppings()\ method return \new ArrayList<>(toppings)\ instead of just \toppings\? What could go wrong if it returned the original list?
  2. Why is \recalculatePrice()\ marked as \private\? What would happen if it were \public\?
  3. You are designing a \UserProfile\ class with fields: username, email, password, and registrationDate. Which of these should have setters? Which should be read-only? Why?

Summary -- Key Takeaways

  • Encapsulation means making fields \private\ and exposing controlled access through \public\ methods. Hide the "how," expose the "what."
  • Access modifiers (\private\, default, \protected\, \public\) control who can see and use your class members.
  • Validate inside setters and methods to ensure your object can never be in an invalid state. This is the whole point.
  • Return defensive copies of mutable objects (like lists or arrays) from getters so outside code cannot tamper with your internal state.