DevNotes
Support
Reflection · Chapter 05

The Class API

Inspecting class metadata — getName vs getSimpleName vs getCanonicalName, packages, superclass, interfaces, modifiers, isXxx() checks, isAssignableFrom and class loaders.

◷ 6 min read§ 16 sections? 11 interview questions{ } 20 code examples

#Introduction

So far we've learned what Reflection is, what the Class class is, how to obtain a Class object, and how class loading works. Now we'll actually start using Reflection. Everything begins with one object:

Java
Class<?> cls = Employee.class;

Once we have it, we can ask the JVM questions like: What is the class name? Which package does it belong to? Is it an interface or an enum? Which superclass does it extend? Which interfaces does it implement? Is it abstract, final, public? All this is called class metadata.

#Sample Class

Throughout this chapter we'll use:

Java
package com.demo;

public class Employee extends Person implements Serializable {
    private String name;

    public void work() {
    }
}
Java
Class<?> cls = Employee.class;

#Names

getName()

Returns the fully qualified class name (FQCN):

Java
System.out.println(cls.getName());
Output
com.demo.Employee

A fully qualified class name is package name + class name — com.demo.Employee instead of Employee. Spring, Hibernate, JDBC and Class.forName("com.demo.Employee") all use fully qualified names.

getSimpleName()

Returns only the class name:

Java
System.out.println(cls.getSimpleName());
Output
Employee
CallResult
cls.getName()com.demo.Employee
cls.getSimpleName()Employee

getCanonicalName()

Java
System.out.println(Employee.class.getCanonicalName());
Output
com.demo.Employee
MethodOutput
getName()JVM (binary) name
getCanonicalName()Java source name

For normal top-level classes both are identical. Arrays and nested classes differ:

