CodeGym /Courses /JAVA 25 SELF /Introduction to reflection: the Class class, why it’s nee...

Introduction to reflection: the Class class, why it’s needed

JAVA 25 SELF
Level 62, Lesson 0
Available

1. What reflection is

Reflection in Java is a mechanism that allows a program to inspect — and even modify — its own structure at runtime. It’s as if you could look inside yourself and find out: “What fields and methods do I have? What constructor do I have? What class am I anyway?” — and even invoke a private method on yourself.

Formal definition:
Reflection is an API that lets you obtain information about classes, interfaces, fields, methods, and constructors at runtime, and also work with them.

Why use reflection?

  • Frameworks: Spring, Hibernate, and JUnit use reflection for “magic”: automatically creating objects, injecting dependencies, invoking methods by name and by annotations.
  • Serialization: Turning objects into JSON/XML and back — you need to discover fields and be able to access them.
  • Testing: Automatically finding and invoking methods with annotations (for example, @Test).
  • Dynamic class loading: Loading a class by name at runtime (plugins, drivers).
  • IDEs and code analysis: Autocompletion, inspections, refactoring.

Real‑world example

public class Person {
    private String name;
    private int age;

    public void sayHello() {
        System.out.println("Hello, my name is " + name);
    }
}

With reflection we can, at runtime, discover that the Person class has fields name and age, as well as a sayHello method. Moreover, you can change the value of a private field or invoke a method by its name.

A bit of humor
You could say reflection is like in “The Matrix”: you realise that all your code is data that can be inspected and changed on the fly. Just don’t forget the red pill of security!

2. The Class class: the heart of reflection

In Java, every object and every type at runtime is associated with a special object of type Class. This object is the “meta‑information” about the type. The java.lang.Class<T> class is the central entry point into the world of reflection.

How do you obtain a Class object?

There are several ways:

  • Via .class
    The most straightforward and safe way if the class is known at compile time:
    Class<Person> personClass = Person.class;
  • Via an object: getClass()
    If you already have an instance:
    Person p = new Person();
    Class<?> clazz = p.getClass();
    Here, clazz is the Class object that describes the actual class of the object p.
  • Via a string: Class.forName()
    If the class name is known only at runtime:
    Class<?> clazz = Class.forName("com.example.Person");
    Suitable for loading plugins, drivers, and other dynamic use cases.

Example: Getting the class name

Person p = new Person();
Class<?> clazz = p.getClass();
System.out.println(clazz.getName()); // Will print: Person or com.example.Person

Table: Main ways to obtain a Class object

How to obtain When to use Example
MyClass.class
If the class is known at compile time
String.class
object.getClass()
If you have an object
someList.getClass()
Class.forName(name)
If the class name is known at runtime
Class.forName("java.util.Date")

Diagram: An object and its Class

+-------------------+
|   Person p        |
+-------------------+
         |
         v
+-------------------+
|  p.getClass()     |
+-------------------+
         |
         v
+-------------------+
|   Class<Person>   |
+-------------------+

Type checking via Class

Sometimes you need to know whether an object belongs to a specific class:

if (p.getClass() == Person.class) {
    System.out.println("This is definitely a Person!");
}

Or, taking inheritance into account — the usual instanceof operator:

if (p instanceof Person) {
    // works as usual
}

3. When reflection is needed — and when it isn’t

With great power comes great responsibility. Reflection is a powerful tool, but use it only when it’s truly necessary.

Where reflection is essential

  • Frameworks and libraries: Spring, Hibernate, JUnit, Jackson — automation, “magic,” and less “manual” code.
  • Serialization: You need to discover an object’s fields to turn it into JSON/XML.
  • Plugins and extensions: Classes are loaded dynamically and their structure is not known beforehand.

Why you shouldn’t overuse reflection

  • Loss of type safety: Errors surface at runtime.
  • Maintenance complexity: Code becomes less obvious.
  • Performance: Slower than direct calls.
  • Security: You can bypass encapsulation and expose private data.

