Dataslope logoDataslope

Encapsulation and Abstraction

The two ideas that turn a collection of classes into a maintainable program — hide what callers should not see, expose what they need

We have been writing private fields and public methods without fully explaining why. Now we explain. The two ideas at work are encapsulation and abstraction, and they are the reason OOP exists at all.

Encapsulation: each object owns its data

Encapsulation is the rule that only an object's own methods may touch its own data. Outside code asks the object to do things; it does not reach in and edit fields.

Mechanically, Java enforces this with access modifiers:

ModifierVisible to
privateOnly code inside the same class
(package-private, no modifier)Code in the same package
protectedSame package, plus subclasses
publicEveryone

The single most important habit you can build right now: make fields private by default, and only expose what callers need through methods.

Why does this matter?

Imagine balanceCents were public. Anywhere in your codebase, someone could write account.balanceCents = -1_000_000; — and the account class would have no way to enforce the rule "a balance cannot be negative."

With balanceCents private and only deposit and withdraw exposed, the class owns the rules. The class can refuse a withdrawal that would overdraw the account. The class can log every change. The class is the single place where decisions about its own data live. If the balance is wrong, you know exactly where to look.

This is the technical foundation that made it possible to write million-line software systems without going insane.

A worked example

Code Block
Java 8 (Update 492)

Notice how much logic lives inside BankAccount itself. The class refuses negative or zero amounts. It refuses overdraws. Callers cannot bypass these rules because they cannot touch balanceCents directly.

If next month we add a "fees" feature, all the logic lives in BankAccount. The rest of the program stays the same.

Abstraction: callers see the what, not the how

Abstraction is the design idea behind encapsulation: callers care about what an object does for them, not how it does it.

The BankAccount class is, from the outside, just:

  • "I have an owner."
  • "I have a balance."
  • "You can deposit money into me."
  • "You can ask to withdraw — I'll say yes or no."

That is the abstraction. The fact that the balance is stored as balanceCents (rather than as balanceDollars or as a BigDecimal) is the implementation. We can change the implementation without breaking any caller, as long as the abstraction stays the same.

A class is well designed to the extent that its abstraction is small, clean, and stable while its implementation can change freely.

Common mistakes beginners make

1. Public fields "to save time"

public class Person {
    public String name;
    public int    age;
}

This skips encapsulation entirely. It is a struct masquerading as a class. There is no way for Person to enforce rules about its own state. Whenever you see this, ask: who is allowed to set age = -5?

2. Getters and setters for every field

The opposite extreme is almost as bad. If you write getX/setX for every private field, you have exposed the implementation through a thin disguise. Outside code can still set the field to nonsense, just through a method.

// Don't blindly do this:
public void setAge(int age) { this.age = age; }

A good rule: do not add a setter unless your design genuinely requires the field to change after construction. If it does, the setter should enforce the rules. ("setAge must be > 0.")

3. Methods that expose internal collections

public List<Item> getItems() {
    return items;     // returns the internal list — outside code can mutate it!
}

Callers can now do account.getItems().clear() and silently break the class's invariants. Common fixes: return a defensive copy, or return an unmodifiable view, or expose specific operations like add, remove, count instead.

QuestionSelect one

What is the single most important reason to make fields private?

It uses less memory

It makes the class faster

So the class itself is the only place that decides how its own data may change, allowing it to enforce its invariants

Because the Java compiler refuses to compile public fields

QuestionSelect one

Which is the best sign that a class is well-encapsulated?

It has a public field for every state value

It has a getter and setter for every field

Its public methods are named for behaviors that callers actually need, and its fields are private

It uses the keyword final somewhere

A small challenge

Challenge
Java 8 (Update 492)
Encapsulate a Thermostat

Build a class Thermostat that:

  • Has a private int field targetC for the target temperature in Celsius.
  • Has a constructor Thermostat(int initialTarget).
  • Has a method int target() that returns the current target.
  • Has a method void setTarget(int newTarget) that updates the target only if it is between 5 and 35 inclusive; otherwise the method does nothing and the target stays the same.

Main.java is given and must print exactly:

target=20
after legal change: 24
after illegal change: 24

Notice how setTarget silently refuses a bad value. The abstraction is "this thermostat is always sensible." Implementation details — bounds checks, clamping, perhaps logging — stay inside.

Next we look at constructors in more depth: how an object comes to exist, and the entire lifecycle from birth to garbage collection.

On this page