Classes and Objects in Java

Learn how classes serve as blueprints for creating objects in Java, and how instantiation with the new keyword works.

published: reading time: 13 min read author: Geek Workbench

Classes and Objects in Java

Every Java application is built on classes and objects. They are the foundation of object-oriented programming — the blueprint versus the building.

Introduction

A class defines the blueprint for creating objects — it specifies the fields (state) that each instance holds and the methods (behavior) that operate on that state. When you write new Robot("Wall-E"), the Robot class constructor is invoked and memory is allocated for a new instance with its own copy of all instance fields. Multiple objects created from the same class share the same method code but maintain independent state values.

Objects live on the heap; references to them live on the stack. A reference variable holds the memory address of an object, not the object itself. This distinction matters because multiple references can point to the same object, and if one reference modifies the object’s state, all other references see the change. Understanding this shared-mutability model is critical for avoiding subtle bugs in code that appears correct in isolation.

This guide covers the anatomy of a class, how objects are instantiated, how references and objects relate to each other in memory, and the design principles that separate good class design from problematic patterns. Understanding classes and objects is the prerequisite for all other object-oriented concepts in Java — inheritance, polymorphism, encapsulation, and abstraction all build on this foundation.

When to Use

Use a class when you need to:

  • Model real-world entities with state (fields) and behavior (methods)
  • Encapsulate related data and logic that belong together
  • Create multiple instances sharing the same structure but different state
  • Organize code into logical units that can be tested and maintained independently
// Defining a class — the blueprint
public class Robot {
    // Fields — state
    private String name;
    private int batteryLevel;

    // Constructor — initializes new instances
    public Robot(String name) {
        this.name = name;
        this.batteryLevel = 100;
    }

    // Method — behavior
    public void charge() {
        this.batteryLevel = Math.min(100, batteryLevel + 20);
    }
}

// Instantiating objects — the buildings
Robot r2d2 = new Robot("R2-D2");
Robot c3po = new Robot("C-3PO");
// Anonymous class for one-off behavior
Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("Executing task");
    }
};

When Not to Use

Avoid classes for:

  • Pure utility functions — use static methods in a utility class instead
  • Single-value data containers — consider a record in Java 16+
  • Data that only wraps primitives — use primitives directly or record
  • One-off scripts — top-level code in a main method may suffice for simple programs
// Don't do this — unnecessary class for a simple operation
class StringUtils {
    public static String capitalize(String s) {
        return s.isEmpty() ? s : Character.toUpperCase(s.charAt(0)) + s.substring(1);
    }
}
// Better: use a simple static utility class is fine, but for simple capitalize, consider if it's truly needed

Class Anatomy — Mermaid Diagram

classDiagram
    class Robot {
        -String name
        -int batteryLevel
        +Robot(String name)
        +charge() void
        +getName() String
    }
    Robot : +new Robot("R2-D2")

Failure Scenarios

1. Uninitialized Reference

Robot robot;           // Declared but not assigned
robot.charge();       // NullPointerException — robot is null

// Fix: always initialize before use
Robot robot = new Robot("Wall-E");

2. Mutable Shared State via References

List<String> list1 = new ArrayList<>();
List<String> list2 = list1;    // Both reference the SAME list
list2.add("item");             // Modifies list1 too

// Fix: create independent copies
List<String> list2 = new ArrayList<>(list1);

3. Forgetting the new Keyword

String s = String.valueOf(42);  // Factory method — correct
Robot r = Robot("Test");        // Compile error — forgot new
Robot r = new Robot("Test");     // Correct

Trade-off Table

ApproachUse CaseDrawback
Concrete classFull control over behavior and stateMore boilerplate
Abstract classShared base with partial implementationSingle inheritance limit
InterfaceMultiple contracts without implementationNo state
Record (Java 16+)Immutable data carrierCannot hold mutable state
EnumFixed set of constantsNot extendable at runtime

Code Snippets

Static Factory Method Pattern

public class Robot {
    private final String name;

    private Robot(String name) {  // Private constructor
        this.name = name;
    }

    // Static factory method instead of public constructor
    public static Robot createVacuumBot(String name) {
        return new Robot(name);
    }

