When you closed module 2 you made an uncomfortable diagnosis of your own program: highestDelay, highestDelayEmployee and highestDelayBook are three loose variables describing one single thing, and nothing stops you from updating one and forgetting the other two. That diagnosis is not a matter of style: it is the symptom of a structural limit of the programming style you have used so far. A procedural program organises code into steps; when the steps grow, related data scatters across independent variables and nobody guarantees they stay consistent with each other. Object-oriented programming (OOP) proposes a different organisation: group the data together with the operations that govern it, in units called objects, so that inconsistent data becomes impossible to build. This lesson does not write the Book class yet —that comes in the next one—; its job is to make you understand what problem OOP solves, what vocabulary you think in, and how the BiblioTech domain is modelled before you type a single brace.
Contents
- The starting point:
BiblioTechApp2.0 up close - Why procedural code degrades as it grows
- What object orientation is
- Class versus object: the blueprint and the house
- Identity, state and behaviour
- Instance and reference
- The four pillars at a glance
- Modelling the domain: from nouns to classes
- BiblioTech's target model
- Relationships between classes: association, composition and aggregation
- The real advantages of OOP... and its costs
- Common Mistakes and Tips
- Exercises
- The starting point:
BiblioTechApp 2.0 up close
BiblioTechApp 2.0 up closeYour application works. It shows a menu, validates input, applies Nexus Software's business rules and prints aligned receipts. But look at the block of declarations main starts with:
// Business rules
final int LOAN_DAYS = 15;
final double DAILY_RATE = 0.25;
final double MAX_FINE = 20.0;
final int MINOR_THRESHOLD = 7;
// Data for the operation in progress
String employee;
String title;
String isbn;
int elapsedDays;
int daysLate;
double fine;
String status;
String severity;
// Session statistics
int returns = 0;
int loans = 0;
double revenue = 0.0;
int highestDelay = 0;
String highestDelayEmployee = "-";
String highestDelayBook = "-";Count what is there: twenty variables in the same scope, all visible from any point in the two hundred lines of main, all modifiable from any point, and no explicit relationship between them. The compiler does not know that title and isbn describe the same book. It does not know that highestDelay and highestDelayEmployee must change together. It does not know that fine can never exceed MAX_FINE. All of that lives in your head and in the comments.
- Why procedural code degrades as it grows
The procedural style —data on one side, steps on the other— works perfectly in small programs. The problem appears when the program grows, and it always shows up in the same four ways:
| Symptom | How it looks in BiblioTech | Consequence |
|---|---|---|
| Scattered data | title, isbn, author describe a book but are independent variables |
Nothing guarantees the ISBN matches the title |
| Mutable global state | The twenty variables are visible throughout main |
Any line can corrupt any piece of data |
| Duplicated rules | The fine cap is applied in the return branch and in the scale table | If the rule changes, you have to hunt down every copy |
| Impossible to scale | Two books would need title1, isbn1, title2, isbn2... |
The code grows linearly with the data |
The fourth symptom is the most revealing. Ask yourself how you would register two simultaneous returns with the current design. The honest answer is: by duplicating variables. And with three, by tripling them. That road leads nowhere.
flowchart LR
subgraph PROC["Procedural"]
D1["title"]
D2["isbn"]
D3["daysLate"]
D4["fine"]
F1["calculate"]
F2["print"]
F1 -.-> D1
F1 -.-> D3
F2 -.-> D2
F2 -.-> D4
end
subgraph OO["Object-oriented"]
O1["Book
title, isbn
isAvailable()"]
O2["Loan
daysLate, fine
calculateFine()"]
O2 --> O1
end
On the left, data and functions cross in every direction. On the right, each piece of data lives inside the unit that knows what to do with it.
- What object orientation is
OOP is a paradigm: a way of organising a program. Its central idea fits in one sentence:
A program is a set of objects that collaborate by sending each other messages; each object keeps its own data and is the only one responsible for the operations that modify it.
Three immediate consequences:
- Data stops being passive. A
Loanis not a tuple of values that somebody inspects from outside; it is an entity you ask how much of a fine applies. - Rules live next to the data they govern. The rule "the fine never exceeds €20" becomes an internal matter for
Loan, not a line lost insidemain. - The program reads in the vocabulary of the business.
loan.calculateFine()is understandable without knowing the implementation;fine = daysLate * DAILY_RATE; if (fine >= MAX_FINE) fine = MAX_FINE;forces you to read arithmetic in order to deduce the intent.
Java is an object-oriented language by design: apart from the eight primitive types you saw in module 1, everything you have manipulated was already an object. String, Scanner and System.out are objects; scanner.nextLine() is precisely a message sent to an object. You have spent two modules using OOP without knowing it; what begins now is writing your own types.
- Class versus object: the blueprint and the house
This is the fundamental distinction, and it is worth pinning down with an analogy before looking at syntax.
A class is an architect's blueprint: it describes what each house will have (number of rooms, floor area) and what you will be able to do with it (open the door, switch on the light). The blueprint is not a house: you cannot live in it, it has no postal address and it cannot be painted blue.
An object is a house built from that blueprint: it occupies a specific place, it has specific values (140 m², painted blue) and it can be used. From the same blueprint a thousand houses are built, all with the same structure and each one with its own values.
| Concept | Class | Object |
|---|---|---|
| Nature | Template, definition, type | Concrete specimen in memory |
| When it exists | At compile time (it is code) | At run time (it occupies memory) |
| How many there are | One per definition | As many as you create |
| In BiblioTech | Book |
"Effective Java", "Refactoring" |
| Analogy | The blueprint, the mould, the recipe | The house, the part, the cake |
Translated into the domain: you will write one Book class and use it to create three objects, one for "Effective Java" (ISBN 978-0000000001), another for "Design Patterns" (978-0000000002) and another for "Refactoring" (978-0000000003). All three share structure —they all have a title, author, ISBN, year and availability— and differ in their values.
- Identity, state and behaviour
Every object is described by three properties. Internalising them will save you confusion throughout the module.
- Identity: which object it is, regardless of its values. Two physical copies of "Effective Java" on the shelf are different objects even though they share a title and an ISBN. In Java, identity is the position in memory, and it is what
==compares (you saw it in module 1 with the string pool, and it will come back in 03-09). - State: the values the object holds at a given moment. The state of a
Bookincludestitle,author,isbn,publicationYearandavailable. State changes:availablegoes fromtruetofalsewhen someone takes the book away. - Behaviour: the operations the object knows how to perform. A
Loanknows how to calculate its days late, its fine and its severity.
Applied to the vocabulary you already use:
| Class | State (fields) | Behaviour (operations) |
|---|---|---|
Book |
title, author, isbn, publicationYear, available |
be lent, be returned, say whether it is available |
Employee |
name, identifier, total loans |
register a loan, say how many it has accumulated |
Loan |
book, employee, elapsedDays |
calculate daysLate, fine, severity, status |
Notice a detail that will govern the whole module: the right-hand column is what is loose inside main today. OOP does not invent new behaviour; it moves it, from the procedure to the object it belongs to.
- Instance and reference
Two words you will hear constantly and that are worth separating right now:
- Instance is a synonym for object: the specific specimen created from a class. "Create an instance of
Book" and "create aBookobject" mean the same thing. - Reference is the variable through which you reach that object. It is not the object: it is the address where it lives.
The distinction matters because in Java you never manipulate objects directly, always through references. When you write:
three independent things happen: memory is reserved for a Book object on the heap (we called it "the heap" in module 1), a variable effectiveJava is declared on the stack, and the object's address is stored in that variable. One immediate consequence, which lesson 03-02 develops in detail: two references can point to the same object, and then a change made through one is seen through the other.
- The four pillars at a glance
OOP rests on four ideas. Here they are, one sentence each, with the lesson where they are developed; do not try to master them now, just recognise the names when they appear.
| Pillar | In one sentence | Example in BiblioTech | Lesson |
|---|---|---|---|
| Encapsulation | The object hides its internal representation and only exposes controlled operations | Nobody can assign an arbitrary fine: it is calculated on return | 03-07 |
| Inheritance | A class can be defined as a specialisation of another and reuse what that one already defines | Book, Magazine and Dvd are types of lendable Material |
03-05 |
| Polymorphism | The same call produces different behaviour depending on the object's actual type | material.calculateFine() charges €0.25/day for a book and €0.50/day for a DVD |
03-06 |
| Abstraction | Only what is essential to the problem is modelled and the rest is hidden | BiblioTech cares about a book's ISBN, not its weight or its cover colour | 03-08 |
A note of honesty: these four terms are recited a lot and understood little. By the end of the module you will not have memorised them, you will have used them, which is the only way to learn them.
- Modelling the domain: from nouns to classes
Before writing code you have to decide which classes exist. There is a simple and surprisingly effective technique to get started: underline the nouns in the problem statement. This is BiblioTech's statement as Nexus Software's department would give it to you:
The internal technical library manages the books that employees take out on loan. Each book has a title, an author, an ISBN and a publication year, and it may be available or not. A loan lasts 15 days; past that term, days late accumulate and generate a fine of €0.25 per day with a maximum of €20. A delay of up to 7 days is considered minor; above that, severe.
Candidate nouns: library, book, employee, loan, title, author, ISBN, year, days, fine, delay.
Now filter them with three criteria:
- Does it have its own identity and state that changes? → it is a candidate for a class. Book, Employee, Loan all qualify.
- Is it a simple descriptive value of something else? → it is a candidate for a field. Title, ISBN, year describe a book; days late and fine describe a loan.
- Is it the whole system or a vague concept? → normally it is not a domain class. Library is the system; what will show up later is a
Catalogthat stores materials and aLoanManagerthat orchestrates operations (modules 5 onwards).
Result of the filtering for this module: three classes, Book, Employee and Loan, exactly the ones announced when module 2 closed.
And one question that separates a good model from a mediocre one: whose responsibility is each thing? A misplaced responsibility compiles just the same, but it poisons the design.
| Responsibility | Whose? | Why |
|---|---|---|
| Knowing its ISBN | Book |
It is intrinsic data of the book |
| Knowing whether it is on loan | Book |
It is a state of the copy |
| Calculating the days late | Loan |
It depends on the term and the time elapsed, not on the book |
| Calculating the fine | Loan |
It is a consequence of the delay of that operation |
| Knowing how many loans it has accumulated | Employee |
It is the person's history |
| Printing the receipt | Neither Book nor Loan |
It is presentation; it is kept apart from the domain (lesson 03-08) |
- BiblioTech's target model
This is the model you will arrive at by the end of the module. Keep it as a map: each lesson adds a piece.
classDiagram
class Book {
-String title
-String author
-String isbn
-int publicationYear
-boolean available
+isAvailable() boolean
+lend() void
+returnItem() void
+toString() String
+equals(Object) boolean
}
class Employee {
-String name
-String identifier
-int totalLoans
+registerLoan() void
+getTotalLoans() int
+toString() String
}
class Loan {
-Book book
-Employee employee
-int loanDay
-int dueDay
-int elapsedDays
+calculateDaysLate() int
+calculateFine() double
+classifySeverity() String
+registerReturn(int) void
+toString() String
}
Loan --> Book : book lent
Loan --> Employee : requester
Read it like this: a Loan knows a Book and an Employee; neither the book nor the employee needs to know the loan. The arrows show the direction of the dependency, and that direction is a design decision, not an accident.
In lesson 03-05 this diagram will grow upwards: a Material class will appear, of which Book, Magazine and Dvd will be specialisations.
- Relationships between classes: association, composition and aggregation
Classes rarely live in isolation. There are three typical ways of relating, besides inheritance:
| Relationship | Meaning | Lifetime of the link | Example in BiblioTech |
|---|---|---|---|
| Association | "uses" or "knows about" | Independent | Loan knows the Employee who requests it |
| Aggregation | "has a" with parts that outlive the whole | The whole can disappear and the parts carry on | A Catalog aggregates Books: if the catalog is deleted, the books still exist |
| Composition | "is made up of" with parts that die with the whole | The parts make no sense without the whole | A Loan composes its own record of dates and status |
| Inheritance | "is a" | Structural, at compile time | A Book is a Material (lesson 03-05) |
The practical test to tell aggregation from composition is to ask: if I destroy the containing object, does it make sense for the part to keep existing? If the answer is yes, it is aggregation; if it is no, composition. A book outlives the catalog that lists it; the internal breakdown of a fine does not outlive the loan that generated it.
Do not obsess over these labels: in Java code, association, aggregation and composition are written almost identically (a field that references another object). Their value lies in the design and in the conversation with other developers.
- The real advantages of OOP... and its costs
Courses usually list only the left-hand column. A professional needs both.
| Advantages | Costs |
|---|---|
| Related data travels together and does not fall out of sync | Indirection: to find out what loan.calculateFine() does you have to open another file |
| Business rules live in a single place | Ceremony: more files, more lines for the same thing in small programs |
| Code reads in the vocabulary of the business | Over-design: it is easy to create seven-level hierarchies for a two-level problem |
| Adding a new type does not force you to touch existing code (03-06) | Learning curve: inheritance and polymorphism have subtle traps |
| Each class can be tested separately (module 11) | Hidden coupling: a badly designed base class breaks all its subclasses (03-05) |
A practical rule that will serve you for years: OOP pays off when the program has to grow and change. A thirty-line script that runs once does not need classes. BiblioTech does: it is going to grow for ten more modules.
Common Mistakes and Tips
- Confusing class with object when speaking. "I'm going to modify the book" is ambiguous: the
Bookclass (the blueprint, affecting all of them) or theeffectiveJavaobject (one copy)? Get used to naming the class in singular with a capital letter (Book) and objects with concrete names (effectiveJava). - Turning every noun in the statement into a class. Not everything nameable deserves a class.
titleis aString, not aTitleclass. Apply the filter from section 8: identity + state that changes. - Creating classes that only hold data. A class with five fields and ten getters, with no behaviour at all, is a data structure in disguise (it is called an anaemic model). Always ask yourself what the class knows how to do, not just what it contains.
- Putting behaviour in the wrong class. If
Bookcalculated fines, it would have to know terms, rates and elapsed days, data that does not belong to it. Each operation goes where the data it needs lives. - Starting with inheritance. It is the flashiest pillar and the most dangerous. Model flat classes that work first; the hierarchy will appear by itself when you need it (lesson 03-05).
- Tip: draw before you type. Five minutes of class diagram on paper save hours of refactoring. You do not need formal UML: named boxes, fields and arrows are enough.
- Tip: name things in the language of the business.
Loan,BookandEmployeeare better names thanDataRecord,ItemorManager, because anyone at Nexus Software understands them without explanation.
Exercises
Exercise 1: identify classes, fields and responsibilities
Nexus Software wants to extend BiblioTech with a meeting rooms module:
Employees book meeting rooms. Each room has a name, a maximum capacity of people and equipment (projector, yes or no). A booking is made by an employee for a room, in a specific time slot, with a number of attendees that cannot exceed the room's capacity. A booking can be cancelled.
Without writing complete Java code:
- List the nouns and classify them as class, field or discarded, justifying each decision.
- Write a responsibility table: what each class must know how to do.
- State what kind of relationship (association, aggregation or composition) there is between
ReservationandMeetingRoom, and betweenReservationandEmployee.
Exercise 2: diagram and class skeletons
Using the result of exercise 1:
- Draw the mermaid
classDiagramof the meeting rooms model. - Write the skeletons of the classes (just the declaration and the fields, with comments where the operations would go). Do not implement anything: the full syntax arrives in 03-02.
Exercise 3: spot misplaced responsibilities
A colleague proposes this design for BiblioTech. Point out three decisions you consider wrong and explain where each responsibility should live:
class Book {
String title;
String isbn;
double lastLoanFine; // (a)
String currentEmployeeName; // (b)
void printReturnReceipt() { } // (c)
void saveToFile() { } // (d)
}Solutions
Solution 1
1. Classification of nouns
| Noun | Classification | Justification |
|---|---|---|
| Employee | Class | Already exists in the model; it has identity and its own state |
| Room | Class | It has identity (each room is different) and state (equipment, capacity) |
| Booking | Class | It has identity, state that changes (active/cancelled) and behaviour (be cancelled) |
| Name, capacity, projector | Fields of MeetingRoom |
They are values describing the room, not entities with a life of their own |
| Time slot, attendees | Fields of Reservation |
They describe a specific booking |
| Module, system | Discarded | They are the whole program, not concepts of the domain |
A professional nuance: time slot could eventually become a class of its own (TimeSlot, with a start and end time, able to say whether it overlaps another). That is a legitimate decision, but premature now: start with fields and promote to a class only when the concept accumulates behaviour of its own.
2. Responsibilities
| Class | Must know |
|---|---|
MeetingRoom |
Its name, its capacity and whether it has a projector; say whether it admits a given number of attendees |
Employee |
Its name and identifier; how many bookings it has accumulated |
Reservation |
Which room, which employee, which slot and how many attendees; validate that they fit; be cancelled; say whether it is active |
Notice that "say whether it admits N attendees" belongs to MeetingRoom, because capacity is its own data; whereas "validate that they fit" belongs to Reservation, because only the booking knows the number of attendees. Each one contributes what it knows.
3. Relationships
Reservation→MeetingRoom: association (or aggregation). The room exists before and after the booking; cancelling a booking does not destroy the room.Reservation→Employee: association. The employee has a life of their own in the company.
There is no obvious composition in this model, unless the time slot were modelled as an object internal to the booking: then it would indeed be composition, because that slot means nothing outside its booking.
Solution 2
Diagram
classDiagram
class MeetingRoom {
-String name
-int maxCapacity
-boolean hasProjector
+accommodates(int attendees) boolean
}
class Employee {
-String name
-String identifier
-int totalReservations
}
class Reservation {
-MeetingRoom room
-Employee requester
-int startHour
-int endHour
-int attendees
-boolean active
+cancel() void
+isActive() boolean
}
Reservation --> MeetingRoom : books
Reservation --> Employee : requested by
Skeletons
package com.nexussoftware.bibliotech.domain;
public class MeetingRoom {
String name;
int maxCapacity;
boolean hasProjector;
// Planned operations:
// accommodates(int attendees) -> boolean
}package com.nexussoftware.bibliotech.domain;
public class Reservation {
MeetingRoom room; // reference to another object: association
Employee requester; // association
int startHour; // 9 = 09:00, provisional integer format
int endHour;
int attendees;
boolean active;
// Planned operations:
// cancel() -> void
// isActive() -> boolean
// everyoneFits() -> boolean (delegates to room.accommodates(attendees))
}Two details that will come back: the fields referencing other classes (MeetingRoom room) are exactly what we drew as an arrow in the diagram, and the hours are modelled as integers because LocalTime belongs to java.time, which is studied in lesson 10-05.
Solution 3
(a) lastLoanFine in Book is misplaced. The fine is a consequence of one specific loan operation, not a property of the book. With this design, if the same copy is lent five times, the field only remembers the last fine and loses the history; and worse, it forces Book to know terms and rates, which are none of its business. The fine belongs to Loan.
(b) currentEmployeeName in Book is wrong for two reasons. First, it is the same scattering that BiblioTechApp suffers from today: storing the name instead of the Employee leaves the data orphaned, with no way to reach the identifier or the accumulated loans. Second, and more importantly, the book–employee link only exists while there is a loan, so its natural home is the Loan class, which already relates the two. If Book must know anything, it is only whether it is available or not.
(c) printReturnReceipt() mixes domain and presentation. A domain class should not know that a console exists, nor the receipt's format, nor the width of the columns. If tomorrow BiblioTech became a web application (module 12), the whole class would have to be rewritten. The receipt is generated by whoever handles the interface. This is the problem of mixed abstraction levels, covered in depth in lesson 03-08.
(d) saveToFile() adds an alien responsibility. Persisting to disk is an infrastructure matter (module 7), not part of the concept "book". A class with two different reasons to change —the business changes, the file format changes— is a class that will make its maintainers unhappy.
Corrected design, in skeleton form:
public class Book {
String title;
String author;
String isbn;
int publicationYear;
boolean available;
// Operations: isAvailable(), lend(), returnItem()
}
public class Loan {
Book book; // the one being lent
Employee employee; // the one it is lent to
int elapsedDays;
// Operations: calculateDaysLate(), calculateFine(), classifySeverity()
}Conclusion
In this lesson you have changed the way you think before changing the way you write. You have seen why the procedural style degrades as the program grows —scattered data, global state, duplicated rules, impossibility of scaling—, with your own BiblioTechApp as the case study. You have learned the essential vocabulary: class as blueprint and object as specimen, identity, state and behaviour, instance and reference. You have a map of the four pillars and of the lesson where each one is developed. And, above all, you have modelled BiblioTech's domain: from the nouns of the statement to three classes —Book, Employee, Loan— with explicit responsibilities, drawn relationships and a target diagram that will guide the rest of the module. You also know that OOP is not free: it costs indirection, ceremony and the risk of over-design, and it only pays for itself when the software has to grow.
You have the blueprint. In the next lesson, Classes and Objects, you start building: you will declare the Book class with its exact syntax, you will see what value the fields take before you assign them anything, you will create objects with new following step by step what happens on the stack and on the heap, you will discover what a NullPointerException really means and why two references to the same object can cause unpleasant surprises. By the end of it, "Effective Java", "Design Patterns" and "Refactoring" will have stopped being loose strings of text and become three objects with a life of their own inside BiblioTechApp.
Java Programming Course
Module 1: Introduction to Java
- Introduction to Java
- Setting Up the Development Environment
- Basic Syntax and Structure
- Variables and Data Types
- Operators
- Console Input and Output
- Your First Complete Program: BiblioTech
Module 2: Control Flow
- Conditional Statements
- Loops
- Switch Statements
- Break and Continue
- Debugging and Execution Traces
- Project: The BiblioTech Interactive Menu
Module 3: Object-Oriented Programming
- Introduction to OOP
- Classes and Objects
- Methods
- Constructors
- Inheritance
- Polymorphism
- Encapsulation
- Abstraction
- The Object Class: equals, hashCode and toString
Module 4: Advanced Object-Oriented Programming
- Interfaces
- Abstract Classes
- Inner Classes
- Anonymous Classes
- Lambda Expressions
- Functional Interfaces and Method References
- Enums and Records
Module 5: Data Structures and Collections
- Arrays
- The Collections Framework
- ArrayList
- LinkedList
- HashMap
- HashSet
- Queue and Deque
- Stack
- Sorting and Searching Collections
Module 6: Exception Handling
- Introduction to Exceptions
- The Try-Catch Block
- Throw and Throws
- Custom Exceptions
- The Finally Block
- Try-with-resources and AutoCloseable
- Error Handling Strategies and Logging
Module 7: File Input/Output
- Reading Files
- Writing Files
- File Streams
- BufferedReader and BufferedWriter
- Serialization
- The NIO.2 API: Path and Files
- Interchange Formats: CSV and Properties
Module 8: Multithreading and Concurrency
- Introduction to Multithreading
- Creating Threads
- Thread Lifecycle
- Synchronization
- Concurrency Utilities
- Concurrent Collections and Atomic Variables
- Asynchronous Tasks with CompletableFuture
Module 9: Networking
- Introduction to Networking
- Sockets
- ServerSocket
- DatagramSocket and DatagramPacket
- URL and HttpURLConnection
- The Modern HTTP Client
Module 10: Advanced Topics
- Generics
- Annotations
- Reflection
- Java 8 Features: Streams and Optional
- Dates and Times with java.time
- Java 9 and Beyond
- Memory, Garbage Collection and Performance
Module 11: Java Frameworks and Libraries
- Introduction to Java Frameworks
- Spring Framework
- Hibernate
- JUnit
- Maven
- Advanced Testing with Mockito
- Essential Ecosystem Libraries
