Classes & Objects

The foundation of Java OOP — how classes define blueprints and objects are instances, with constructors, fields, methods, and the this keyword.

Last reviewed: January 2025

·Java 8Java 11Java 17Java 21
✅
Quick Understanding: A class is a blueprint. An object is an instance of that blueprint. A class defines what data (fields) and behavior (methods) objects will have. When you write new Employee(), you are creating an object — an instance of the Employee class — allocated on the heap.

🔍 What Is It?

Class

A class is a template that defines:

  • →Fields (instance variables) — the data/state each object holds
  • →Methods — the behavior/actions objects can perform
  • →Constructors — special methods to initialize objects
  • →Static members — shared across all instances (class-level, not instance-level)

Object

An object is a runtime instance of a class:

  • →Has its own copy of instance fields
  • →Shares methods with all other instances (methods are in the class, not each object)
  • →Lives on the heap, referenced by a variable on the stack

Records (Java 16+)

Immutable data classes with auto-generated constructor, getters, equals, hashCode, toString:

record Point(int x, int y) {}

Key Concepts:

  • →this — refers to the current object instance
  • →static — belongs to the class, not instances
  • →final — on a class: cannot be subclassed; on a field: cannot be reassigned; on a method: cannot be overridden
  • →Access modifiers: private, package-private (default), protected, public

🎯 Why Do We Need It?

Classes and objects are the core of OOP. They solve:

  • →Modeling real-world entities — Employee, Order, Product are natural classes
  • →Encapsulation — bundle data and behavior together; hide implementation details
  • →Reusability — define once, instantiate many times
  • →Code organization — group related functionality into cohesive units

Without classes, you'd have scattered variables and functions with no clear ownership — like C procedural code. In enterprise Java, every entity (User, Order, Product), every service (OrderService, PaymentService), and every configuration (AppConfig) is a class.

🏗️ What Happens in Real Projects?

In a real Spring Boot enterprise project:

// Entity class (JPA)
@Entity
public class Order {
    @Id @GeneratedValue
    private Long id;
    private String customerId;
    private BigDecimal totalAmount;
    private OrderStatus status;
    // getters, setters, business methods
}

// Service class
@Service
public class OrderService {
    private final OrderRepository repo;
    public Order createOrder(CreateOrderRequest req) { ... }
}

// Value object (Record, Java 16+)
record CreateOrderRequest(String customerId, List<OrderItem> items) {}

⚙️ How It Works

Object Creation:

  1. JVM allocates memory on the heap for the object
  2. All instance fields are initialized to defaults (0, null, false)
  3. Constructor runs, setting fields to specified values
  4. Reference to the object is returned to the caller

Constructor Chaining:

  • →this() calls another constructor in the same class
  • →super() calls the parent class constructor (implicit first line if not specified)

Static vs Instance:

  • →Static fields: one copy shared by all instances — stored in Metaspace
  • →Instance fields: each object has its own copy — stored on Heap
  • →Static methods: called on the class, cannot access instance fields
  • →Instance methods: called on an object, can access both static and instance fields

Garbage Collection:

Objects become eligible for GC when no references point to them.

💻 Example

Building a Real Enterprise Class — A complete Employee class showing all key concepts in one place.

java
public class Employee {
    // Static field — shared by all instances
    private static int totalEmployees = 0;
    
    // Instance fields — each object has its own copy
    private final Long id;          // final: set once in constructor
    private String name;
    private String department;
    private BigDecimal salary;
    
    // Primary constructor
    public Employee(Long id, String name, String department, BigDecimal salary) {
        this.id = id;
        this.name = name;
        this.department = department;
        this.salary = salary;
        totalEmployees++;           // update shared counter
    }
    
    // Convenience constructor — chains to primary
    public Employee(Long id, String name) {
        this(id, name, "Unassigned", BigDecimal.ZERO);
    }
    
    // Business method
    public void giveRaise(BigDecimal percentage) {
        if (percentage.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("Raise must be positive");
        }
        BigDecimal multiplier = BigDecimal.ONE.add(
            percentage.divide(new BigDecimal("100"))
        );
        this.salary = this.salary.multiply(multiplier)
                          .setScale(2, RoundingMode.HALF_UP);
    }
    