Example: When reflection isn’t needed

p.sayHello();

Example: When reflection is a must‑have
You’re writing a mini‑framework for testing: the user marks methods with the @Test annotation, and the program must find and invoke them automatically — there’s no way to do this without reflection.

4. Demo: getting to know Class

Example: Printing the class name and its superclass

public class ReflectionDemo {
    public static void main(String[] args) {
        String s = "Hello, reflection!";
        Class<?> clazz = s.getClass();

        System.out.println("Class name: " + clazz.getName());
        System.out.println("Simple class name: " + clazz.getSimpleName());
        System.out.println("Package: " + clazz.getPackageName());
        System.out.println("Superclass: " + clazz.getSuperclass().getName());
    }
}

Result:

Class name: java.lang.String
Simple class name: String
Package: java.lang
Superclass: java.lang.Object

Example: Getting a Class for primitive types

Class<Integer> intClass = int.class;
System.out.println(intClass.getName()); // int

Class<?> doubleClass = double.class;
System.out.println(doubleClass.getName()); // double

Example: Getting a Class for an array

int[] arr = new int[10];
Class<?> arrClass = arr.getClass();
System.out.println(arrClass.getName()); // [I (JVM-specific format for arrays)

Example: Checking type membership

if (arrClass.isArray()) {
    System.out.println("It's an array!");
}

5. Practice: mini program “What class is it?”

Let’s write a program that takes a fully qualified class name (for example, "java.util.ArrayList") and prints basic information about it.

import java.util.Scanner;

public class ClassInfoPrinter {
    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter the fully qualified class name: ");
        String className = scanner.nextLine();

        Class<?> clazz = Class.forName(className);

        System.out.println("Class name: " + clazz.getName());
        System.out.println("Package: " + clazz.getPackageName());
        System.out.println("Superclass: " + clazz.getSuperclass().getName());

        Class<?>[] interfaces = clazz.getInterfaces();
        System.out.print("Implements interfaces: ");
        for (Class<?> i : interfaces) {
            System.out.print(i.getName() + " ");
        }
        System.out.println();
    }
}

Sample run:

Enter the fully qualified class name: java.util.ArrayList
Class name: java.util.ArrayList
Package: java.util
Superclass: java.util.AbstractList
Implements interfaces: java.util.List java.util.RandomAccess java.lang.Cloneable java.io.Serializable 

Interesting facts about Class and reflection

  • Every loaded type in the JVM has one shared representative — a Class object. All instances of the same type share the same Class<T>.
  • You can determine the kind of type: isInterface(), isEnum(), isArray(), isPrimitive(), and others.
  • If you specify an incorrect name in Class.forName, you’ll get a ClassNotFoundException.
  • With reflection you can create objects without knowing the class at compile time — we’ll talk about this in the next lecture.

6. Common mistakes when first learning reflection

Mistake #1: Expecting reflection to be fast.
In reality, reflection is slower than ordinary method calls and field access. Don’t use it for every little thing.

Mistake #2: Trying to get a Class for a non‑existent class.
If you misspell the name, you’ll get a ClassNotFoundException. Handle this exception.

Mistake #3: Confusing a Class object with an instance.
A Class object describes the structure of a type; it is not an instance of that type.

Mistake #4: Using reflection when it isn’t necessary.
If you can manage with ordinary code, do so. Reflection is intended for dynamics, frameworks, plugins, and similar tasks.

Mistake #5: Careless handling of private fields and methods.
Reflection allows you to bypass encapsulation, which can lead to errors and security issues, especially in large projects.

1
Task
JAVA 25 SELF, level 62, lesson 0
Locked
Identification of the digital "blueprint" 📚
Identification of the digital "blueprint" 📚
1
Task
JAVA 25 SELF, level 62, lesson 0
Locked
Searching for magical scrolls by name 🧙
Searching for magical scrolls by name 🧙
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION