ZetCode

Java Class Class

Last modified: April 13, 2025

The java.lang.Class class represents the runtime representation of classes and interfaces in a Java application. It serves as the entry point for reflection operations, allowing programmers to retrieve metadata about classes, such as their fields, methods, constructors, and modifiers. By leveraging the Class class, developers can analyze and manipulate Java types dynamically.

Every object in Java is associated with a Class object that describes its type. These objects are automatically created by the Java Virtual Machine (JVM) when the corresponding classes are loaded. Since the Class class has no public constructor, instances of Class cannot be directly instantiated but are instead obtained through methods like getClass and Class.forName(String className).

Understanding the Class class is fundamental for advanced Java features such as reflection, annotations, and dynamic class loading. Reflection allows programs to inspect and modify class structures at runtime, enabling frameworks and libraries to perform dynamic operations without explicit type definitions.

Class Class Methods

The Class class provides various methods for examining class metadata at runtime. Some key methods include:

By utilizing these methods, Java enables dynamic class inspection and manipulation, which is particularly useful in frameworks, dependency injection, serialization, and testing environments.

Getting Class Objects

There are several ways to obtain a Class object in Java. The most common are using the class literal, calling getClass on an object, and using Class.forName. Each approach has different use cases and implications.

Main.java
class Sample {}

void main() {

    Class<Sample> cls1 = Sample.class;
    
    Sample obj = new Sample();
    Class<? extends Sample> cls2 = obj.getClass();
    
    try {
        Class<?> cls3 = Class.forName("Sample");
    } catch (ClassNotFoundException e) {
        System.out.println("Class not found: " + e.getMessage());
    }
    
    System.out.println("Class name via class literal: " + cls1.getName());
    System.out.println("Class name via getClass(): " + cls2.getName());
}

This example demonstrates three ways to get Class objects. The class literal is compile-time safe, getClass works with existing objects, and Class.forName allows dynamic class loading by name. All three approaches return the same Class object for a given class.

Inspecting Class Metadata

The Class class provides methods to inspect various aspects of a class's structure. This includes getting the class name, package, modifiers, superclass, and implemented interfaces. These methods are fundamental for reflection-based code analysis.

Main.java
interface Greetable {
    void greet();
}

class Person implements Greetable {
    public void greet() {
        System.out.println("Hello!");
    }
}

void main() {

    Class<Person> personClass = Person.class;
    
    System.out.println("Class name: " + personClass.getName());
    System.out.println("Simple name: " + personClass.getSimpleName());
    System.out.println("Package: " + personClass.getPackageName());
    System.out.println("Superclass: " + personClass.getSuperclass());
    
    Class<?>[] interfaces = personClass.getInterfaces();
    System.out.println("Implemented interfaces:");
    for (Class<?> iface : interfaces) {
        System.out.println("  " + iface.getSimpleName());
    }
    
    System.out.println("Is interface? " + personClass.isInterface());
    System.out.println("Is array? " + personClass.isArray());
}

This example shows how to inspect basic class metadata. We examine the Person class's name, package, superclass, and implemented interfaces. The output demonstrates how reflection can reveal a class's structure at runtime.

Accessing Fields

The Class class provides methods to access a class's fields reflectively. You can get public fields, declared fields (including non-public ones), and inspect field metadata like type and modifiers. Field values can also be get and set reflectively.

Main.java
class Book {

    public String title;
    private String author;
    protected int pages;
}

void main() throws Exception {

    Class<Book> bookClass = Book.class;
    Book book = new Book();
    
    // Get all public fields (including inherited)
    Field[] publicFields = bookClass.getFields();
    System.out.println("Public fields:");
    for (Field field : publicFields) {
        System.out.println("  " + field.getName());
    }
    
    // Get all declared fields (regardless of modifier)
    Field[] allFields = bookClass.getDeclaredFields();
    System.out.println("\nAll declared fields:");
    for (Field field : allFields) {
        System.out.println("  " + field.getName() + 
                          " (" + field.getType().getSimpleName() + ")");
    }
    
    // Access private field
    Field authorField = bookClass.getDeclaredField("author");
    authorField.setAccessible(true); // Override access control
    authorField.set(book, "J.K. Rowling");
    System.out.println("\nAuthor set to: " + authorField.get(book));
}

This example demonstrates field access through reflection. We show how to get public fields, all declared fields, and how to access private fields by overriding access control. Note that bypassing access control should be done cautiously as it breaks encapsulation.

Invoking Methods