    // Static factory method (preferred over constructor in many cases)
    public static Employee newHire(String name, String department) {
        return new Employee(
            System.currentTimeMillis(),  // generate ID
            name, department,
            new BigDecimal("50000.00")   // default starting salary
        );
    }
    
    // Static utility
    public static int getTotalEmployees() { return totalEmployees; }
    
    // Getters (no public setters for id — immutable)
    public Long getId() { return id; }
    public String getName() { return name; }
    public String getDepartment() { return department; }
    public BigDecimal getSalary() { return salary; }
    public void setName(String name) { this.name = name; }
    
    @Override
    public String toString() {
        return "Employee{id=%d, name='%s', dept='%s', salary=%s}"
            .formatted(id, name, department, salary);
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Employee other)) return false;
        return Objects.equals(id, other.id);  // equality by ID
    }
    
    @Override
    public int hashCode() { return Objects.hash(id); }
}

🤔 Common Confusions

❓ What is the difference between a class and an object?

A class is a blueprint/template defined in code. An object is a live instance created from that blueprint at runtime. Employee is a class. The specific employee "John Smith with ID 42" is an object. You can create thousands of Employee objects from one Employee class.

❓ What is the difference between static and instance members?

Static members belong to the class — there is one copy shared by all instances. Instance members belong to each object — every object has its own copy. Use static for utility methods, constants, and counters. Use instance fields for object-specific state (name, age, salary).

❓ When should I use a Record vs a regular class?

Use Record for immutable data carriers — DTOs, value objects, API request/response bodies, configuration data. Use regular classes when you need mutability, inheritance, or complex business logic. Records cannot extend other classes (they implicitly extend Record) but can implement interfaces.

🚨 Common Production Issues

Mutable objects shared across threads — if multiple threads access the same object and at least one modifies it, you have a race condition. Use immutable objects (records, final fields) or synchronization.

equals/hashCode not overridden — objects used in HashSet or as HashMap keys must have consistent equals and hashCode. Default Object.equals() compares references, not values.

Static mutable state — static fields shared across all instances cause bugs in multi-threaded environments and make testing hard. Prefer dependency injection over static state.

Constructors with too many parameters — more than 4-5 parameters is a code smell. Use Builder pattern or records with named fields.

✅ Best Practices

Always override equals() and hashCode() together, based on the same fields.

Make fields private and provide getters/setters only where needed.

Use records (Java 16+) for immutable data carriers — they eliminate boilerplate.

Prefer static factory methods over constructors for complex object creation.

Keep classes small and focused — Single Responsibility Principle.

Use final on fields that should not change after construction.

🏛️ Architect's Note

Senior Architect's Perspective

In modern Spring Boot applications, most classes fall into clear categories: @Entity (JPA data model), @Service (business logic), @Repository (data access), @Controller/@RestController (HTTP layer), and Records/DTOs (data transfer). This layered architecture with clear class responsibilities is what makes enterprise codebases maintainable. Avoid "god classes" that do everything.

💬 Frequently Asked Questions

What is the Builder pattern and when should I use it?

The Builder pattern is for creating objects with many optional parameters. Instead of a constructor with 10 parameters, you chain setter-like methods: new Employee.Builder().name("John").department("Eng").salary(90000).build(). Lombok @Builder annotation generates this automatically. Use it when a class has more than 4-5 constructor parameters.

What is the difference between shallow copy and deep copy?

Shallow copy copies the object but shares references to nested objects. Deep copy recursively copies all nested objects. Object.clone() does a shallow copy by default. For deep copy, use copy constructors, serialization, or libraries like ModelMapper. In practice, prefer immutable objects (records) to avoid copy complexity entirely.

🎯 Key Takeaways

1A class is a blueprint; an object is a runtime instance on the heap
2Static members belong to the class; instance members belong to each object
3Always override equals() and hashCode() together
4Use records (Java 16+) for immutable data carriers — they eliminate boilerplate
5Constructors chain with this(); parent constructors with super()
6Keep classes small and focused on a single responsibility

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Core Java references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages