Topic 4. Objects and Classes
As code grows beyond a few files, procedural style becomes fragile: data and behavior drift apart, and changes become risky. Object-oriented design addresses this by grouping state and behavior into class-level contracts.
Introduction to Object-Oriented Programming
In OOP, a system is a set of objects interacting through methods.
This keeps responsibilities clearer. Example: Order computes totals, while EmailService handles notifications.
Using Existing Classes
In real projects, you combine your own classes with existing ones (String, List, Map, LocalDate, framework classes).
Core skill: understand class contracts.
- What inputs are valid.
- What is returned in normal scenarios.
- What exceptions can happen.
- What side effects exist.
Building Your Own Classes
A good class starts with invariants: states that must always remain valid.
public class Student {
private final String name;
private int score;
public Student(String name, int score) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("name is required");
}
if (score < 0) {
throw new IllegalArgumentException("score must be >= 0");
}
this.name = name;
this.score = score;
}
public void addScore(int delta) {
if (delta > 0) {
score += delta;
}
}
public int getScore() {
return score;
}
}
Static Fields and Methods
static means member belongs to the class, not an instance.
public class IdGenerator {
private static long next = 1;
public static long nextId() {
return next++;
}
}
Use static for constants, factories, utility behavior. Avoid uncontrolled mutable global state.
Method Parameters
Method parameters define input contracts.
Important theory
- Parameter type constrains allowed inputs.
- Parameter names should reflect domain meaning (
amount,studentId). - Long parameter lists increase error probability.
How Java passes parameters
Java always passes arguments by value:
- primitives: value is copied,
- objects: reference value is copied.
Implication:
- called method cannot rebind caller variable to a new object,
- called method can mutate the object referenced by both sides.
public class ParamDemo {
static void changeInt(int n) {
n = 99;
}
static void changeScore(Student s) {
s.addScore(10);
}
}
Practical parameter design steps
- Define minimal required inputs.
- If parameter count grows beyond 3-4, consider request object.
- Validate preconditions (null, ranges, format).
- Be explicit about mutation behavior.
Object Construction
Constructor goal: create a valid object immediately.
Weak beginner pattern: create empty object, then “fix it later” via setters. That introduces invalid intermediate states.
Better flow:
- Identify mandatory fields.
- Pass them to constructor.
- Validate inputs in constructor.
- Throw clear exceptions for invalid data.
- Ensure object is usable right after construction.
public class BankAccount {
private final String owner;
private long balance;
public BankAccount(String owner, long initialBalance) {
if (owner == null || owner.isBlank()) {
throw new IllegalArgumentException("owner is required");
}
if (initialBalance < 0) {
throw new IllegalArgumentException("initialBalance must be >= 0");
}
this.owner = owner;
this.balance = initialBalance;
}
}
Packages
package organizes classes by responsibility, not random folders.
It solves:
- Logical project structure.
- Visibility boundaries (
public, package-private,protected).
Beginner mental model
- Package = district.
- Class = building in that district.
import= address used to reach another building.
Example structure:
com.course.courseweb
├── controller
│ └── ArticleController
├── service
│ └── ArticleService
├── repo
│ └── ArticleRepository
└── model
└── Article
Package declaration:
package com.course.courseweb.service;
Import usage:
import com.course.courseweb.model.Article;
Documentation Comments
Use Javadoc for public APIs and shared team contracts.
Describe:
- method purpose,
- input constraints,
- return behavior,
- thrown exceptions.
Class Design Hints
- One class should solve one primary responsibility.
- Protect invariants in constructors and critical methods.
- Keep public API minimal and clear.
- Keep fields
privateby default. - Use meaningful names for classes, methods, and parameters.
- Avoid “god objects” that know and do too much.
- If a method needs too much explanation, simplify the method first.