Class Creation

In Unit 1 you used objects built by other people. Here you'll build your own — designing classes with state (instance variables), behavior (methods), and controlled access (public vs. private).

3.1 Anatomy of a Class

A class is a blueprint. It declares two things:

  • Instance variables (a.k.a. fields, attributes) — the data each object holds
  • Methods — the actions each object can perform

The skeleton

public class Dog {
    // instance variables — usually private
    private String name;
    private int age;

    // constructor — runs when you say `new Dog(...)`
    public Dog(String n, int a) {
        name = n;
        age = a;
    }

    // methods — usually public
    public String getName() { return name; }
}

public vs private

ModifierWho can use itTypical for
publicAny code, anywhereConstructors, methods
privateOnly code inside the same classInstance variables
📘 Encapsulation

The principle of hiding data (private) and exposing it only through methods (public) is called encapsulation. It lets you change a class's insides later without breaking the code that uses it.

✏️ Your turn — your first class

Write a class called Dog with two private instance variables: a String name and an int age. Add a public constructor Dog(String n, int a) that assigns them. Add a public method describe() that returns a String like "Rex is 4 years old".

In main, create a Dog and print its description.

Show solution
public class Main {
    public static void main(String[] args) {
        Dog d = new Dog("Rex", 4);
        System.out.println(d.describe());
    }
}

class Dog {
    private String name;
    private int age;

    public Dog(String n, int a) {
        name = n;
        age = a;
    }

    public String describe() {
        return name + " is " + age + " years old";
    }
}

3.2 Constructors

A constructor is a special method that initializes a new object. Rules:

  • Same name as the class (case-sensitive)
  • No return type — not even void
  • Called automatically when you write new ClassName(...)

The default constructor

If you write a class with no constructor at all, Java gives you a free one with no arguments. As soon as you write your own, you lose that freebie.

🚨 Common compile error

If your class has only Dog(String, int) and someone writes new Dog(), you get a compile error. Add a no-arg constructor explicitly if you need both.

Overloading — multiple constructors

A class can have several constructors as long as their parameter lists differ.

✏️ Your turn — two constructors

Write a class Point with private int x and int y. Provide two constructors:

  1. Point() — sets both to 0 (the origin)
  2. Point(int x, int y) — sets them to the given values

Add a method show() that returns "(x, y)". In main, create one of each and print both.

Expected: (0, 0) and (3, 7)

Show solution
class Point {
    private int x;
    private int y;

    public Point() {
        x = 0;
        y = 0;
    }

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public String show() {
        return "(" + x + ", " + y + ")";
    }
}
📘 Sneak peek: this

When a parameter has the same name as a field, you use this.x = x to mean "the object's x equals the parameter x." More on this in lesson 3.7.

3.3 Accessor Methods (Getters)

If your fields are private, outside code can't read them directly. An accessor (a.k.a. getter) is a public method that returns a field's value.

The naming convention

  • getName() for a field name
  • getAge() for a field age
  • isReady() for a boolean field ready

toString() — the universal accessor

If you override toString(), Java will automatically call it when you print the object or concatenate it with a String.

public String toString() {
    return name + " (" + age + ")";
}

// Now this works:
System.out.println(dog);   // calls dog.toString()
✏️ Your turn — getters and toString

Extend the Dog class with three accessors:

  1. getName() returning the String name
  2. getAge() returning the int age
  3. toString() returning "Rex, age 4" (the name, comma, then "age" and the number)

In main, print each getter individually, then print the whole dog directly (which triggers toString).

Show solution
class Dog {
    private String name;
    private int age;

    public Dog(String n, int a) {
        name = n;
        age = a;
    }

    public String getName() { return name; }
    public int    getAge()  { return age; }

    public String toString() {
        return name + ", age " + age;
    }
}
⚠ Don't expose mutable objects directly

If a field is an array or ArrayList, returning it directly lets callers modify your private state. For AP CSA, returning it is fine, but be aware: in production code, you'd return a copy.

3.4 Mutator Methods (Setters)

A mutator (a.k.a. setter) is a public void method that changes a private field. Setters can also validate the new value.

public void setAge(int a) {
    if (a >= 0) {        // validation
        age = a;
    }
}
💡 Why bother with setters?

If age were public, anyone could write dog.age = -5. With a private field and a validating setter, you control what's legal.

Mutators can do work, not just assign

A mutator might compute, increment, or trigger side effects:

public void haveBirthday() {
    age++;
}
✏️ Your turn — setters with validation

Build a BankAccount class with a private double balance. Provide:

  1. A constructor BankAccount(double initial) that sets the starting balance
  2. getBalance() — returns the balance
  3. deposit(double amount) — adds only if amount > 0; otherwise does nothing
  4. withdraw(double amount) — subtracts only if amount > 0 AND amount <= balance

Try the test code below — it deposits 50, withdraws 30, then tries to withdraw 9999 (should be ignored). Final balance: 120.0.

