Topic 4.1
Classes and Objects
In one line
A class is a blueprint that describes what data a kind of thing holds and what it can do. An object is one real thing built from that blueprint with new, living on the heap, reached through a reference.
Think of it like this
A cake recipe and the cakes. The recipe says every cake has a flavour, a size and candles, and that you can slice it. The recipe is not a cake: you can't eat it. Each time you bake, you get a new cake with its own flavour and its own candles. Eat a slice of one cake and the other cakes are untouched. In Java the recipe is the class, and each cake is an object.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Class
- A blueprint you write in code that describes a kind of thing: what data it holds and what it can do. It's also a new type you can use for variables.
- Object
- One real thing made from a class while the program runs. It lives in memory and has its own values for each field.
- Instance
- Another word for object. "An instance of Dog" means "one Dog object".
- Field
- A variable declared inside a class but outside any method. Each object gets its own copy (unless the field is
static, Topic 4.6). - `new`
- The keyword that asks the JVM to create a fresh object on the heap and run its constructor.
- Reference
- The value stored in a variable of class type. It tells the JVM where the object is, like a house address written on a card.
- Heap
- The big area of memory where all objects live. It's shared by the whole program and cleaned up by the garbage collector.
- Dot operator (`.`)
- How you reach inside an object:
rex.namereads a field,rex.bark()calls a method on that object.
Step by step
01Write a class: the blueprint
A class has a name (by convention a noun in UpperCamelCase), and inside its braces you list fields and methods. Here Dog says: every dog has a name and an age, and every dog can bark().
Nothing runs yet. A class on its own is like a recipe card in a drawer. The compiler checks it and turns it into a Dog.class file of bytecode, but no dog exists until some code says new Dog().
Notice main lives in Main, and Dog sits in the same file without public. Java allows only one public top-level class per file, and its name must match the file name. Other top-level classes in the file are package-private (Topic 4.5).
public class Main {
public static void main(String[] args) {
// no Dog exists yet
}
}
class Dog {
String name; // field: each dog has its own name
int age; // field: each dog has its own age
void bark() { // method: something every dog can do
System.out.println(name + " says woof");
}
}02Create objects with new
new Dog() does four things in order: it makes sure the Dog class is loaded, allocates enough heap memory for one dog's fields, sets every field to its default (null and 0), and runs the constructor. Then it returns a reference to the new object.
Dog rex = new Dog(); stores that reference in the local variable rex. The variable lives in main's stack frame (Topic 3.11); the object lives on the heap.
You then use the dot operator to set fields and call methods. rex.bark() means "run the bark code, with rex's object as the dog doing the barking".
Dog rex = new Dog();
rex.name = "Rex";
rex.age = 3;
Dog bella = new Dog();
bella.name = "Bella";
bella.age = 5;
rex.bark(); // Rex says woof
bella.bark(); // Bella says woof03Picture the memory: stack holds references, heap holds objects
Two new calls means two separate objects on the heap, each with its own name and age. The variables rex and bella on the stack are small: each holds only a reference (typically 4 or 8 bytes).
The name field itself is a reference too, pointing to a String object elsewhere on the heap. Objects very often point to other objects; that web of references is your program's data.
04Copying a variable copies the reference, not the object
Dog alias = rex; does not create a dog. It copies the reference stored in rex into alias. Now two variables point to one object.
Change the name through alias, and reading through rex shows the change, because there's only one dog. This is the same rule you met in Topic 3.3 (Java is always pass-by-value): the value being copied is the reference.
== compares references. rex == alias is true (same object). A brand-new dog with the same name and age is a different object, so == says false.
Dog alias = rex; // no new object
alias.name = "Rexy";
System.out.println(rex.name); // Rexy
Dog twin = new Dog();
twin.name = "Rexy";
twin.age = 3;
System.out.println(rex == alias); // true - same object
System.out.println(rex == twin); // false - different objects05Default values for fields, but not for locals
When new allocates an object, the JVM zeroes the memory first. That's why an unset int field reads 0, a boolean reads false and a reference field reads null. Zeroing happens before any of your code runs, so a field can never contain leftover garbage.
Local variables are different. They live in the stack frame and the compiler's definite assignment rules (JLS chapter 16) require you to assign them before reading. Dog d; d.bark(); is a compile error: variable d might not have been initialized.
This difference is deliberate: fields can be read from many methods in any order, so the JVM guarantees a safe starting value. Locals are visible in one method, so the compiler can prove you set them, and an unset local is almost always a bug.
06What the compiler and JVM actually do for new
new Dog() compiles to three bytecode instructions: new Dog (allocate and zero the memory), dup (keep a copy of the reference on the operand stack), and invokespecial Dog.<init>()V (run the constructor on it). You can see them with javap -c Main.
On HotSpot, allocation is usually very cheap: each thread has a private chunk of the heap called a TLAB (thread-local allocation buffer), and allocating is mostly just bumping a pointer. Creating many small, short-lived objects is normal Java style, not a performance sin.
Every object also carries a small header before its fields: a mark word (used for locking, hashing and the garbage collector) and a pointer to its class. That class pointer is how rex.getClass() and method dispatch know what kind of object it is.
Try it yourself
- 1
Add a third dog
In "Two dogs, one class", create
Dog maxwith age 1. Before running, predict the full output, then run and check. Callmax.birthday()twice and print his age. - 2
Predict the alias
In "References: alias vs a new object", add
twin = rex;before the==lines. Predict both==results, then run. Explain why the second line changed. - 3
Look at the bytecode
Compile the first example locally and run
javap -c Main. Find thenew,dupandinvokespecialinstructions for each dog.terminal$ javac Main.javajavap -c Main── expected output ──...0: new #7 // class Dog3: dup4: invokespecial #9 // Method Dog."<init>":()V7: astore_1...
Code & diagrams
Each object has its own fields: Rex's birthday doesn't change Bella's age.
Expected output
Rex says woof
Bella says woof
Rex is now 4
Bella is still 5Expected output
rex.name = Rexy
rex == alias: true
rex == twin: false
rex still there: RexyExpected output
int: 0
double: 0.0
boolean: false
char is zero: true
String: null
array: nullThe compiler added a constructor Dog() you never wrote: the default constructor (Topic 4.3).
> javac Main.java
> javap Dog
Compiled from "Main.java"
class Dog {
java.lang.String name;
int age;
Dog();
void bark();
void birthday();
}Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Forget the new keyword
Write Dog d = Dog(); instead of Dog d = new Dog();.
Break #2
Call a method through a null reference
Write Dog rex = null; and then rex.bark();.
Myth vs fact
Myth
A variable of type Dog contains the dog.
Fact
It contains a reference to a Dog object on the heap. Assigning it to another variable copies the reference, so both point to the same object.
Myth
Each object has its own copy of the methods.
Fact
Method code exists once per class (in the JVM's metaspace). Each object only stores its fields plus a small header; methods find the right object through this.
Myth
Creating objects is slow, so avoid new.
Fact
On HotSpot, allocation in a thread-local buffer is a pointer bump, and short-lived objects are cheap to collect. Write clear code first; measure before avoiding allocation.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Object size on 64-bit HotSpot with compressed class pointers (the default): a 12-byte header (8-byte mark word + 4-byte class pointer), then fields, rounded up to a multiple of 8 bytes. A
Dogwith one reference and oneintis 12 + 4 + 4 = 20, padded to 24 bytes. JDK 24 added experimental compact object headers (8 bytes), made a product option in JDK 25. Measure real layouts with the JOL tool (org.openjdk.jol). - ▸
The JVM may not even allocate on the heap: after JIT compilation, escape analysis can prove an object never leaves a method and replace it with plain local values (scalar replacement). This is why micro-benchmarks of
neware misleading without JMH. - ▸
In single-file source mode (
java Main.java, Java 11 to 21), the launcher runsmainin the first top-level class in the file. IfDogcomes first you geterror: can't find main(String[]) method in class: Dog. PutMainfirst. (Java 22+ picks the class matching the file name.) - ▸
Classes are loaded lazily:
Dog.classis read, verified and initialised the first time it's actually needed (for example the firstnew Dog()), not when the program starts. Topic 0.8 and Phase 13 go deeper into class loading.
Remember this
- 1
A class declares a new type. It lists fields (the data each object holds, such as
nameandage) and methods (the actions each object can perform, such asbark()). Writing a class creates no objects at all; it only teaches the compiler a new shape. - 2
An object (also called an instance) is created at run time with the
newkeyword, for examplenew Dog(). The JVM finds free memory on the heap, sets every field to its default value, runs the constructor, and hands back a reference: the address-like value that lets you reach the object. - 3
A variable of a class type, such as
Dog rex, never holds the object itself. It holds a reference to it. That's whyDog b = rex;copies the reference, not the dog: both variables now point to the same single object, and a change made through one is visible through the other. - 4
Each object has its own copy of every (non-static) field. Two
Dogobjects can have different names and ages at the same time. Methods, on the other hand, exist once per class; every object shares the same method code and the method knows which object it's working on through a hidden reference calledthis(Topic 4.4). - 5
Fields that you don't set get default values:
0for numbers,falseforboolean,'\u0000'forchar, andnullfor references. Local variables inside methods get no default, which is why the compiler insists you assign them first. - 6
==on two references asks "are these the same object?", not "do they hold the same data?". Two separatenew Dog()objects with identical fields are still different objects. Comparing by content is the job ofequals, which you'll write yourself in Topic 4.8.
Explain it without notes
What is the difference between a class and an object? Use an analogy and then the precise Java terms.
What exactly happens, step by step, when the JVM executes Dog d = new Dog();?
Dog a = new Dog(); Dog b = a; b.name = "Max"; What is a.name and why?
Why do fields get default values while local variables must be assigned before use?
Practice
Write a class Book with fields title (String) and pages (int) and a method describe() that prints <title> has <pages> pages. Create two books and call describe() on both.
Write a class Counter with an int value field and a method increment(). Create two counters, increment the first three times and the second once, and print both values.
Create one Book, store it in two variables x and y, change pages through y, and print x.pages and x == y.
Trade-offs
- ↔
Modelling everything as small objects makes code readable and matches the problem, at the cost of some memory per object (header + padding). For millions of tiny values in a hot loop, a primitive array can be far smaller; choose objects first and optimise only where a profiler points.
- ↔
Public fields set from outside (as in this topic's first examples) are quick to write but let any code put an object into a nonsense state, like a negative age. Topics 4.3 and 4.5 fix that with constructors and
privatefields.
Done when you can
Done when you can explain class vs object with an analogy and with heap/stack terms.
Done when you can write a class with fields and methods and create several independent objects from it.
Done when you can predict the result of copying a reference and of
==between two objects.Done when you can list the default value of each field type and explain why locals have none.
Done when you can describe what
newdoes in the JVM (allocate, zero, construct, return reference).