    public static Robot createSecurityBot(String name) {
        Robot bot = new Robot(name);
        // Security bot specific setup
        return bot;
    }
}

// Usage
Robot vac = Robot.createVacuumBot("Roomba");
Robot sec = Robot.createSecurityBot("Guard");

Nested Class

public class Outer {
    private String outerField = "outer";

    public class Inner {
        private String innerField = "inner";
        public void accessOuter() {
            System.out.println(outerField);  // Can access outer class field
        }
    }
}

// Instantiate inner class via outer instance
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

Observability Checklist

  • Fields are private with controlled access via getters/setters
  • Constructor validates required parameters
  • Immutable classes use final fields and no setters
  • Static fields documented with their purpose
  • Thread-safe design for shared instances

Security Notes

  • Encapsulate fields — never expose internal state directly
  • Defensive copies — when returning collections from getters, return copies not references
  • Immutable objects — prefer immutability to avoid race conditions
  • Input validation — validate all constructor and setter parameters
public class User {
    private final List<String> roles;  // Mutable field

    public List<String> getRoles() {
        return List.copyOf(roles);  // Return defensive copy — prevents external modification
    }
}

Pitfalls

  1. Creating too many classes — each class should earn its place
  2. God objects — classes that do too much; split into focused units
  3. Tight coupling — classes that depend heavily on each other’s internals
  4. Mutable fields that shouldn’t be — default to final where possible
  5. Reassigning parameters — don’t modify parameter values inside methods
// Bad: modifying parameter
public void process(User user) {
    user = new User();  // This only changes local copy, not the caller's reference
}

// Good: operate on the object, don't reassign
public void process(User user) {
    user.updateStatus("active");  // Call methods on the object
}

Quick Recap

  • Class = blueprint defining state (fields) and behavior (methods)
  • Object = instance created from a class via new
  • Reference = variable holding object’s memory address
  • Encapsulation = keep fields private, expose via methods
  • Immutability = use final fields and no setters

Interview Questions

1. What is the difference between a class and an object?

Model Answer: "A class is a blueprint or template that defines the structure (fields) and behavior (methods) that objects of that type will have. An object is a concrete instance created from that blueprint, occupying its own memory with actual values for the fields.

2. Can a class have multiple constructors?

Model Answer: "Yes. Java supports constructor overloading — multiple constructors with different parameter lists. This allows flexible object creation with different initial states.

3. What is the default value of an object reference field?

Model Answer: "The default value is null. Unlike primitive types which have specific defaults (0, false, etc.), object references default to null until explicitly initialized.

4. What is the difference between `new` and factory methods?

Model Answer: "new always creates a new instance via a constructor. Factory methods (like List.of() or Path.of()) can return cached instances, subclasses, or do special processing — the caller doesn't know or care about the concrete type.

5. When should you use a record instead of a class?

Model Answer: "Use a record for immutable data carriers where the main purpose is to hold values (like DTOs, transfer objects, results). Records automatically get equals(), hashCode(), toString(), and constructor parameter accessors — reducing boilerplate significantly.

6. What happens when you try to instantiate an interface directly in Java?

Model Answer: "Compilation error — interfaces cannot be instantiated with new. Interfaces are abstract contracts and require a concrete implementation class. You must create a class that implements the interface and instantiate that instead.

7. Can a class have both instance methods and static methods? Explain the difference.

Model Answer: "Yes — instance methods require an object (via new) and operate on instance state. Static methods belong to the class itself, not instances — called via ClassName.method(). Static methods cannot access instance fields directly without an object reference.

8. What is the difference between a nested class and an inner class in Java?

Model Answer: "Static nested class does not have access to enclosing class instance (use new OuterClass.InnerClass()). Inner class (non-static) has implicit reference to outer class instance and can access its members. Inner class requires an outer instance to create; nested class does not.

9. Why should mutable objects stored as fields be defensive-copied in constructors and getters?

Model Answer: "Prevents external code from modifying internal state — maintains encapsulation. If you store a direct reference, caller can change the object and affect your invariants. Defensive copy (new ArrayList<>(list) or List.copyOf()) gives you independent ownership.

10. What is method signature and does it include the return type?

