DevNotes
Support
Reflection · Chapter 03

Three Ways to Obtain a Class Object

ClassName.class, object.getClass() and Class.forName() — compile time vs runtime, which ones load the class, ClassNotFoundException, and what frameworks use.

◷ 5 min read§ 10 sections? 7 interview questions{ } 12 code examples

#Introduction

In the previous chapter we learned that Reflection starts with the Class object, and the Class object holds metadata about a class. Now the question is: how do we obtain the Class object?

Java provides three standard ways:

  1. ClassName.class
  2. object.getClass()
  3. Class.forName()

Every Java developer should know when to use each one.

#Overview

MethodKnown at compile timeWorks at runtimeObject requiredLoads the class
Employee.classYes—NoLoads if needed, but does not initialize
emp.getClass()NoYesYesClass is already loaded
Class.forName()NoYesNoYes — loads and initializes

#Method 1 — Using .class

Java
class Employee {
}

Class<Employee> cls = Employee.class;

Employee.class returns the Class object representing Employee.

How Does It Work?

During compilation, the compiler already knows Employee, so the expression refers directly to the corresponding Class object. No object creation is required.

Employee.class
Class<Employee>
Metadata

Advantages

  • Very simple
  • Fast
  • Type-safe (Class<Employee>, not Class<?>)
  • No object required

Disadvantage

The class name must be known during compilation. Employee.class works, but this is impossible:

Java
String className = "Employee";
className.class;   // ❌ does not compile

Interview Questions on .class

Can we write…?Answer
String.classYes — String itself is a class
int.classYes — primitive types also have Class objects
void.classYes — even void has a corresponding Class object

#Method 2 — Using getClass()

Java
class Employee {
}

Employee emp = new Employee();
Class<?> cls = emp.getClass();

Returns the runtime class of the object.

Internal Working

Every Java object internally stores a reference to its class. When we write emp.getClass(), the JVM simply returns that stored reference.

Employee object
Class<Employee>
Metadata

The Object Must Already Exist

Without an object, we cannot call getClass():

Java
Employee.getClass();          // ❌ compilation error — getClass() is an instance method

Employee emp = new Employee();
emp.getClass();               // ✅ correct

Advantage — The Actual Runtime Type

Useful when we don't know the object's exact type:

Java
Object obj = new Employee();
obj.getClass();   // Employee, not Object

It returns the actual runtime class.

Java
Object obj = "Hello";
System.out.println(obj.getClass());
Output
class java.lang.String
Java
Object obj = 100;
System.out.println(obj.getClass());
Output
class java.lang.Integer

…because of autoboxing.

#Method 3 — Using Class.forName()

The most important method in Reflection.

Java
Class<?> cls = Class.forName("com.demo.Employee");

Notice the argument is a fully qualified class name — not just Employee, but com.demo.Employee.

What Does Class.forName() Do?

It asks the JVM: "Load this class if it is not already loaded, and return its Class object."

Find the class
Load the class (if not already loaded)
Initialize it (static blocks run)
Return the Class object

Why Frameworks Use It

Imagine Spring Boot. You write com.company.UserService. Spring knows that name only as a String, and later turns it into a class:

Java
Class.forName(className);

Spring never writes new UserService() because it doesn't know your class at compile time.

Dynamic Class Loading

Java
String className = "com.demo.Employee";
Class<?> cls = Class.forName(className);

Tomorrow className = "com.demo.Student" — no code changes required, only the string changes. This is called dynamic class loading.

ClassNotFoundException

Suppose Class.forName("ABC") is called but no ABC class exists. The JVM throws ClassNotFoundException (a checked exception), so:

Java
try {
    Class.forName("ABC");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

#Difference Between All Three

.classgetClass()Class.forName()
ExampleEmployee.classemp.getClass()Class.forName("com.demo.Employee")
Requires an object✗✓✗
Compile-time class reference✓✗✗
Runtime class discovery✗✓✓
Uses a String class name✗✗✓
Can dynamically load classes✗✗✓
Throws ClassNotFoundException✗✗✓
Used heavily in frameworksFor known classes (e.g. SpringApplication.run(App.class))Limited✓
Key traitNo exceptionReturns runtime typeDynamic, String-based

#Which One Should We Use?

SituationUse
The class is knownEmployee.class
An object already existsemp.getClass()
The class name comes from a database, properties file, XML, JSON, configuration or user inputClass.forName()

Real-World Example

Suppose application.properties contains:

Text
service.class=com.demo.UserService

Java code:

Java
String className = properties.getProperty("service.class");
Class<?> cls = Class.forName(className);

Now the application is configurable without changing source code.

#Interview Questions

Q1Which method is most commonly used by frameworks?

Class.forName() (or class-loader equivalents), because frameworks discover classes dynamically.

Q2Which method requires an object?

getClass().

Q3Which methods work even before object creation?

Employee.class and Class.forName().

Q4Which method throws ClassNotFoundException?

Class.forName().

Q5Why do we use the fully qualified class name in Class.forName()?

Because multiple packages can contain classes with the same simple name. The fully qualified name uniquely identifies the class for the JVM — e.g. com.company.hr.Employee and com.company.sales.Employee are both named Employee but are different classes.

#Key Points to Remember

  • Reflection always begins by obtaining a Class object.
  • Java provides three ways to obtain it.
  • .class is compile-time and type-safe.
  • getClass() returns the runtime class of an existing object.
  • Class.forName() dynamically loads a class using its fully qualified name.
  • Frameworks use Class.forName()-style loading because class names often become available only at runtime.

#Chapter Summary

  • .class is the simplest approach when the class is already known.
  • getClass() is useful when you already have an object and want its actual runtime type.
  • Class.forName() is the most powerful approach because it supports dynamic class loading — the preferred choice for frameworks like Spring, Hibernate, JDBC drivers and plugin architectures.