WL
Java Full Stack Developer
Wassim Lagnaoui

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.