Skip to main content

Section 3.4 Classes and Objects

So far, weโ€™ve battled this messy situation:
// Player 1's stats
int player1Health = 100;
int player1Gold = 50;

// Player 2's stats (getting messy...)
int player2Health = 100;
int player2Gold = 50;

// Whoops, which health am I changing?
player1Health = healPlayer(player2Health, 5); // Easy mistake!
Methods helped a bit, but we still pass variables around separately. Wouldnโ€™t it be great if all these stats lived in one place?

Subsection 3.4.1 A Single โ€œEntityโ€ That Owns Its Data

Letโ€™s pretend we want to treat the player as a single entity, storing all of that playerโ€™s stats in one โ€œbox.โ€ We also want that โ€œboxโ€ to have special โ€œbuttonsโ€ (like โ€œhealโ€ or โ€œadd goldโ€) that automatically change that playerโ€™s stats.
Thatโ€™s exactly what Javaโ€™s class feature will let us do. But letโ€™s build it slowly so itโ€™s not mysterious.

Subsection 3.4.2 Step 1: An Empty "Container" Called Player

Letโ€™s start with the most basic version of this feature. Hereโ€™s the smallest possible container we can make:
public class Player {
    // We'll fill details in soon!
}
This might look empty and useless, but weโ€™ve just done something powerful: weโ€™ve created a new type in Java. Just like int lets us create numbers and String lets us create text, Player will let us create... well, players! Weโ€™ll fill in what that means exactly, but the key idea is there: Java lets us create our own types to represent anything we need in our programs.
Right now our container is emptyโ€”like an empty box. It compiles, but doesnโ€™t hold anything yet. Letโ€™s fix that next.

Subsection 3.4.3 Step 2: Putting Variables Inside That Box

Remember how we used to write:
int playerHealth;
int playerGold;
in our main method? Instead of having these floating around separately, we can put them inside our container:
public class Player {
    // These belong to the Player now
    int health;
    int gold;
}
This is like saying "every Player has their own health and gold." When we create a Player (using new Player()), Java sets aside space for both variables. Theyโ€™ll start at 0 by default, which isnโ€™t greatโ€”we usually want to pick starting values like (10, 50). Weโ€™ll fix that next, but first appreciate what weโ€™ve done: weโ€™ve moved from separate, floating variables to a single container that keeps related data together.

Subsection 3.4.4 Step 3: Setting Initial Values

Remember how our variables defaulted to 0? Thatโ€™s not great for a gameโ€”we want to choose starting values. If this was a method, weโ€™d write something like:
public static Player createPlayer(int startHealth, int startGold) {
    // ... somehow make a Player with these values ...
}
Since creating objects with initial values is something we do constantly, Java gives us special syntax for itโ€”a shorter, cleaner way to write this common task:
public class Player {
    int health;
    int gold;

    // Like createPlayer, but special syntax!
    // Constructor syntax:
    // 1. Same name as the class
    // 2. No return type (not even void)
    // 3. public ClassName(parameters) { ... }
    public Player(int startHealth, int startGold) {
        health = startHealth;  // set this Player's health
        gold   = startGold;    // set this Player's gold
    }
}
Now instead of calling createPlayer(10, 50), we write:
Player p = new Player(10, 50);
What happens when you write new Player(10, 50)?
  1. new tells Java to create space in memory
  2. Java fills that space with default values (0 for numbers)
  3. The constructor runs to set up the object properly
  4. You get back a reference to your new Player
This special method is called a constructor. You might wonder: "Doesnโ€™t it return a Player?" It does, but Javaโ€™s grammar treats constructors speciallyโ€”they implicitly return the new object without needing a return type. Thatโ€™s why:
Youโ€™ll see this pattern everywhere in Java:
String name = new String("Alice");      // Make new String with "Alice"
Scanner input = new Scanner(System.in); // Make new Scanner reading System.in
The constructor tells Java "when someone asks for a new Player, hereโ€™s how to set it up." Much better than default 0s!

