Encapsulation
IB Syllabus: B3.1 – Apply encapsulation by using access modifiers and accessor/mutator methods.
Table of Contents
- Key Concepts
- Worked Examples
- Quick Check
- Spot the Error
- Fill in the Blanks
- Predict the Output
- Practice Exercises
- Connections
Key Concepts
What is Encapsulation?
Encapsulation means putting variables and methods together into one unit (the class) and restricting direct access to the variables by making them private. External code can only interact with the object’s data through its public methods – the class controls how its data is read and modified.
Think of a vending machine: you cannot reach inside and grab items directly. You interact through the defined interface – insert money, press a button, receive the product. The machine’s internal state (stock levels, cash stored) is hidden and protected.
Why Encapsulate?
Without encapsulation, any code anywhere can change any variable to any value:
// WITHOUT encapsulation -- attributes are public
public class BankAccount {
public String owner;
public double balance;
}
BankAccount acc = new BankAccount();
acc.balance = -999999.99; // nothing stops this
acc.owner = ""; // empty string is allowed
With encapsulation, the class controls what values are acceptable:
// WITH encapsulation -- attributes are private, access through methods
public class BankAccount {
private String owner;
private double balance;
public BankAccount(String owner, double balance) {
this.owner = owner;
this.balance = (balance >= 0) ? balance : 0;
}
public double getBalance() { return balance; }
public void deposit(double amount) {
if (amount > 0) {
balance = balance + amount;
}
}
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance = balance - amount;
return true;
}
return false;
}
}
Now balance can never go negative – the class enforces this rule in every method that modifies it. External code cannot bypass this protection.
Encapsulation provides four key advantages:
| Advantage | How |
|---|---|
| Data hiding | Internal representation is hidden; other classes interact only through the public interface |
| Validation | Setters and mutators check that values are valid before accepting them |
| Flexibility | Internal implementation can change without affecting code that uses the class |
| Security | Prevents accidental or malicious modification of an object’s state |
Encapsulation = “putting variables and methods into one unit; variables private; accessed only through public members.” This is the exact phrasing IB mark schemes use. Memorise it.
Accessor Methods (Getters)
An accessor method returns the value of a private instance variable without modifying it. It provides read-only access to the object’s data.
Naming convention: getAttributeName() for most types, isAttributeName() for booleans.
public class Student {
private String name;
private int grade;
private boolean graduated;
// Accessor methods
public String getName() { return name; }
public int getGrade() { return grade; }
public boolean isGraduated() { return graduated; }
}
Getters are simple – they just return the value. They do not change anything.
Mutator Methods (Setters)
A mutator method modifies the value of a private instance variable. It provides controlled write access – typically with validation to ensure only valid values are accepted.
Naming convention: setAttributeName(newValue) or a descriptive verb like deposit(), enrol().
public class Student {
private String name;
private int grade;
private boolean graduated;
// Constructor
public Student(String name, int grade) {
this.name = name;
this.grade = grade;
this.graduated = false;
}
// Mutator with validation
public void setGrade(int grade) {
if (grade >= 0 && grade <= 100) {
this.grade = grade;
}
}
// Mutator with logic
public void graduate() {
if (grade >= 50) {
this.graduated = true;
}
}
// Accessors
public String getName() { return name; }
public int getGrade() { return grade; }
public boolean isGraduated() { return graduated; }
}
Not every attribute needs a setter. Some attributes should be read-only after construction – for example, a student’s name or ID should not change after the object is created. Only provide setters for attributes that legitimately need to be modified.
The Encapsulation Pattern
The standard pattern for encapsulated classes:
- Attributes are
private– neverpublic - Getters are
public– provide read access - Setters are
publicwith validation – provide controlled write access - Constructor initialises all attributes, with validation if needed
public class Temperature {
private double celsius; // 1. private attribute
public Temperature(double celsius) {
if (celsius >= -273.15) {
this.celsius = celsius;
} else {
this.celsius = -273.15;
}
}
public double getCelsius() { // 2. public getter
return celsius;
}
public void setCelsius(double celsius) { // 3. public setter with validation
if (celsius >= -273.15) {
this.celsius = celsius;
}
}
public double toFahrenheit() { // public method using the data
return celsius * 9.0 / 5.0 + 32;
}
}
What Happens Without Encapsulation
Consider a CircularBuffer class where the internal index must always be between 0 and capacity-1. Without encapsulation:
// Dangerous: public fields
buffer.index = 9999; // way beyond capacity -- corrupts memory
buffer.index = -1; // invalid -- causes ArrayIndexOutOfBoundsException
With encapsulation, the class protects itself:
public void advanceIndex() {
index = (index + 1) % capacity; // always stays in range
}
No external code can set index to an invalid value because index is private.
Worked Examples
Example 1: Designing an Encapsulated Class
Scenario: A Product class for an online store. Each product has a name, price, and stock quantity. Prices must be positive. Stock cannot go negative.
public class Product {
private String name;
private double price;
private int stock;
public Product(String name, double price, int stock) {
this.name = name;
this.price = (price > 0) ? price : 0.01;
this.stock = (stock >= 0) ? stock : 0;
}
// Getters
public String getName() { return name; }
public double getPrice() { return price; }
public int getStock() { return stock; }
public boolean isInStock() { return stock > 0; }
// Setters with validation
public void setPrice(double price) {
if (price > 0) {
this.price = price;
}
}
// Business methods
public boolean sell(int quantity) {
if (quantity > 0 && quantity <= stock) {
stock = stock - quantity;
return true;
}
return false;
}
public void restock(int quantity) {
if (quantity > 0) {
stock = stock + quantity;
}
}
}
What encapsulation prevents:
- Setting price to 0 or negative
- Setting stock to negative
- Selling more items than available
- Restocking with a negative amount
- Directly modifying
nameafter creation (no setter provided – read-only)
Example 2: Before and After
Without encapsulation (broken):
Product p = new Product();
p.price = -50.0; // invalid price
p.stock = -10; // impossible stock
// The object is now in an impossible state
With encapsulation (safe):
Product p = new Product("Widget", -50.0, -10);
// Constructor clamps: price becomes 0.01, stock becomes 0
System.out.println(p.getPrice()); // 0.01
System.out.println(p.getStock()); // 0
p.setPrice(-20.0); // rejected -- price stays 0.01
p.sell(5); // rejected -- not enough stock
// The object is always in a valid state
Quick Check
Q1. What is encapsulation?
Q2. Which of the following is NOT an advantage of encapsulation?
Q3. What does an accessor method do?
Q4. A Product has price 19.99. What happens when setPrice(-5) is called?
Q5. Should every private attribute have a public setter?
Spot the Error
This class compiles but violates encapsulation. Click the buggy line, then pick the fix.
Pick the fix:
Fill in the Blanks
Complete the encapsulated class:
public class Student {
String name;
int grade;
public Student(String name, int grade) {
= name;
= grade;
}
// Accessor method
public String () {
return name;
}
// Mutator method with validation
public void setGrade(int grade) {
if (grade >= 0 && grade <= 100) {
= grade;
}
}
} Predict the Output
Using the Product class from Example 1:
Product p = new Product("Widget", 9.99, -5);
p.sell(3);
System.out.println(p.getStock());What is printed?
What is printed?
Product p = new Product("Gadget", 29.99, 10);
p.sell(3);
p.sell(5);
p.sell(5);
System.out.println(p.getStock());What is printed?
Practice Exercises
Core
-
Explain why – A student writes
public int age;in their Person class. Explain two problems this causes and how encapsulation solves each one. -
Write an encapsulated class – Create a
BankAccountclass with privateowner(String) andbalance(double). Include a constructor, getters,deposit(double), andwithdraw(double)with validation (no negative amounts, cannot withdraw more than the balance). Test with several scenarios including invalid operations. -
Read-only attributes – Create a
Studentclass wherenameandstudentIDhave getters but NO setters (read-only after construction), whilegradehas both a getter and a validated setter (0-100).
Extension
- Encapsulation audit – The following class has three encapsulation violations. Find and fix all of them:
public class Ticket { public String event; public double price; private boolean used; public void useTicket() { used = true; } public void setPrice(double p) { price = p; } } - Design challenge – A
Passwordclass stores a hashed password. Design the class so that: the password can be set (with minimum length validation) but NEVER read back (no getter for the actual password). Provide acheckPassword(String attempt)method that returns true/false.
Challenge
- Full encapsulated system – Design a
Libraryclass that manages an array ofBookobjects. The library should encapsulate its book collection (private array) and provide methods to: add a book, remove by ISBN, check out (sets a flag), return (clears the flag), and count available books. No external code should be able to access the array directly or modify books without going through Library methods.
Connections
- Prerequisites: The
thisKeyword –thisenables the getter/setter pattern - Prerequisites: Constructors – constructors initialise private attributes
- Next: Access Modifiers – deeper dive into
private,public,protected - Related: UML Class Diagrams –
-means private,+means public - Forward: Inheritance – encapsulated attributes are inherited but accessed through getters in child classes