Show solution
class BankAccount {
    private double balance;

    public BankAccount(double initial) {
        balance = initial;
    }

    public double getBalance() {
        return balance;
    }

    public void deposit(double amount) {
        if (amount > 0) balance += amount;
    }

    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
        }
    }
}

3.5 Static Variables & Methods

Static members belong to the class, not to any individual object. There's exactly one copy, shared by everyone.

Instance (no static)Static
Belongs toEach objectThe class itself
Called withobj.method()ClassName.method()
Can accessBoth static and instanceOnly static (no this)
Examplesdog.getName()Math.sqrt(16)

Classic use: a counter shared across all instances

public class Dog {
    private static int dogsCreated = 0;   // ONE copy for the whole class

    public Dog() {
        dogsCreated++;
    }

    public static int getDogsCreated() {
        return dogsCreated;
    }
}
🚨 Static can't see instance variables

A static method has no this — it isn't tied to any one object. Trying to use a non-static field inside a static method is a compile error.

✏️ Your turn — count instances

Build a Robot class:

  1. Private instance String name
  2. Static int count starting at 0
  3. Constructor Robot(String n) that sets name and increments count
  4. Instance method getName()
  5. Static method howMany() that returns count

Create three robots, print their names, then call Robot.howMany() — should print 3.

Show solution
class Robot {
    private String name;
    private static int count = 0;

    public Robot(String n) {
        name = n;
        count++;
    }

    public String getName() {
        return name;
    }

    public static int howMany() {
        return count;
    }
}
📘 Static main

That's why main is public static void main(String[]) — Java needs to call it before any object exists, so it can't be tied to an instance.

3.6 Scope & Access

Scope is the region of code where a variable is visible.

KindDeclared inLives for
Local variableInside a method or blockUntil the block ends
ParameterThe method's parenthesesThe duration of that call
Instance variableInside the class, outside methodsAs long as the object exists
Static variableInside the class with staticThe entire program

Inner scopes shadow outer ones

If a local variable has the same name as an instance variable, code inside that method sees the local one. The instance one is hidden (you'd need this.x to reach it).

🚨 Loop variables are local

A variable declared inside for (int i = 0; ...) only exists inside that loop. Trying to use i after the loop is a compile error unless you declared i outside.

✏️ Your turn — predict, then run

Read the program below carefully — don't run it yet. Predict what each println will print.

The class has an instance variable n = 10. The method demo() declares a parameter n and a local variable n in a nested block.

Once you've written your guesses, run it and see if you were right.

Show expected output
20    // the parameter n shadows the field
10    // this.n reaches the instance variable
99    // the local x is visible here
// uncommenting the last line would fail: x is out of scope
💡 Avoid shadowing in real code

Even though the AP exam may test it, in real code don't reuse a field's name as a local. The exception: constructors and setters where this.x = x; is idiomatic.

3.7 The this Keyword

Inside an instance method, this refers to the specific object the method was called on. It lets you:

  • Disambiguate a field from a parameter with the same name
  • Pass the current object to another method
  • Call one constructor from another: this(args)

Use 1 — distinguishing field from parameter

public Dog(String name, int age) {
    this.name = name;   // the field   ← the parameter
    this.age  = age;
}

Use 2 — passing yourself along

public void register() {
    Kennel.add(this);   // pass this Dog object to the Kennel
}

Use 3 — chaining constructors

public Point() {
    this(0, 0);         // call Point(int, int) with (0, 0)
}
public Point(int x, int y) {
    this.x = x;
    this.y = y;
}
🚨 this doesn't exist in static methods

Because static methods aren't called on an object, there's no "this" to refer to. Using this in a static method is a compile error.

✏️ Your turn — use this properly

Build a Rectangle class:

  1. Two private fields: int width, int height
  2. Constructor Rectangle(int width, int height) — parameter names match the field names, so you must use this. to assign them
  3. Method area() returning width * height
  4. Method isLargerThan(Rectangle other) returning true if this.area() > other.area()
Show solution
class Rectangle {
    private int width;
    private int height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public int area() {
        return width * height;
    }

    public boolean isLargerThan(Rectangle other) {
        return this.area() > other.area();
    }
}

🎯 Unit 3 Review Quiz

1. Why are instance variables usually declared private?
A Because private fields can't be read by anyone
B Encapsulation — the class controls access through public methods
C Performance
D They use less memory
2. Which is true about a Java constructor?
A It can never have parameters
B A class can have only one constructor
C It has no return type
D It always returns void
3. Given Dog d = new Dog("Rex", 4);, how do you read the private field name from outside the class?
A d.name
B Call a public accessor like d.getName()
C Dog.name(d)
D this.name
4. A static method cannot:
A Call another static method
B Access instance variables directly (without an object)
C Take parameters
D Return a value
5. In the constructor public Dog(String name) { this.name = name; }, what does this.name refer to?
A The instance variable of the object being constructed
B The parameter
C A static field
D Compile error