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:
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:
Rank #2
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.
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.
Rank #4
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.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Best Value
| 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.
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.
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.
Quick Recap
- Handle checked lookup failures such as
ClassNotFoundException,NoSuchMethodException, andNoSuchFieldException. - 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.

