Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Java class metadata is information about a type that a running program can inspect through its Class object. A Class object describes a class, interface, or other supported type; reflection methods then let you examine details such as its name, modifiers, hierarchy, members, and runtime-visible annotations.

How Java turns class-file bytes into metadata

When the JVM derives a type from class-file bytes, it constructs a corresponding Class object. That object is the runtime handle Java uses to represent classes and interfaces in a running application. The API also represents enum classes, record classes, annotation interfaces, arrays, primitive types, and void.

.class bytes → JVM loads/derives type → Class<?> object → reflection queries

Reflection is the process of obtaining that handle, calling methods on it to inspect the type, and using the returned reflective objects when appropriate. See Oracle’s Java SE API documentation and reflection tutorial.

Get a Class object in three ways

Use a class literal

When the type is known at compile time, use its .class literal:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Class<String> type = String.class;

Start from an object

For an existing object, call getClass() to get the runtime class of that object:

Object value = "hello";
Class<?> type = value.getClass();

Load a class by name

Use Class.forName when the class name is supplied dynamically:

Class<?> type = Class.forName("java.lang.String");

This form can throw the checked exception ClassNotFoundException if the named class cannot be found. For ordinary application code, prefer a class literal when possible because it avoids string-based lookup.

Inspect a type’s identity and shape

Once you have a Class<?>, query the properties relevant to your task. Names have different purposes: getName() returns the binary name, while getSimpleName() returns the source-level simple name. getCanonicalName() may be null for types without a canonical name, so do not treat it as a universal identifier.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Class<?> type = String.class;
System.out.println(type.getName());
System.out.println(type.getSimpleName());
System.out.println(type.isInterface());
System.out.println(type.isEnum());
System.out.println(type.isRecord());
System.out.println(type.isAnnotation());
System.out.println(type.isArray());
System.out.println(type.isPrimitive());

Use getModifiers() to retrieve modifier flags, then decode them with java.lang.reflect.Modifier rather than interpreting the integer directly:

int flags = type.getModifiers();
System.out.println(Modifier.toString(flags));

For ancestry, getSuperclass() returns the direct superclass, or null when there is none; getInterfaces() returns directly implemented or extended interfaces. The generic variants, getGenericSuperclass() and getGenericInterfaces(), preserve generic type information when it is available.

Inspect fields, methods, constructors, and nested types

The getDeclared* family inspects members declared directly by the target type, including non-public members. Common calls include getDeclaredFields(), getDeclaredMethods(), getDeclaredConstructors(), and getDeclaredClasses(). A member lookup such as getDeclaredMethod("trim") can throw NoSuchMethodException; field lookup can throw NoSuchFieldException.

Use getMethods() when you want public methods visible on the type, including inherited public methods. getConstructors() returns public constructors declared by the type; constructors are not inherited.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Question getDeclaredMethods() getMethods()
Which methods? Methods declared directly by the target type Public methods on the type, including inherited public methods
Which visibility? Any declared visibility, including private and protected Public methods only
Inherited methods? No; inspect a superclass or interface separately if needed Yes, from superclasses and superinterfaces
Ordering? No specified order No specified order
Generated methods? May include compiler-generated bridge and synthetic methods May include methods that are not direct source-level declarations

Do not depend on the order of reflection arrays. If you need stable output, sort the results yourself using a key such as method name and parameter types. When presenting methods as a source-level API, consider filtering methods for which Method.isSynthetic() or Method.isBridge() is true.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Read annotations at runtime

Annotations are metadata that may be applied to program declarations. To retrieve an annotation through runtime reflection, it must be retained at runtime with @Retention(RetentionPolicy.RUNTIME). An annotation retained only in source or class files is not necessarily available to runtime annotation queries.

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface Reviewed {
    String value();
}

@Reviewed("approved")
class MyType { }

Reviewed reviewed = MyType.class.getAnnotation(Reviewed.class);
if (reviewed != null) {
    System.out.println(reviewed.value());
}

Because Class implements AnnotatedElement, it provides methods including getAnnotation, getDeclaredAnnotation, getAnnotations, and getDeclaredAnnotations. In general, the getDeclared* annotation methods inspect annotations present directly on the element, whereas inherited annotation behavior depends on the annotation type’s @Inherited setting. Repeatable annotations can be queried with getAnnotationsByType or getDeclaredAnnotationsByType when you need repeatable-aware results. Consult the Java Language Specification’s annotation rules for the language-level details.

Account for access, modules, and runtime behavior

Finding a reflective member does not guarantee that your code can invoke or otherwise access it. Java visibility rules and module boundaries constrain access; reflective access changes can also be subject to runtime configuration. Do not assume that calling setAccessible(true) will bypass every restriction. Test reflective operations in the Java version and module configuration where the application will run.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For diagnostics involving where a type came from, inspect getClassLoader() and getModule(). Nesting queries answer different questions: getDeclaringClass() identifies a member’s declaring type, getEnclosingClass() identifies its lexically enclosing class where applicable, and getNestHost() identifies the nest host.

  • Handle checked lookup failures such as ClassNotFoundException, NoSuchMethodException, and NoSuchFieldException.
  • Filter bridge and synthetic methods when your output should reflect the apparent source API rather than compiler implementation details.
  • Sort reflection results explicitly if their presentation or processing requires a stable order.
  • Prefer an explicit interface or generated code when compile-time type safety matters more than runtime flexibility.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.