Classes & Objects
A class is a blueprint describing fields and methods; an object is a concrete instance of that blueprint, created with new and manipulated through references.
Classes and objects are the foundation of object-oriented programming in Java. A class defines a template — what data (fields) and behavior (methods) instances of that type will have — while an object is an actual instance of that class, created at runtime and holding its own independent copy of instance state. Everything else in Java's object model (inheritance, polymorphism, encapsulation) builds directly on this class/object relationship.
What a Class Is
A class is a blueprint: it declares the fields (data) every instance will carry and the methods (behavior) every instance can perform, but the class declaration itself doesn't allocate any object — it just describes what one would look like.
class Dog {
String name; // field — each Dog instance gets its own name
int age; // field — each Dog instance gets its own age
void bark() { // method — behavior shared by all Dog instances
System.out.println(name + " says Woof!");
}
}What an Object Is
An object is a concrete instance of a class, created at runtime with the new keyword, which allocates memory for the object and runs a constructor to initialize it. Each object has its own independent set of instance field values, even though all objects of the same class share the same method definitions.
Dog d1 = new Dog();
d1.name = "Rex";
d1.age = 3;
Dog d2 = new Dog();
d2.name = "Fido";
d2.age = 5;
d1.bark(); // "Rex says Woof!"
d2.bark(); // "Fido says Woof!"
System.out.println(d1.age); // 3 — independent of d2's ageFields (Instance State)
Fields declared directly in a class (without static) are instance fields — every object created from that class gets its own separate copy. Instance fields default to a type-appropriate value (0, false, null, etc.) if not explicitly initialized, the same defaulting rules that apply to array elements.
class Counter {
int value; // defaults to 0 if never explicitly set
}
Counter c = new Counter();
System.out.println(c.value); // 0 — default value, not initialized explicitlyMethods (Behavior)
Methods define what objects of a class can do. Instance methods operate on a particular object's state (implicitly referring to that object via this), while static methods belong to the class itself and don't operate on any particular instance's fields.
class Rectangle {
double width, height;
double area() { // instance method — uses this object's width/height
return width * height;
}
static Rectangle unitSquare() { // static method — a factory, not tied to any instance
Rectangle r = new Rectangle();
r.width = 1;
r.height = 1;
return r;
}
}Object Creation With new
The new keyword, followed by a constructor call, does three things: allocates memory for a new object on the heap, runs the appropriate constructor to initialize its fields, and evaluates to a reference to that freshly created object, which you typically store in a variable.
Constructors are covered in their own dedicated topic — here the focus is simply that new triggers object creation and initialization.
class Point {
int x, y;
Point(int x, int y) { // constructor — initializes the new object's fields
this.x = x;
this.y = y;
}
}
Point p = new Point(3, 4); // allocate, initialize, and get a reference back
System.out.println(p.x + ", " + p.y); // 3, 4Object Identity and References
A variable of a class type holds a reference to an object, not the object's data directly. Two different variables can reference the same object (aliasing), or two objects with identical field values can still be entirely distinct objects — this is the core distinction between reference equality (==) and value/content equality (a properly implemented .equals()).
Object identity vs. equality, and equals()/hashCode() overriding, get their own dedicated deep treatment in a later topic.
Point p1 = new Point(1, 1);
Point p2 = new Point(1, 1); // a SEPARATE object with the same field values
Point p3 = p1; // p3 references the SAME object as p1
System.out.println(p1 == p2); // false — two distinct objects, even with equal content
System.out.println(p1 == p3); // true — same object, just two variable names for it
p3.x = 99;
System.out.println(p1.x); // 99 — p1 and p3 alias the same object, so mutation is sharedInstance State Is Independent Per Object
This bears repeating because it's the whole point of instance fields: every object created from a class carries its own independent copy of every instance field. Changing one object's fields never affects another object of the same class, unless the two variables happen to reference the exact same object (aliasing, as shown above).
class BankAccount {
double balance;
}
BankAccount alice = new BankAccount();
BankAccount bob = new BankAccount();
alice.balance = 100;
bob.balance = 50;
System.out.println(alice.balance); // 100 — unaffected by bob's balance
System.out.println(bob.balance); // 50Class vs. Object — the Relationship
Think of a class the way you'd think of a blueprint for a house, and objects as the actual houses built from that blueprint — one blueprint, potentially many houses, each with its own address, paint color, and furniture (instance state), even though they were all built from the exact same design (the class's fields and methods).
Class (blueprint) Object (instance)
-------------------------------- --------------------------------
Declared once in source code Created at runtime with new
Defines fields and methods Has actual field VALUES
One class definition Potentially many objects
Compiled to one .class file Each occupies its own heap memoryPractical Usage
Classes model real-world or domain concepts — a Customer, an Order, a Connection — bundling relevant data and the operations that make sense on that data together. Well-designed classes keep related state and behavior together and hide internal details behind a clean method-based interface (encapsulation, covered separately).
class Order {
private final String id;
private double total;
Order(String id) {
this.id = id;
}
void addItem(double price) {
total += price;
}
double getTotal() {
return total;
}
}Edge Cases
A class variable that has never been assigned an object holds null, and attempting to access a field or call a method on it throws NullPointerException. Java has no way to "declare an object without a class" — every object is an instance of exactly one class, and every value that isn't a primitive is either a reference to an object or null.
Dog d = null;
try {
d.bark(); // NullPointerException — no object exists to call bark() on
} catch (NullPointerException e) {
System.out.println("caught: d was never assigned an object");
}Best Practices
Keep classes focused on a single clear responsibility. Prefer private fields with controlled access through methods over exposing raw public fields (encapsulation, covered in its own topic). Give classes meaningful, noun-based names (Customer, not Data or Manager for everything) and keep constructors focused on establishing a valid initial state.
Complete Example
public class ClassObjectDemo {
static class Book {
String title;
String author;
int pages;
Book(String title, String author, int pages) {
this.title = title;
this.author = author;
this.pages = pages;
}
void describe() {
System.out.println(title + " by " + author + " (" + pages + " pages)");
}
}
public static void main(String[] args) {
Book b1 = new Book("Effective Java", "Joshua Bloch", 412);
Book b2 = new Book("Clean Code", "Robert Martin", 464);
b1.describe(); // Effective Java by Joshua Bloch (412 pages)
b2.describe(); // Clean Code by Robert Martin (464 pages)
System.out.println(b1 == b2); // false — distinct objects
System.out.println(b1.pages < b2.pages); // true — independent instance state
}
}Mental Model
A class is a recipe; an object is a dish made from that recipe. You can make as many dishes as you want from one recipe, each dish exists independently once served (change the salt in one bowl and it doesn't affect the others), but every dish made from the same recipe follows the same preparation steps (the class's methods) and has slots for the same kinds of ingredients (the class's fields).
Key Takeaways
A class is a blueprint declaring fields and methods; it defines no actual object by itself.
An object is a runtime instance of a class, created with new, holding its own independent copy of instance field values.
Object variables hold references, not the object's data directly — two variables can alias the same object, or two objects can have equal content while being entirely distinct (== vs .equals()).
Every object created from a class carries independent instance state; mutating one object never affects another unless they're aliases of the same object.
Calling a method or accessing a field on a null reference throws NullPointerException, since there's no actual object backing that reference.
Dog d; d.bark(); // never assigned an object — NullPointerException
Dog d = new Dog(); d.bark(); // object created first A declared but unassigned reference variable is null by default; you must create an object with new before calling methods or accessing fields on it.
Point p1 = new Point(1, 1); Point p2 = new Point(1, 1); if (p1 == p2) { /* expecting equal content to match */ } if (p1.x == p2.x && p1.y == p2.y) { /* or implement/override equals() for content comparison */ }
== on object references checks identity (are these the SAME object?), not content — two separately created objects with identical field values are never == to each other.
Dog original = new Dog(); original.name = "Rex"; Dog copy = original; // intending an independent copy copy.name = "Fido"; // unexpectedly also renames original
Dog copy = new Dog(); copy.name = original.name; // manually copy field values into a NEW object
Assigning one object reference to another variable aliases the same object rather than copying it — mutating through either variable name affects the one shared object. Create a genuinely new object and copy field values if independence is needed.
What is the fundamental difference between a class and an object?
A class is a blueprint or template that declares what fields and methods instances of that type will have, but it doesn't itself represent any actual data. An object is a concrete instance of a class, created at runtime with new, holding its own actual field values and able to invoke the class's methods.
Why does creating two Dog objects with new Dog() and setting different name values on each not cause them to interfere with each other?
Because each object created from a class has its own independent copy of every instance field — instance state is per-object, not shared across all instances of the class. Setting a field on one object only affects that object's own copy of the field, never another object's copy, unless the two variables happen to reference the exact same object.
What does a class-typed variable actually hold — the object's data, or something else?
It holds a reference to the object, not the object's data directly. The actual field values live in the object itself, allocated on the heap; the variable is just a pointer-like handle used to access that object.
If p1 and p2 are two separately created objects with identical field values, is p1 == p2 true or false, and why?
It is false. == on reference types compares object identity — whether both references point to the exact same object in memory — not the equality of their field values. Two distinct objects with identical content are still two different objects, so == returns false unless you explicitly override and use .equals() for content comparison.
What happens when you call a method on a reference variable that is currently null?
It throws NullPointerException, because a null reference doesn't point to any actual object — there is no instance to invoke the method on or to hold the fields the method might try to access.
What three things does the new keyword do when creating an object?
It allocates memory for a new object (on the heap, in a typical JVM implementation), runs the appropriate constructor to initialize the object's fields, and evaluates to a reference to that newly created object, typically then stored in a variable.
If Dog b = a; is executed where a already references a Dog object, and then a field is changed through b, is that change visible through a? Why?
Yes — the change is visible through a as well, because b = a copies only the reference, making a and b two variable names that both point to the SAME underlying object (aliasing). Mutating the object through either variable name affects the one object both variables share, not two separate objects.
Write a Rectangle class with width and height fields, a constructor that sets both, and an area() method, then create two instances and print their areas.
public class RectangleDemo { static class Rectangle { double width; double height; Rectangle(double width, double height) { this.width = width; this.height = height; } double area() { return width * height; } } public static void main(String[] args) { Rectangle r1 = new Rectangle(3, 4); Rectangle r2 = new Rectangle(5, 5); System.out.println(r1.area()); // 12.0 System.out.println(r2.area()); // 25.0 } }
Each Rectangle instance stores its own width and height, and area() reads this object's fields (via the implicit this), so r1.area() computes 3*4=12.0 and r2.area() independently computes 5*5=25.0, demonstrating that instance state and the results derived from it are per-object.
What does this print, and why doesn't changing c1.value at the end affect c2.value?
It prints "99 10". c1 and c2 are two entirely separate Counter objects. The line c2.value = c1.value; copies the primitive int VALUE (10) from c1's field into c2's own field — it does not make c2 an alias of c1. So subsequently reassigning c1.value = 99 only changes c1's own field, leaving c2's independently-stored copy at 10.
What does this print, and why does modifying b.contents also change what a.contents reports?
It prints 42, then true. Box b = a; copies only the object reference, not the object's data — so a and b become two variable names referring to the exact same Box object (aliasing). Setting b.contents = 42 mutates that one shared object, so reading it back through a shows the same updated value, and a == b is true because both variables hold identical references to the same object.
class Counter {
int value;
}
Counter c1 = new Counter();
Counter c2 = new Counter();
c1.value = 10;
c2.value = c1.value;
c1.value = 99;
System.out.println(c1.value + " " + c2.value);class Box {
int contents;
}
Box a = new Box();
a.contents = 5;
Box b = a;
b.contents = 42;
System.out.println(a.contents);
System.out.println(a == b);