Classes and Objects in Java
- A class is a blueprint; an object is a live instance allocated on the heap.
- Each object gets its own copy of instance fields — changes to one object never affect another.
- If you define any constructor, Java will NOT generate a default no-arg constructor.
A class is a blueprint. An object is a concrete instance of that blueprint created in memory. You define a class once with its fields and methods, then create as many objects from it as you need using the new keyword. Each object gets its own copy of instance fields.
Defining a Class: The Blueprint
A class definition has three primary components: Fields (the state/data it holds), Constructors (the initialization logic), and Methods (the behaviors it performs). In production Java, we prioritize encapsulation by keeping fields private and exposing them through controlled methods.
package io.thecodeforge.java.oop; import java.util.UUID; /** * Production-grade BankAccount implementation emphasizing encapsulation. */ public class BankAccount { private final String accountId; private String owner; private double balance; // Constructor: Essential for ensuring an object starts in a valid state public BankAccount(String owner, double initialBalance) { if (initialBalance < 0) { throw new IllegalArgumentException("Initial balance cannot be negative"); } this.accountId = UUID.randomUUID().toString(); this.owner = owner; this.balance = initialBalance; } public void deposit(double amount) { if (amount <= 0) throw new IllegalArgumentException("Deposit must be positive"); this.balance += amount; } public boolean withdraw(double amount) { if (amount > this.balance) return false; this.balance -= amount; return true; } // Standard Getters public double getBalance() { return this.balance; } public String getOwner() { return this.owner; } public String getAccountId() { return this.accountId; } @Override public String toString() { return String.format("BankAccount[ID=%s, owner=%s, balance=%.2f]", accountId, owner, balance); } }
Creating Objects: Heap Allocation
When you use the new keyword, the JVM performs several critical steps: it allocates memory on the Heap, initializes fields to default values, executes the constructor logic, and finally returns a reference (memory address) to the variable on the Stack.
package io.thecodeforge.java.oop; public class BankAccountDemo { public static void main(String[] args) { // Each 'new' call creates a unique identity in memory BankAccount accountA = new BankAccount("Senior Dev", 5000.00); BankAccount accountB = new BankAccount("Junior Dev", 1000.00); accountA.deposit(500.00); System.out.println(accountA); System.out.println(accountB); // Reference comparison vs Identity System.out.println("Different objects: " + (accountA != accountB)); } }
BankAccount[ID=..., owner=Junior Dev, balance=1000.00]
Different objects: true
Constructor Overloading & Delegation
Java allows multiple constructors (Overloading) as long as their parameter signatures differ. Use to delegate calls between constructors, reducing code duplication and ensuring a single source of initialization truth.this()
package io.thecodeforge.java.oop; public class Product { private String sku; private double price; private int stock; // Primary Constructor public Product(String sku, double price, int stock) { this.sku = sku; this.price = price; this.stock = stock; } // Overloaded Constructor: Defaults stock to zero public Product(String sku, double price) { this(sku, price, 0); } // Overloaded Constructor: Default/Placeholder values public Product() { this("PENDING-SKU", 0.0, 0); } @Override public String toString() { return String.format("Product[%s] - $%.2f (In Stock: %d)", sku, price, stock); } }
The 'this' Keyword and Method Chaining
this is a reference to the current object instance. It is indispensable for resolving naming conflicts between parameters and fields, and for enabling 'Fluent APIs' through method chaining.
package io.thecodeforge.java.oop; public class Counter { private int count = 0; private final String label; public Counter(String label) { this.label = label; // 'this.label' refers to field; 'label' refers to parameter } // Method Chaining: Return 'this' to allow consecutive calls public Counter increment() { this.count++; return this; } public void display() { System.out.println(this.label + " status: " + this.count); } public static void main(String[] args) { new Counter("System-Metrics") .increment() .increment() .display(); } }
🎯 Key Takeaways
- A class is a blueprint; an object is a live instance allocated on the heap.
- Each object gets its own copy of instance fields — changes to one object never affect another.
- If you define any constructor, Java will NOT generate a default no-arg constructor.
- Use this(args) as the first line of a constructor to delegate to another constructor.
- The this keyword refers to the current object — use it to disambiguate fields from parameters.
Interview Questions on This Topic
- QExplain the lifecycle of a Java object from 'new' keyword to Garbage Collection.
- QWhat is the impact of defining a parameterized constructor on the default no-arg constructor?
- QHow does the JVM distinguish between 'this.field' and a local parameter with the same name during execution?
- QLeetCode Standard: Given a class 'ListNode', how would you implement a constructor that allows for recursive initialization of a linked list?
- QDescribe a scenario where returning 'this' from a method is preferred over returning 'void' (e.g., The Builder Pattern).
Frequently Asked Questions
What is the difference between a class variable and an instance variable?
An instance variable is declared without static — each object gets its own copy on the heap. A class variable is declared with static — there is exactly one copy shared across all instances, stored in the Method Area (PermGen/Metaspace). If you change a static field, the change is reflected across all objects of that class.
What happens if I do not write a constructor?
Java automatically generates a 'default constructor' (no-arg) that calls the superclass constructor. However, the moment you define any constructor with parameters, Java will no longer provide this default. In production frameworks like Spring or Hibernate, you often need to manually re-add a no-arg constructor for reflection-based object creation.
What is the difference between == and .equals() when comparing objects?
== is a reference comparison—it checks if two variables point to the exact same memory address. .equals() is a logical comparison—it checks if two objects are 'meaningfully equivalent' based on their internal data. You should always override both equals() and hashCode() when your business logic requires content-based comparison.
Can a Java class have multiple constructors?
Yes, this is known as Constructor Overloading. It allows you to initialize objects in different ways depending on available data, such as creating a User object with just an email, or with an email and a full profile.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.