Lesson 88 min read

Classes & Objects

Build your own data types from scratch

Classes = Blueprints, Objects = Houses

Imagine you're an architect. You draw a blueprint for a house — it says "every house has a color, a number of rooms, and a door that can open and close." That blueprint is a class.

When you actually build a house from that blueprint, you get an object (also called an instance). You can build lots of houses from the same blueprint — one red, one blue, one with 3 rooms, one with 5. Each house is its own object, but they all follow the same structure.

Classes are the building blocks of every data structure you'll encounter, from linked lists to trees. A class bundles two things together:

  • Fields (also called instance variables) — the data each object holds (color, name, score)
  • Methods — the actions each object can do (run, jump, calculateTotal)

Your First Class

public class Main {
// Define a Dog class inside Main for this example
static class Dog {
String name;
String breed;
int age;
// Constructor — runs when you create a new Dog
Dog(String name, String breed, int age) {
this.name = name; // 'this' means "this object's" field
this.breed = breed;
this.age = age;
}
// Method — something a Dog can do
void bark() {
System.out.println(name + " says: Woof!");
}
void describe() {
System.out.println(name + " is a " + age + "-year-old " + breed + ".");
}
}
public static void main(String[] args) {
// Create objects from the blueprint
Dog rex = new Dog("Rex", "German Shepherd", 5);
Dog luna = new Dog("Luna", "Golden Retriever", 3);
rex.describe();
rex.bark();
luna.describe();
luna.bark();
}
}
Output
Rex is a 5-year-old German Shepherd.
Rex says: Woof!
Luna is a 3-year-old Golden Retriever.
Luna says: Woof!

Access Modifiers & Encapsulation

Not everything inside a class should be accessible from the outside. Imagine a vending machine — you can press buttons and insert coins (public interface), but you can't reach inside and grab the mechanism (private internals).

Java has access modifiers to control this:

  • public — anyone can access it. It's the front door.
  • private — only code inside the same class can access it. It's the locked vault.
  • protected — accessible within the class and its subclasses.
  • (no modifier) — "package-private," accessible within the same package.

The practice of hiding internal data and exposing only what's necessary is called encapsulation. You use getters and setters to control access to private fields.

Encapsulation with Getters & Setters

public class Main {
static class BankAccount {
private String owner;
private double balance;
BankAccount(String owner, double initialBalance) {
this.owner = owner;
this.balance = initialBalance;
}
// Getter — read the balance
public double getBalance() {
return balance;
}
// Getter — read the owner
public String getOwner() {
return owner;
}
// Controlled deposit — no negative deposits!
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited $" + amount);
} else {
System.out.println("Invalid amount!");
}
}
// Controlled withdrawal
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("Withdrew $" + amount);
} else {
System.out.println("Can't withdraw $" + amount);
}
}
}
public static void main(String[] args) {
BankAccount acc = new BankAccount("Alice", 100.0);
System.out.println(acc.getOwner() + "'s balance: $" + acc.getBalance());
acc.deposit(50);
acc.withdraw(30);
acc.withdraw(200); // too much!
acc.deposit(-10); // sneaky!
System.out.println("Final balance: $" + acc.getBalance());
}
}
Output
Alice's balance: $100.0
Deposited $50.0
Withdrew $30.0
Can't withdraw $200.0
Invalid amount!
Final balance: $120.0

Static vs Instance

public class Main {
static class Player {
static int totalPlayers = 0; // shared across ALL players
String name; // unique to each player
int score; // unique to each player
Player(String name) {
this.name = name;
this.score = 0;
totalPlayers++; // every new player increases the count
}
void addScore(int points) {
this.score += points;
}
// Static method — belongs to the class, not any one player
static int getTotalPlayers() {
return totalPlayers;
}
}
public static void main(String[] args) {
System.out.println("Players: " + Player.getTotalPlayers());
Player p1 = new Player("Mario");
Player p2 = new Player("Luigi");
Player p3 = new Player("Peach");
p1.addScore(100);
p2.addScore(80);
System.out.println("Players: " + Player.getTotalPlayers());
System.out.println(p1.name + ": " + p1.score);
System.out.println(p2.name + ": " + p2.score);
}
}
Output
Players: 0
Players: 3
Mario: 100
Luigi: 80
Note: Think of static as a poster on the classroom wall — there's one copy, and everyone sees the same thing. Instance variables are like each student's own notebook — personal and different for each student. Use static for things that truly belong to the class itself (like a player count), not to any individual object.

Quick check

What does the 'this' keyword refer to?
MethodsInheritance & Polymorphism