Subsection 3.4.5 Step 4: Adding "Operations" for That Player

Remember how we wrote methods before? Each had a return type (like int or void) and parameters in parentheses. Weโ€™ll do the same here, but inside our class:
Why not just use static methods? Letโ€™s compare approaches:
////////////////////////////////////////////////
// Static approach - explicitly pass everything
////////////////////////////////////////////////
public static void healPlayer(int currentHealth, int healAmount) {
    currentHealth += healAmount;
}

////////////////////////////////////////////////
// Instance method approach - data is inside
////////////////////////////////////////////////
public void heal(int amount) {
    this.health += amount;  // 'this' refers to current Player
}
The instance method might look simpler, but whatโ€™s really happening? When you write:
player1.heal(5);
Java secretly translates this to something like โ€œcall heal with the data belonging to player1.โ€ Thatโ€™s what this means in an instance methodโ€”itโ€™s a reference to "the object weโ€™re calling this method on." So:
public void heal(int amount) {
    this.health += amount;  // explicitly say "this object's health"
}
is the same as:
public void heal(int amount) {
    health += amount; // 'this.' is implicit
}
The instance method approach is:
Much better than juggling separate variables and explicit parameters!
public class Player {
    int health;
    int gold;

    // ...constructor as before...
    public Player(int startHealth, int startGold) {
        health = startHealth;
        gold   = startGold;
    }

    // The method is right next to the data it changes
    public void heal(int amount) {
        health += amount;  // directly use this Player's health
    }

    public void addGold(int amount) {
        gold += amount;    // same for gold
    }
}
// Create an object using the Player class:
Player p = new Player(10, 50);
p.heal(5);  // can't mix up which health we're changing!

Subsubsection 3.4.5.1 The โ€™thisโ€™ Keyword: Who Am I?

When writing instance methods, you might wonder: "How does Java know which Playerโ€™s health to change?" The answer is the this keyword.
public class Player {
    int health;
    // ...other fields...

    public void heal(int health) {  // Uh oh, parameter named same as field!
        health = health;    // Which health is which? ๐Ÿ˜•
    }

    // Better: use 'this' to be clear
    public void heal(int health) {
        this.health = health;  // "THIS object's health = parameter health"
    }
}
When you call player1.heal(5), Java secretly passes player1 to the method as this. You can think of it like:
// What you write:
player1.heal(5);

// What Java effectively does:
heal(player1, 5); // player1 becomes 'this' inside the method
The this keyword is especially useful when:
Usually you can skip writing this. (itโ€™s implicit), but itโ€™s there if you need it!

Subsection 3.4.6 Using Our New Type

Letโ€™s add a main method right to our Player class to try it out:
Look how natural this is:
  1. new Player(10, 50) creates a fresh Player with starting values
  2. p1.heal(5) tells that specific Player to heal itself
  3. Each Player keeps track of its own stats
Now that weโ€™ve seen it in action, letโ€™s give these ideas their proper names:
  • A Player we create (like p1 or p2) is called an object or instance.
  • The methods inside Player (like heal) are called instance methods because they work on a specific instance.
  • The setup code (like Player(10, 50)) is called a constructor.
Think of it like this: You wrote a recipe (Player class), and now you can bake as many cookies (Player objects) as you want. Each cookie has its own ingredients (health, gold), but they all follow the same recipe!

Subsection 3.4.7 One Blueprint, Many Objects

Think about how games like Minecraft work:
// Define the blueprint once
public class Player {
    int health;
    int gold;
    // ...constructor and methods as before...
}

// Create many different players from it
Player steve = new Player(20, 0);    // Steve starts with 20 health, 0 gold
Player alex  = new Player(20, 100);  // Alex starts with 20 health, 100 gold
When steve.heal(5) runs, it only changes Steveโ€™s health. Alexโ€™s stats stay the same. Each object is independent, just like:
When we say Player p1 = new Player(...), we say p1 is an โ€œinstanceโ€ of that class. Thatโ€™s just fancy talk for โ€œa house built from the blueprint.โ€ No big mystery!
The class is just the instructions (โ€œPlayers have health and goldโ€). The objects are the actual things we create using those instructions. Thatโ€™s why we say โ€œclass is the blueprint, objects are the houses.โ€