Model Answer: "Method signature includes method name and parameter types (e.g., doSomething(String, int)). Return type is NOT part of the method signature in Java. Two methods with same name and params but different return types cause compilation error.

11. What is the purpose of the `static` keyword on a nested class?

Model Answer: "Makes the nested class independent of any outer class instance. Static nested class cannot access outer class instance fields or methods directly. Useful when nested class doesn't need to reference outer class instances.

12. What is an anonymous class and when would you use one?

Model Answer: "Anonymous class is a local class without a name, defined and instantiated in one expression. Used for one-off implementations of interfaces or extension of classes on the fly. Common for event handlers, Runnable tasks, Comparator implementations.

13. Can a class be declared as both `final` and `abstract`?

Model Answer: "No — these are mutually exclusive modifiers. abstract means the class must be subclassed; final means the class cannot be subclassed. Compilation error results from this combination.

14. What is the difference between member nested class and local inner class?

Model Answer: "Member inner class is declared at class level (as a member of the outer class). Local inner class is declared inside a method body. Local inner class can access effectively final local variables of the enclosing method.

15. What happens when a class has no explicit constructor definition?

Model Answer: "Java compiler provides a default no-arg constructor automatically. This default constructor initializes all instance fields to default values (0, false, null). Once you define any constructor, the default is no longer provided.

16. What is the relationship between `class` and `interface` for multiple inheritance?

Model Answer: "Java class supports single inheritance only (one extends clause). Class can implement multiple interfaces (implements clause accepts comma-separated list). Interface can extend multiple interfaces (allows contract composition without state inheritance).

17. When is it appropriate to use `var` (local variable type inference) in Java 10+?

Model Answer: "Useful for long generic type names or complex generic chains. Cannot use var for fields, method parameters, or constructor parameters. Must initialize var on declaration — compiler infers exact type from initializer.

18. What is the purpose of a private constructor in a class?

Model Answer: "Prevents direct instantiation from outside the class. Common in Singleton pattern — class controls how many instances exist. Factory pattern also uses private constructors to control object creation.

19. What is the difference between an object reference and a primitive variable?

Model Answer: "Primitives store actual values (int, boolean, double) directly on the stack. Object references store memory addresses pointing to heap-allocated objects. Reference default is null; primitives have type-specific defaults (0, false, 0.0).

20. What is the diamond problem and how does Java handle multiple inheritance?

Model Answer: "Diamond problem occurs when a class inherits from two classes that have a common ancestor. Java does not support multiple class inheritance, avoiding this issue. Interfaces can extend multiple interfaces, providing contract composition without state inheritance.

Further Reading

Summary

Classes and objects form the bedrock of Java’s object-oriented model. A class defines the blueprint — the fields that hold state and the methods that provide behavior — while objects are live instances created via the new keyword, each with their own memory for instance fields.

Understanding the distinction between references and objects is critical: a reference is a pointer to an object’s location in heap memory, and multiple references can point to the same object. This is why defensive copying matters for mutable types.

The class anatomy diagram shows how fields (state) and methods (behavior) work together. When designing classes, favor encapsulation by keeping fields private and exposing controlled access points. This prevents external code from putting objects into invalid states.

For one-off behavior, anonymous classes and lambda expressions (for functional interfaces) let you create objects without formal class definitions. Static factory methods offer an alternative to constructors for cases where you need to return cached instances, subclasses, or want more expressive creation syntax.

Classes integrate closely with other OOP concepts: constructors (covered in Java Constructors) initialize new instances, fields (detailed in Fields and Instance Variables) hold the per-object state, and methods define behavior that subclasses can override to achieve polymorphism (explored in Polymorphism in Java).

Category

Related Posts

Abstract Classes in Java

Learn about partially implemented classes that define contracts for subclasses using abstract methods and concrete implementations.

#java-abstract-classes #java #java-fundamentals

Arithmetic Operators in Java

Master Java arithmetic operators: addition, subtraction, multiplication, division, and modulo with integer division gotchas and operator precedence explained.

#java-arithmetic-operators #java #java-fundamentals

Array Basics in Java

Learn Java array fundamentals: declaration, initialization, element access, and the length property explained simply.

#java-array-basics #java #java-fundamentals