OOP Basics
IB Syllabus: B3.1 – Fundamentals of OOP for a single class: class design, constructors, encapsulation, static members, UML class diagrams, object references.
Table of Contents
- Key Concepts
- Worked Examples
- Quick Check
- Trace Exercise
- Spot the Error
- Fill in the Blanks
- Predict the Output
- Practice Exercises
- Connections
Key Concepts
What is Object-Oriented Programming?
In procedural programming, you write a sequence of instructions that operate on data stored in variables. As programs grow larger, this becomes hard to manage – variables multiply, functions need to know about data they shouldn’t touch, and changes in one part break others.
Object-oriented programming (OOP) solves this by organising code around objects – self-contained units that bundle related data (attributes) and the operations that act on that data (methods) into a single entity. Each object is responsible for managing its own state.
The four core principles of OOP are:
| Principle | What it means |
|---|---|
| Encapsulation | Bundling data and methods into one unit; restricting direct access to the data |
| Inheritance | Creating new classes that reuse and extend existing classes |
| Polymorphism | Different objects responding to the same method call in different ways |
| Abstraction | Hiding complex implementation details and exposing only what is necessary |
This page covers encapsulation in depth. Inheritance and polymorphism are covered in OOP with Multiple Classes (HL).
Classes and Objects
A class is a blueprint that defines what attributes (data) and methods (behaviour) its objects will have. Think of it as a template or a cookie cutter.
An object is a specific instance created from that class. If the class is the blueprint for a house, each actual house built from that blueprint is an object.
Instantiation is the process of creating a new object from a class by calling its constructor. When you write new BankAccount("Alice", 1000.0), you are instantiating a BankAccount object – the constructor is called, memory is allocated, and the object’s attributes are initialised with the provided values.
// Class definition (the blueprint)
public class BankAccount {
private String owner;
private double balance;
// Constructor -- called when a new object is created
public BankAccount(String owner, double initialBalance) {
this.owner = owner;
this.balance = initialBalance;
}
// Method -- behaviour the object can perform
public void deposit(double amount) {
if (amount > 0) {
balance = balance + amount;
}
}
public double getBalance() {
return balance;
}
public String getOwner() {
return owner;
}
}
// Creating objects (instantiation)
public class Main {
public static void main(String[] args) {
BankAccount acc1 = new BankAccount("Alice", 1000.0);
BankAccount acc2 = new BankAccount("Bob", 250.0);
acc1.deposit(500.0);
System.out.println(acc1.getOwner() + ": " + acc1.getBalance());
System.out.println(acc2.getOwner() + ": " + acc2.getBalance());
}
}
Output:
Alice: 1500.0
Bob: 250.0
Each object has its own copy of the attributes. Depositing into acc1 does not affect acc2 – they are independent instances of the same class.
Constructors
A constructor is a special method that is called automatically when a new object is created. Its job is to initialise the object’s attributes – setting them to their starting values.
Constructors have the same name as the class and no return type (not even void).
public class Student {
private String name;
private int grade;
// Default constructor (no parameters)
public Student() {
this.name = "Unknown";
this.grade = 0;
}
// Parameterised constructor (accepts values)
public Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
}
Student s1 = new Student(); // uses default constructor
Student s2 = new Student("Maya", 11); // uses parameterised constructor
If you define a parameterised constructor but NOT a default constructor, Java will no longer provide one automatically. Code that calls
new Student()will fail to compile unless you explicitly define the default constructor.
Encapsulation
Encapsulation means putting variables and methods together into one unit (the class) and restricting direct access to the variables by making them private. The data can only be accessed or modified through public methods – typically getters (accessor methods) and setters (mutator methods).
This protects the object’s internal state from being changed in unexpected ways. The class controls how its data is accessed and what values are allowed.
public class Temperature {
private double celsius; // private -- cannot be accessed directly from outside
public Temperature(double celsius) {
this.celsius = celsius;
}
// Accessor method (getter) -- returns the value of a private instance variable
public double getCelsius() {
return celsius;
}
// Mutator method (setter) -- modifies the value with validation
public void setCelsius(double celsius) {
if (celsius >= -273.15) { // absolute zero check
this.celsius = celsius;
}
}
public double toFahrenheit() {
return celsius * 9.0 / 5.0 + 32;
}
}
Why encapsulation matters:
- Data hiding – the internal representation is hidden from other classes; they interact only through the public interface
- Validation – setters can check that values are valid before accepting them (e.g., temperature cannot go below absolute zero)
- Flexibility – the internal implementation can change without affecting code that uses the class, as long as the public methods stay the same
- Security – prevents accidental or malicious modification of an object’s state
Access modifiers control visibility:
| Modifier | Access level |
|---|---|
private | Only accessible within the class itself |
public | Accessible from any other class |
protected | Accessible within the class and its subclasses |
The standard pattern: attributes are
private, methods arepublic. An accessor method (getter) returns the value of a private instance variable. A mutator method (setter) modifies it, typically with validation.
The this Keyword
The this keyword refers to the current object – the specific instance whose method is being called. It is most commonly used when a method parameter has the same name as an instance variable:
public class Circle {
private double radius;
public Circle(double radius) {
this.radius = radius; // this.radius = instance variable, radius = parameter
}
}
Without this, the compiler would think both radius refer to the parameter, and the instance variable would never be set.
Static vs Instance Members
Instance variables and methods belong to each individual object. Every object has its own copy.
Static variables and methods belong to the class itself, not to any particular object. There is only one copy shared by all instances.
public class Student {
private String name; // instance variable -- each student has their own
private static int count = 0; // static variable -- shared by ALL students
public Student(String name) {
this.name = name;
count++; // increments the shared counter
}
public String getName() { // instance method -- operates on this object
return name;
}
public static int getCount() { // static method -- operates on the class
return count;
}
}
Student s1 = new Student("Alice");
Student s2 = new Student("Bob");
Student s3 = new Student("Charlie");
System.out.println(Student.getCount()); // 3 -- called on the class, not an object
Static methods cannot access instance variables or call instance methods directly – they do not have a
thisreference because they do not belong to any specific object. Use static for utility methods and counters; use instance for everything that depends on a specific object’s data.
Object References
When you create an object with new, Java does not store the object in the variable. Instead, the variable holds a reference – the memory address where the object is stored. This has important consequences:
BankAccount a1 = new BankAccount("Alice", 500.0);
BankAccount a2 = a1; // a2 now points to the SAME object as a1
a2.deposit(100.0);
System.out.println(a1.getBalance()); // prints 600.0 -- both variables reference the same object
Assigning one object variable to another does not create a copy of the object. Both variables refer to the same object in memory. Changing the object through one reference is visible through the other.
A variable that does not reference any object holds the value null. Calling a method on a null reference causes a NullPointerException:
Student s3 = null;
System.out.println(s3.getName()); // NullPointerException!
When working with arrays of objects, always check for
nullbefore calling methods on array elements. Not every slot in an object array may have been filled.
Aggregation (Has-A Relationship)
Aggregation describes a relationship where one object contains or stores another object as an attribute. This is a “has-a” relationship – a Lawyer has Case objects, a School has Student objects.
public class Address {
private String street;
private String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
public String getCity() {
return city;
}
}
public class Student {
private String name;
private Address homeAddress; // aggregation -- Student HAS-A Address
public Student(String name, Address homeAddress) {
this.name = name;
this.homeAddress = homeAddress;
}
public String getCity() {
return homeAddress.getCity(); // delegating to the contained object
}
}
Aggregation differs from inheritance:
| Aggregation (has-a) | Inheritance (is-a) | |
|---|---|---|
| Relationship | One object contains another | One class extends another |
| Example | A Car has an Engine | A Hare is a Racer |
| Coupling | Loose – contained object is independent | Tight – child depends on parent structure |
| Polymorphism | Not supported | Supported |
UML Class Diagrams
A UML (Unified Modeling Language) class diagram is a visual representation of a class showing its name, attributes, and methods in a three-section box. UML is the standard way to design and communicate class structures before writing code.
+---------------------------+
| BankAccount | ← Class name
+---------------------------+
| - owner: String | ← Attributes (- = private)
| - balance: double |
+---------------------------+
| + BankAccount(String, | ← Methods (+ = public)
| double) |
| + deposit(double): void |
| + getBalance(): double |
| + getOwner(): String |
+---------------------------+
Access modifier symbols:
-meansprivate+meanspublic#meansprotected
Attribute format: visibility name: type Method format: visibility name(parameters): returnType
Relationships in UML
Inheritance (is-a) is shown with a solid line and a closed triangle pointing to the parent class:
+----------+
| Racer | ← parent/superclass
+----------+
△
|
+----------+
| Hare | ← child/subclass
+----------+
Aggregation (has-a) is shown with a line ending in a diamond on the “whole” side:
+----------+ +----------+
| Student |◇------| Address |
+----------+ +----------+
(whole) (part)
UML diagrams appear on every IB Paper 2 exam. You must be able to draw a complete three-section class diagram with correct access modifiers, data types, and relationship arrows.
Common OOP Patterns in Exams
These patterns appear repeatedly. Being familiar with them saves time under exam pressure.
Pattern 1: Filter and Display
Loop through an array of objects, check a condition, and display matching items.
// Display all students with grade above 90
for (int i = 0; i < students.length; i++) {
if (students[i] != null && students[i].getGrade() > 90) {
System.out.println(students[i].getName());
}
}
Note the null check – object arrays may have unfilled slots.
Pattern 2: Accumulate and Return
Loop through objects, filter by a condition, accumulate a total, and return it.
public double totalBalance() {
double total = 0;
for (int i = 0; i < accounts.length; i++) {
if (accounts[i] != null) {
total = total + accounts[i].getBalance();
}
}
return total;
}
Pattern 3: Selection Sort on Object Array
Sort an array of objects by one of their attributes, in descending order.
// Sort products by price, highest first
for (int i = 0; i < products.length - 1; i++) {
int maxIndex = i;
for (int j = i + 1; j < products.length; j++) {
if (products[j].getPrice() > products[maxIndex].getPrice()) {
maxIndex = j;
}
}
// Swap the objects
Product temp = products[i];
products[i] = products[maxIndex];
products[maxIndex] = temp;
}
Selection sort on object arrays is a frequently tested pattern. The key difference from sorting primitives: you compare using getter methods (e.g.,
getPrice()) and swap the entire object references, not individual fields.
Worked Examples
Example 1: Design a Class from a Scenario
Scenario: A library system tracks books. Each book has a title, author, ISBN (String), and a flag indicating whether it is currently on loan. The system needs to check out and return books.
Step 1: Identify attributes and methods
| Attributes | Type | Methods | Return type |
|---|---|---|---|
| title | String | getTitle() | String |
| author | String | getAuthor() | String |
| isbn | String | isOnLoan() | boolean |
| onLoan | boolean | checkOut() | void |
| returnBook() | void |
Step 2: UML diagram
+---------------------------+
| Book |
+---------------------------+
| - title: String |
| - author: String |
| - isbn: String |
| - onLoan: boolean |
+---------------------------+
| + Book(String, String, |
| String) |
| + getTitle(): String |
| + getAuthor(): String |
| + isOnLoan(): boolean |
| + checkOut(): void |
| + returnBook(): void |
+---------------------------+
Step 3: Java code
public class Book {
private String title;
private String author;
private String isbn;
private boolean onLoan;
public Book(String title, String author, String isbn) {
this.title = title;
this.author = author;
this.isbn = isbn;
this.onLoan = false;
}
public String getTitle() { return title; }
public String getAuthor() { return author; }
public boolean isOnLoan() { return onLoan; }
public void checkOut() {
if (!onLoan) {
onLoan = true;
}
}
public void returnBook() {
onLoan = false;
}
}
Example 2: Method Construction with Object Array
Scenario: A Library class has an array of Book objects. Write a method countAvailableBooks() that returns how many books are not currently on loan.
public class Library {
private Book[] catalogue;
public Library(int capacity) {
catalogue = new Book[capacity];
}
public int countAvailableBooks() {
int count = 0;
for (int i = 0; i < catalogue.length; i++) {
if (catalogue[i] != null && !catalogue[i].isOnLoan()) {
count++;
}
}
return count;
}
}
Marking breakdown (typical 5-mark method):
- Loop through the array [1]
- Null check on each element [1]
- Correct condition (
!isOnLoan()) [1] - Counter incremented correctly [1]
- Return statement [1]
Quick Check
Q1. What is encapsulation?
Q2. What is the purpose of a constructor?
Q3. What does an accessor method do?
Q4. Why can a static method not access instance variables?
Q5. In a UML class diagram, what do - and + mean?
Q6. Which relationship does aggregation represent?
Trace Exercise
Trace through this code and determine the output. Track each object’s attribute values after each operation.
Trace: Object State Changes
Given the BankAccount class from the Key Concepts section:
BankAccount a = new BankAccount("Alice", 500.0);
BankAccount b = new BankAccount("Bob", 300.0);
a.deposit(200.0);
b.deposit(100.0);
a.deposit(50.0);| After line | a.owner | a.balance | b.owner | b.balance |
|---|---|---|---|---|
| Line 1-2 (creation) | Alice | 500.0 | Bob | 300.0 |
| Line 3: a.deposit(200) | Alice | Bob | ||
| Line 4: b.deposit(100) | Alice | Bob | ||
| Line 5: a.deposit(50) | Alice | Bob |
Spot the Error
This class compiles but produces unexpected null values. Click the buggy line, then pick the fix.
Pick the correct fix:
Fill in the Blanks
Complete the OOP terminology summary:
OOP Principles
==============
= bundling data and methods; making attributes private.
= creating new classes that extend existing ones (is-a).
= different objects responding to the same method differently.
= hiding complex details; exposing only what is necessary.
Class Members
=============
An method (getter) returns a private variable's value.
A method (setter) changes a private variable's value.
A variable is shared by all objects of the class.
UML Symbols
===========
means private.
means public. Predict the Output
Using the Student class from the Static vs Instance section:
Student s1 = new Student("Alice");
Student s2 = new Student("Bob");
Student s3 = new Student("Charlie");
System.out.println(Student.getCount());What is printed?
What does this code print?
BankAccount s1 = new BankAccount("Alice", 500.0);
BankAccount s2 = s1;
s2.deposit(100.0);
System.out.println(s1.getBalance());What is printed?
Practice Exercises
Core
-
Design a class – A gym tracks its members. Each member has a name, membership type (“basic” or “premium”), and months remaining. Design the
Memberclass: draw the UML diagram, then write the Java code with a constructor, getters, and a methodrenewMembership(int months)that adds months. -
Object array method – Write a method
countPremiumMembers(Member[] members)that returns how many members have “premium” membership type. Remember to check fornullelements. -
Encapsulation practice – Explain why the
balanceattribute inBankAccountisprivaterather thanpublic. What could go wrong if it werepublic?
Extension
-
Static vs instance – Add a static variable
totalMembersto yourMemberclass that tracks how manyMemberobjects have been created. The constructor should increment it. Add a static methodgetTotalMembers(). Explain whytotalMembersmust be static. -
UML from code – Given the following Java code, draw the complete UML class diagram including both classes and their relationship:
public class Engine { private int horsepower; private String fuelType; public Engine(int hp, String fuel) { ... } public int getHorsepower() { ... } } public class Car { private String make; private Engine engine; public Car(String make, Engine engine) { ... } public String getMake() { ... } public int getCarPower() { return engine.getHorsepower(); } }
Challenge
- Full class design – A school library system needs to track books and loans. Design two classes:
Bookwith title, author, ISBN, and loan statusLibrarywith an array of books (max 1000), methods to add a book, check out a book by ISBN, return a book by ISBN, and list all overdue books
Draw the UML for both classes showing the aggregation relationship. Write the full Java code. Include null checks in all array operations.
- Selection sort on objects – Write a method
sortByTitle(Book[] books)that sorts an array ofBookobjects alphabetically by title using selection sort. Use.compareTo()for String comparison.
Connections
- Prerequisites: Programming Constructs – you must understand method declaration, parameters, and return values before defining class methods
- Prerequisites: 1D Arrays – object arrays follow the same patterns as primitive arrays
- Related: Searching Algorithms – the same search patterns apply to object arrays (search by attribute using getters)
- Related: Sorting Algorithms – selection sort on object arrays is a common exam pattern
- Forward: OOP with Multiple Classes (HL) – inheritance, polymorphism, abstract classes