Reflection allows method invocation at runtime. The Class class provides methods to get method metadata and invoke methods. This is useful for frameworks that need to call methods dynamically, such as dependency injection or testing frameworks.

Main.java
class Calculator {

    public int add(int a, int b) {
        return a + b;
    }
    
    private String repeat(String s, int times) {
        return s.repeat(times);
    }
}

void main() throws Exception {

    Class<Calculator> calcClass = Calculator.class;
    Calculator calc = new Calculator();
    
    // Get and invoke public method
    Method addMethod = calcClass.getMethod("add", int.class, int.class);
    int result = (int) addMethod.invoke(calc, 5, 3);
    System.out.println("5 + 3 = " + result);
    
    // Get and invoke private method
    Method repeatMethod = calcClass.getDeclaredMethod("repeat", 
                                                    String.class, int.class);
    repeatMethod.setAccessible(true);
    String repeated = (String) repeatMethod.invoke(calc, "Java ", 3);
    System.out.println("Repeated: " + repeated);
    
    // List all public methods (including inherited)
    System.out.println("\nPublic methods:");
    for (Method method : calcClass.getMethods()) {
        System.out.println("  " + method.getName());
    }
}

This example shows how to discover and invoke methods reflectively. We demonstrate calling both public and private methods, listing all public methods, and handling method parameters. Method invocation is type-safe at runtime but loses compile-time type checking.

Creating Instances

The Class class enables dynamic instantiation of objects through reflection, allowing objects to be created even when their types are unknown at compile time. This is particularly useful in frameworks, dependency injection, and scenarios requiring flexible object creation. Using reflection, constructors can be accessed and invoked dynamically, providing precise control over object instantiation.

Main.java
static class Vehicle {

    private String type;

    public Vehicle() {
        this("Unknown");
    }

    public Vehicle(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}

public static void main(String[] args) throws Exception {

    // Using default constructor reflectively
    Class<Vehicle> vehicleClass = Vehicle.class;
    Vehicle v1 = vehicleClass.getDeclaredConstructor().newInstance();
    System.out.println("Default type: " + v1.getType());

    // Using parameterized constructor
    Constructor<Vehicle> constructor = vehicleClass.getConstructor(String.class);
    Vehicle v2 = constructor.newInstance("Truck");
    System.out.println("Specified type: " + v2.getType());

    // Creating instance from class name
    Class<?> dynamicClass = Class.forName("Main$Vehicle");
    Vehicle v3 = (Vehicle) dynamicClass.getConstructor(String.class).newInstance("Car");
    System.out.println("Dynamic type: " + v3.getType());
}

This example demonstrates three ways to create instances reflectively:

Reflection-based instantiation is widely used in frameworks such as Spring and dependency injection systems, enabling dynamic object creation without explicit type dependencies. By leveraging reflection, applications can instantiate classes based on runtime configurations, improving flexibility in modular and extensible designs.

Working with Arrays

The Class class provides special support for array types. You can create arrays reflectively, get component types, and determine array dimensions. Array classes follow special naming conventions in Java reflection.

Main.java
void main() throws ClassNotFoundException {
    
    // Get array class
    Class<?> stringArrayClass = String[].class;
    System.out.println("Array class: " + stringArrayClass.getName());
    
    // Create array instance
    String[] names = (String[]) Array.newInstance(String.class, 3);
    Array.set(names, 0, "Alice");
    Array.set(names, 1, "Bob");
    Array.set(names, 2, "Charlie");
    
    System.out.println("\nArray contents:");
    for (int i = 0; i < Array.getLength(names); i++) {
        System.out.println("  " + Array.get(names, i));
    }
    
    // Multi-dimensional arrays
    Class<?> int2DArrayClass = Class.forName("[[I");
    int[][] matrix = (int[][]) Array.newInstance(int.class, 2, 3);
    matrix[1][2] = 42;
    System.out.println("\nMatrix[1][2] = " + matrix[1][2]);
    
    // Component type
    Class<?> componentType = stringArrayClass.getComponentType();
    System.out.println("\nComponent type: " + componentType);
}

This example shows reflection with arrays. We demonstrate getting array class, creating arrays reflectively, accessing elements, working with multi-dimensional arrays, and examining component types. Array reflection follows Java's internal naming conventions for array types.

Source

Java Class Class Documentation

In this article, we've covered the essential aspects of the Java Class class with practical examples. Understanding Class is crucial for advanced Java features like reflection, annotations, and dynamic class loading.

Author

My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.

List all Java tutorials.