Subsection 3.4.8 Putting It All Together

Letโ€™s see how all the pieces work together. Hereโ€™s our complete Player class:
The magic is in how it all fits together:
  1. Create objects using the blueprint (new Player(...))
  2. Each object gets its own variables (health, gold)
  3. Methods work on the object you call them on (hero.heal(5) vs enemy.heal(10) )
No more โ€œwhich health am I changing?โ€ Everything belongs to a specific Player!
One Last Thing: Notice how anyone can write p1.health = -999;? Thatโ€™s not greatโ€”players shouldnโ€™t have negative health! If our game allows negative health, that could cause weird glitches like invincible players or healing that kills you. Weโ€™ll fix this by making variables private, letting only the Player class control how they change:
public class Player {
    private int health;  // Only Player methods can touch this now
    private int gold;    // This too!

    // Methods control how these values change
    public void heal(int amount) {
        if (amount > 0) {
            health += amount;  // Only allow positive healing
        }
    }
}

Subsection 3.4.9 Exercises: Classes and Objects

Checkpoint 3.4.1. Multiple Choice: Why Use Classes?

In this section, we introduced the idea of creating a Player class to hold related stats (health, gold, etc.). Which statement best captures why this is beneficial for handling multiple players in a game?
  • Because Java wonโ€™t compile if you declare more than three int variables outside a class.
  • Thatโ€™s not true; Java doesnโ€™t limit the number of variables outside a class.
  • Each Player object bundles its own stats, reducing confusion and preventing you from mixing up player1Health and player2Health.
  • Exactly! Grouping related data (and methods) in a class makes it far less error-prone than many separate variables.
  • Because static methods can only accept one parameter, so we must use a class to handle multiple stats.
  • Static methods can accept multiple parameters; thatโ€™s not the main reason classes are helpful.
  • Classes automatically remove the need to type public or static in your code.
  • Classes donโ€™t remove public or static usage; you still control that in the class definition.

Checkpoint 3.4.2. True/False: Separate Data in Each Object.

    If you create two Player objects, p1 and p2, each has its own health and gold fields. Calling p1.heal(5) affects only p1, and does not affect p2.
  • True.

  • Each instance has its own copy of the fields, so operations on p1 do not change p2.
  • False.

  • Each instance has its own copy of the fields, so operations on p1 do not change p2.

Checkpoint 3.4.3. Parsons: A Minimal Player Class.

Rearrange the following lines to form a complete, minimal Player class that compiles and runs. It should have:
  • A health field
  • A constructor that sets health
  • A method heal(int amount) that adds to health
  • A main method creating a Player, calling heal, and printing health
Only one arrangement will compile and run correctly.

Checkpoint 3.4.4. Short Answer: What Is the this Keyword?

In the code example, this appears inside heal to refer to the current objectโ€™s fields (e.g., this.health). In 1-2 sentences, explain what this refers to when an instance method is called (e.g., p1.heal(5)).
Solution.
Sample Solution: When we call p1.heal(5), this inside the heal method refers to p1. In other words, this is the specific Player object on which the method was invoked.

Checkpoint 3.4.5. Reflection: Why No Return Type for a Constructor?

Java constructors look like methods but donโ€™t have a return type. In 2-3 sentences, explain why constructors lack a void or Player return type. Hint: think about how new Player(...) already returns a fully constructed object without needing an explicit return statement.
Solution.
Sample Solution: A constructorโ€™s job is to initialize a new object. The new keyword allocates memory and then calls the constructor to set fields. By definition, constructors implicitly return the new object referenceโ€”so Java syntax doesnโ€™t require (or allow) a named return type.
You have attempted of activities on this page.