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
| Modifier | Who can use it | Typical for |
public | Any code, anywhere | Constructors, methods |
private | Only code inside the same class | Instance 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:
Point() — sets both to 0 (the origin)
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:
getName() returning the String name
getAge() returning the int age
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:
- A constructor
BankAccount(double initial) that sets the starting balance
getBalance() — returns the balance
deposit(double amount) — adds only if amount > 0; otherwise does nothing
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 to | Each object | The class itself |
| Called with | obj.method() | ClassName.method() |
| Can access | Both static and instance | Only static (no this) |
| Examples | dog.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:
- Private instance
String name
- Static
int count starting at 0
- Constructor
Robot(String n) that sets name and increments count
- Instance method
getName()
- 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.
| Kind | Declared in | Lives for |
| Local variable | Inside a method or block | Until the block ends |
| Parameter | The method's parentheses | The duration of that call |
| Instance variable | Inside the class, outside methods | As long as the object exists |
| Static variable | Inside the class with static | The 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:
- Two private fields:
int width, int height
- Constructor
Rectangle(int width, int height) — parameter names match the field names, so you must use this. to assign them
- Method
area() returning width * height
- 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