TypegetName()getCanonicalName()
String[][Ljava.lang.String;java.lang.String[]
Nested Employee.Addresscom.demo.Employee$Addresscom.demo.Employee.Address

Canonical names are easier for humans to read.

#getPackageName()

Java
System.out.println(cls.getPackageName());
Output
com.demo

Older Java versions used cls.getPackage() (which returns a Package object); Java 9 introduced getPackageName(), which returns a String.

#getSuperclass()

Java
class Person {
}

class Employee extends Person {
}

System.out.println(cls.getSuperclass());
Output
class com.demo.Person
Java
String.class.getSuperclass();   // class java.lang.Object
Object.class.getSuperclass();   // null

Object.class.getSuperclass() returns null because Object has no parent. (Interfaces and primitive types also return null.)

#getInterfaces()

Returns all directly implemented interfaces.

Java
class Employee implements Serializable, Cloneable {
}

Class<?>[] interfaces = cls.getInterfaces();

for (Class<?> c : interfaces) {
    System.out.println(c.getName());
}
Output
java.io.Serializable
java.lang.Cloneable
  • Employee
    • Serializable
    • Cloneable
Reflection returns both interfaces

#getModifiers()

Returns modifiers as an integer bit mask.

Java
public final class Employee {
}

int mod = cls.getModifiers();
System.out.println(mod);
Output
17

17 = PUBLIC (1) + FINAL (16). Since that isn't meaningful directly, Java provides the Modifier utility class:

Java
import java.lang.reflect.Modifier;

System.out.println(Modifier.isPublic(mod));
System.out.println(Modifier.isFinal(mod));
System.out.println(Modifier.isAbstract(mod));
Output
true
true
false

Common Modifier Methods

Java
Modifier.isPublic()
Modifier.isPrivate()
Modifier.isProtected()
Modifier.isFinal()
Modifier.isStatic()
Modifier.isAbstract()
Modifier.isInterface()

These are commonly asked in interviews. (Modifier.toString(mod) gives "public final".)

#Type Checks

CheckExampleResult
isInterface()Vehicle.class.isInterface() (for interface Vehicle {})true
Employee.class.isInterface()false
isEnum()Status.class.isEnum() (for enum Status { ACTIVE, INACTIVE })true
Employee.class.isEnum()false
isAnnotation()Test.class.isAnnotation() (for @interface Test {})true
isArray()String[].class.isArray()true
Employee.class.isArray()false
isPrimitive()int.class.isPrimitive()true
double.class.isPrimitive()true
Integer.class.isPrimitive()false

Spring internally checks annotations like @Component, @Service and @Repository using Reflection.

#isAssignableFrom()

One of the most frequently asked Reflection methods.

Java
class Animal {
}

class Dog extends Animal {
}

System.out.println(Animal.class.isAssignableFrom(Dog.class));
Output
true

Meaning: a Dog object can be assigned to an Animal reference — Animal a = new Dog(); is valid.

Java
Dog.class.isAssignableFrom(Animal.class);   // false

…because Dog d = new Animal(); is invalid.

#getClassLoader()

Returns the class loader that loaded the class.

Java
System.out.println(Employee.class.getClassLoader());
Output
jdk.internal.loader.ClassLoaders$AppClassLoader@4e0e2f2a

(The hash after @ varies; on Java 8 it prints sun.misc.Launcher$AppClassLoader@….)

Java
System.out.println(String.class.getClassLoader());
Output
null

Why? Because String is loaded by the Bootstrap ClassLoader, which is implemented natively and isn't represented by a regular Java object.

#getDeclaredClasses()

Returns nested classes.

Java
class Employee {
    class Address {
    }
}

Class<?>[] classes = Employee.class.getDeclaredClasses();
System.out.println(Arrays.toString(classes));
Output
[class com.demo.Employee$Address]

#Complete Example

Java
Class<?> cls = Employee.class;

System.out.println(cls.getName());
System.out.println(cls.getSimpleName());
System.out.println(cls.getPackageName());
System.out.println(cls.getSuperclass());
System.out.println(cls.isInterface());
System.out.println(cls.isEnum());
System.out.println(cls.isAnnotation());
System.out.println(cls.isArray());
System.out.println(cls.isPrimitive());
System.out.println(cls.getClassLoader());
Output
com.demo.Employee
Employee
com.demo
class com.demo.Person
false
false
false
false
false
jdk.internal.loader.ClassLoaders$AppClassLoader@4e0e2f2a

#Methods Covered in This Chapter

Java
getName()
getSimpleName()
getCanonicalName()
getPackageName()
getSuperclass()
getInterfaces()
getModifiers()
isInterface()
isEnum()
isAnnotation()
isArray()
isPrimitive()
isAssignableFrom()
getClassLoader()
getDeclaredClasses()

#Interview Questions

Q1Which method returns the fully qualified class name?

getName().

Q2Which method returns only the class name?

getSimpleName().

Q3Which method returns the parent class?

getSuperclass().

Q4Which method checks whether a class is an interface?

isInterface().

Q5Which method returns implemented interfaces?

getInterfaces().

Q6Which method returns the class loader?

getClassLoader().

Q7Why does String.class.getClassLoader() return null?

Because String is loaded by the Bootstrap ClassLoader, which isn't exposed as a normal Java object.

Q8What is the difference between isPrimitive() for primitives and wrapper classes?

Primitive types (int, double, boolean, …) return true. Wrapper classes (Integer, Double, Boolean, …) return false.

#Key Points to Remember

  • Class provides metadata about a loaded class.
  • getName() returns the fully qualified class name; getSimpleName() only the class name.
  • getSuperclass() returns the parent class (null for Object).
  • getInterfaces() lists implemented interfaces.
  • Modifier decodes modifier flags.
  • isAssignableFrom() checks type compatibility.
  • getClassLoader() identifies which class loader loaded the class.

#Chapter Summary

The Class API is the entry point for exploring a class's metadata. It lets us inspect names, packages, inheritance, interfaces, modifiers, type characteristics and class loaders. These methods are widely used by frameworks such as Spring, Hibernate, Jackson and JUnit before creating objects, injecting dependencies or invoking methods.