Lesson 03: Object Oriented Programming (OOP)
Model real things with classes and build usable instances as objects. Learn fields, constructors, and how this clarifies intent.
Introduction
Imagine you're designing a video game and you need to create different types of characters: warriors, mages, and archers. Instead of writing separate code for each character that duplicates common features like health, name, and level, you can create a blueprint that defines what all characters have in common, then create specific instances for each player. Object-oriented programming (OOP) works exactly this way - it lets you model real-world concepts by creating blueprints called classes and building actual working copies called objects. A class is like an architectural blueprint that shows what rooms a house will have and where they'll be located, while an object is the actual house built from that blueprint where people can live. OOP helps you organize your code around things rather than just actions, making it easier to understand, maintain, and extend. This approach mirrors how we naturally think about the world - in terms of things (objects) that have characteristics (fields) and can perform actions (methods). Once you understand OOP, you'll be able to model complex real-world systems in your programs.
Classes
Definition
A class is a blueprint or template that defines the structure and behavior of objects. It specifies what data (fields) objects of that type will have and what actions (methods) they can perform. Classes don't store actual data themselves - they're just the plans for creating objects that do store data. Think of a class as the mold used to create multiple identical-shaped objects.
Analogy
A class is like a cookie cutter in your kitchen. The cookie cutter itself isn't a cookie - it's just a shaped tool that defines what cookies made with it will look like. When you press the cookie cutter into dough, you create actual cookies that have the shape defined by the cutter. You can use the same cookie cutter to make dozens of cookies, and while each cookie is a separate, individual item you can eat, they all share the same basic shape and characteristics defined by the cutter. Similarly, a class defines the "shape" (what fields and methods it has) but doesn't hold any actual data. When you use the class to create objects, each object gets its own copy of the data but follows the same structure defined by the class.
Example
// Class definition - the blueprint
public class Student { // class keyword starts the blueprint
// Fields (data/attributes) - what every Student object will have
String name; // student's name
int age; // student's age
String major; // student's field of study
double gpa; // grade point average
// Methods (behaviors) - what every Student object can do
void study() { // action: studying
System.out.println(name + " is studying " + major);
}
void displayInfo() { // action: show information
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Major: " + major);
System.out.println("GPA: " + gpa);
}
void updateGPA(double newGPA) { // action: change GPA
gpa = newGPA; // modify the field
System.out.println(name + "'s GPA updated to " + gpa);
}
}
// Using the class - this is just the blueprint, no actual students exist yet
public class StudentDemo {
public static void main(String[] args) {
// The class Student exists, but no actual student data is stored yet
// We need to create objects (instances) to hold real student information
System.out.println("Student class defined, ready to create actual students...");
}
}
Objects
Definition
An object is a specific instance created from a class blueprint. While the class defines the structure, objects contain actual data and can perform actions. Each object has its own copy of the fields defined in the class, so changing one object's data doesn't affect other objects. You create objects using the `new` keyword followed by the class name.
Analogy
If a class is like a house blueprint, then an object is an actual house built from that blueprint. The blueprint shows where the kitchen, bedrooms, and bathrooms will be, but you can't live in a blueprint - you need an actual house. Once built, each house has its own address, its own furniture, and its own family living inside. You can build multiple houses from the same blueprint, but each house is completely separate - painting one house blue doesn't change the color of the others. When the Smith family moves their furniture around, it doesn't affect the furniture in the Jones family's house next door, even though both houses were built from the same blueprint. Each house (object) has the same room layout (class structure) but contains different belongings (data values).
Example
// Creating and using objects from the Student class
public class StudentObjectsDemo {
public static void main(String[] args) {
// Create first student object - Alice
Student alice = new Student(); // 'new Student()' creates an object
alice.name = "Alice Johnson"; // set Alice's name
alice.age = 20; // set Alice's age
alice.major = "Computer Science"; // set Alice's major
alice.gpa = 3.8; // set Alice's GPA
// Create second student object - Bob
Student bob = new Student(); // create another separate object
bob.name = "Bob Smith"; // Bob has his own name
bob.age = 22; // Bob has his own age
bob.major = "Mathematics"; // Bob has his own major
bob.gpa = 3.6; // Bob has his own GPA
// Create third student object - Carol
Student carol = new Student(); // create third separate object
carol.name = "Carol Davis"; // Carol has her own data
carol.age = 19; // completely independent from Alice and Bob
carol.major = "Physics";
carol.gpa = 3.9;
// Each object can perform actions independently
alice.displayInfo(); // shows Alice's information
System.out.println("---");
bob.displayInfo(); // shows Bob's information
System.out.println("---");
carol.displayInfo(); // shows Carol's information
// Objects can perform actions that affect only themselves
alice.study(); // only Alice studies
bob.updateGPA(3.7); // only Bob's GPA changes
carol.study(); // only Carol studies
// Verify that changing one object doesn't affect others
System.out.println("\nAfter individual actions:");
System.out.println("Alice's GPA: " + alice.gpa); // still 3.8
System.out.println("Bob's GPA: " + bob.gpa); // changed to 3.7
System.out.println("Carol's GPA: " + carol.gpa); // still 3.9
}
}
Fields
Definition
Fields (also called instance variables or attributes) are variables that belong to a class and store data for each object. Every object created from the class gets its own copy of these fields. Fields define what information an object can remember, like a person's name, age, or address. Fields represent the state or characteristics of an object.
Analogy
Fields are like the information written on a driver's license. Every driver's license has the same categories of information - name, address, birthdate, license number, and photo - but each person's license contains their own unique values for these categories. Your license has your name and address, while your friend's license has their name and address. The license format (class) is the same for everyone, but the actual information (field values) is different for each person (object). When you move to a new address, only your license gets updated - everyone else's license stays the same. The categories of information (fields) are defined by the Department of Motor Vehicles (the class), but each individual license holder (object) has their own specific values stored in those categories.
Example
// Class demonstrating different types of fields
public class BankAccount {
// Fields - data that each BankAccount object will store
String accountNumber; // text field - account ID
String ownerName; // text field - who owns account
double balance; // number field - current money amount
boolean isActive; // true/false field - account status
int transactionCount; // whole number field - number of transactions
// Method to show all field values for this specific object
void displayAccountInfo() {
System.out.println("=== Account Information ===");
System.out.println("Account Number: " + accountNumber);
System.out.println("Owner: " + ownerName);
System.out.println("Balance: $" + balance);
System.out.println("Active: " + isActive);
System.out.println("Transactions: " + transactionCount);
System.out.println();
}
// Method to modify field values
void deposit(double amount) {
balance = balance + amount; // change balance field
transactionCount = transactionCount + 1; // change transaction count field
System.out.println("Deposited $" + amount + " to account " + accountNumber);
}
}
public class FieldsDemo {
public static void main(String[] args) {
// Create first account object with its own field values
BankAccount account1 = new BankAccount();
account1.accountNumber = "ACC001"; // set account1's account number
account1.ownerName = "John Doe"; // set account1's owner name
account1.balance = 1500.50; // set account1's balance
account1.isActive = true; // set account1's status
account1.transactionCount = 5; // set account1's transaction count
// Create second account object with different field values
BankAccount account2 = new BankAccount();
account2.accountNumber = "ACC002"; // account2 has different account number
account2.ownerName = "Jane Smith"; // account2 has different owner
account2.balance = 2300.75; // account2 has different balance
account2.isActive = false; // account2 has different status
account2.transactionCount = 12; // account2 has different transaction count
// Display both accounts - each has its own field values
account1.displayAccountInfo(); // shows account1's specific data
account2.displayAccountInfo(); // shows account2's specific data
// Modify one account's fields - doesn't affect the other
account1.deposit(200.00); // only changes account1's balance and count
// Show that only account1 changed
System.out.println("After deposit to account1:");
System.out.println("Account1 balance: $" + account1.balance); // changed
System.out.println("Account1 transactions: " + account1.transactionCount); // changed
System.out.println("Account2 balance: $" + account2.balance); // unchanged
System.out.println("Account2 transactions: " + account2.transactionCount); // unchanged
}
}
Constructors
Definition
A constructor is a special method that runs automatically when you create a new object. It has the same name as the class and no return type. Constructors are used to initialize an object's fields with starting values, ensuring that objects begin in a valid, usable state. You can have multiple constructors with different parameters to provide flexibility in how objects are created.
Analogy
A constructor is like the setup process when you first get a new smartphone. Before you can really use the phone, it automatically walks you through essential setup steps: choosing your language, connecting to WiFi, signing into your account, and setting up basic preferences. You can't skip this setup - it happens automatically when you first turn on the phone. The phone manufacturer (class) designed this setup process (constructor) to ensure every phone starts in a working state with the necessary basic configuration. Some phones might have a quick setup for basic users and an advanced setup for power users - these are like having multiple constructors that let you initialize the phone in different ways depending on your needs.
Example
// Class with multiple constructors
public class Car {
// Fields that every car has
String make; // car manufacturer
String model; // car model
int year; // year manufactured
String color; // car color
double mileage; // miles driven
// Default constructor - creates car with basic values
public Car() { // no parameters
make = "Unknown"; // set default make
model = "Unknown"; // set default model
year = 2025; // set default year
color = "White"; // set default color
mileage = 0.0; // set default mileage
System.out.println("Created a default car");
}
// Constructor with basic information
public Car(String carMake, String carModel, int carYear) {
make = carMake; // set provided make
model = carModel; // set provided model
year = carYear; // set provided year
color = "White"; // default color
mileage = 0.0; // default mileage
System.out.println("Created " + year + " " + make + " " + model);
}
// Constructor with full information
public Car(String carMake, String carModel, int carYear, String carColor, double carMileage) {
make = carMake; // set all provided values
model = carModel;
year = carYear;
color = carColor;
mileage = carMileage;
System.out.println("Created " + year + " " + color + " " + make + " " + model + " with " + mileage + " miles");
}
// Method to display car information
void displayCarInfo() {
System.out.println(year + " " + color + " " + make + " " + model + " - " + mileage + " miles");
}
}
public class ConstructorDemo {
public static void main(String[] args) {
// Using different constructors to create cars
// Default constructor - minimal setup
Car car1 = new Car(); // calls default constructor
car1.displayCarInfo();
// Constructor with basic info
Car car2 = new Car("Toyota", "Camry", 2023); // calls 3-parameter constructor
car2.displayCarInfo();
// Constructor with full info
Car car3 = new Car("Honda", "Civic", 2022, "Blue", 15000.5); // calls 5-parameter constructor
car3.displayCarInfo();
// Each car was properly initialized by its constructor
System.out.println("\nAll cars were created with valid starting values:");
System.out.println("Car1 make: " + car1.make); // "Unknown" from default constructor
System.out.println("Car2 color: " + car2.color); // "White" from 3-parameter constructor
System.out.println("Car3 mileage: " + car3.mileage); // 15000.5 from 5-parameter constructor
}
}
The this Keyword
Definition
The `this` keyword refers to the current object - the specific instance of the class that is currently executing the method. It's used to distinguish between instance variables (fields) and parameters or local variables that have the same name. The `this` keyword helps clarify which variable you're referring to and can also be used to call other constructors within the same class.
Analogy
The `this` keyword is like saying "my" when you're talking about yourself in a conversation. Imagine you're at a party where several people have the same name. When someone asks "What's your phone number?", you naturally say "MY phone number is..." to make it clear you're talking about your own number, not someone else's who happens to share your name. In a classroom, if the teacher asks "What's your grade?", you'd say "MY grade is an A" to distinguish your grade from other students' grades. The word "my" (like `this`) points specifically to your own information, not someone else's. In Java, when multiple variables have the same name, `this` acts like "my" to specify that you're referring to the current object's version of that variable.
Example
// Class demonstrating the 'this' keyword
public class Person {
// Fields (instance variables)
String name; // person's name
int age; // person's age
String city; // person's city
// Constructor with parameters that match field names
public Person(String name, int age, String city) {
// Without 'this', Java wouldn't know which 'name' you mean:
// name = name; // This would assign parameter to itself - doesn't work!
// Using 'this' to clarify - 'this.name' means the object's field
this.name = name; // this object's name field = parameter value
this.age = age; // this object's age field = parameter value
this.city = city; // this object's city field = parameter value
System.out.println("Created person: " + this.name);
}
// Method where 'this' helps with clarity
public void updateAge(int age) { // parameter has same name as field
System.out.println("Updating age for " + this.name); // clearly refers to object's name field
this.age = age; // this object's age field = new parameter value
System.out.println(this.name + " is now " + this.age + " years old");
}
// Method demonstrating calling another method on the same object
public void introduce() {
System.out.println("Hi, I'm " + this.name); // this object's name
this.showDetails(); // call another method on this same object
}
public void showDetails() {
// 'this' is optional when there's no naming conflict, but makes intent clear
System.out.println("Name: " + this.name); // this object's fields
System.out.println("Age: " + this.age);
System.out.println("City: " + this.city);
}
// Method that compares this object with another object
public boolean isOlderThan(Person otherPerson) {
return this.age > otherPerson.age; // compare this object's age with other object's age
}
}
public class ThisKeywordDemo {
public static void main(String[] args) {
// Create two person objects
Person alice = new Person("Alice", 25, "New York"); // 'this' refers to alice object in constructor
Person bob = new Person("Bob", 30, "Los Angeles"); // 'this' refers to bob object in constructor
// Each object's methods use 'this' to refer to themselves
alice.introduce(); // 'this' in introduce() refers to alice
System.out.println("---");
bob.introduce(); // 'this' in introduce() refers to bob
// Update age - 'this' ensures we update the correct object
alice.updateAge(26); // 'this' in updateAge() refers to alice
bob.updateAge(31); // 'this' in updateAge() refers to bob
// Compare objects - 'this' refers to the object calling the method
if (alice.isOlderThan(bob)) { // 'this' in isOlderThan() refers to alice
System.out.println("Alice is older than Bob");
} else {
System.out.println("Bob is older than Alice");
}
}
}
Summary
Object-oriented programming provides a natural way to model real-world concepts in your code by organizing information and behavior together. Classes serve as blueprints that define what data objects will store and what actions they can perform, while objects are the actual instances that hold specific values and can execute those actions. Fields store the state and characteristics of each object, constructors ensure objects start with valid initial values, and the `this` keyword helps you clearly refer to the current object when needed. These concepts work together to create code that is more organized, reusable, and easier to understand because it mirrors how we naturally think about things in the real world. In the next lesson, we'll dive deeper into advanced OOP concepts like encapsulation, inheritance, and polymorphism, which will give you even more powerful tools for designing robust and flexible programs.