@Override
public String toString() {
return "MyObject";
}
@Deprecated
public void oldMethod() { }
@SuppressWarnings("unchecked")
public void legacyCode() { }
Topics
36
Annotations
Arrays & Multidimensional Arrays
Build Tools: Maven & Gradle
Class Loading & Bytecode Verification
Collections Framework
Concurrency & Threads
Design Patterns in Java
Enums
equals(), hashCode() & toString() Contracts
Exceptions
Functional Interfaces & Method References
Garbage Collection
Generics
I/O & NIO
Inner Classes & Anonymous Classes
Interfaces & Abstract Classes
Java Date & Time API (java.time)
Java Fundamentals: Syntax, Data Types & Operators
Java Networking & HTTP Client
Java Platform Module System (JPMS)
JDBC & Database Connectivity
JVM, JRE & Memory
Logging in Java (java.util.logging, SLF4J, Log4j)
Object Cloning & Copy Semantics
OOP & Classes
Optional & Null Safety
Pattern Matching & Switch Expressions
Records & Sealed Classes
Reflection API
Regular Expressions in Java
Serialization & Deserialization
Static & Instance Initialization Blocks
Streams & Lambdas
String Handling, StringBuilder & Immutability
Unit Testing with JUnit & Mockito
Varargs, Autoboxing & Unboxing
Annotations
15 questions found
Annotations are a form of metadata that attach information to Java code elements (classes, methods, fields, parameters) without directly affecting program logic -- they're read by the compiler, tools, or the runtime via reflection to influence behavior, such as @Override triggering a compile-time check, or @Entity marking a class for an ORM framework to map to a database table.
Real-world example
A Spring Boot application uses @RestController and @GetMapping annotations to declaratively wire up HTTP routes to Java methods, letting the framework read this metadata at startup to build the routing table without any manual registration code.
Reflection API;Java Fundamentals: Syntax
Data Types & Operators
How do you create a custom annotation in Java, and what role do @Retention and @Target play?
IntermediateA custom annotation is defined using @interface; @Retention specifies how long the annotation is retained (SOURCE, CLASS, or RUNTIME -- with RUNTIME needed if you want to read it via reflection), and @Target restricts which code elements (METHOD, TYPE, FIELD, etc.) the annotation can legally be applied to, both themselves being meta-annotations that configure your custom annotation's behavior.
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {
String value() default "";
}
public class Service {
@Loggable("critical operation")
public void process() { }
}
Real-world example
A team defines a custom @Loggable annotation with RUNTIME retention and METHOD target, letting an AOP-style interceptor read this annotation via reflection at runtime to automatically wrap annotated methods with logging behavior, without modifying each method's implementation.
Reflection API;Design Patterns in Java
How would you read custom annotations at runtime using reflection to implement annotation-driven behavior, such as a simple dependency injection or validation framework?
AdvancedUse java.lang.reflect classes (Class.getDeclaredMethods(), Method.getAnnotation(YourAnnotation.class), Field.isAnnotationPresent()) to inspect a class's structure at runtime, checking for the presence of your custom RUNTIME-retention annotation and extracting its element values to drive behavior -- this is the fundamental mechanism underlying frameworks like Spring's @Autowired or Hibernate Validator's @NotNull, where annotation metadata is read reflectively to wire dependencies or apply validation rules without generated boilerplate.
public class AnnotationProcessor {
public static void invokeLoggable(Object obj) throws Exception {
for (Method method : obj.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(Loggable.class)) {
Loggable ann = method.getAnnotation(Loggable.class);
System.out.println("Invoking logged method: " + ann.value());
method.invoke(obj);
}
}
}
}
Real-world example
A lightweight in-house DI container scans classes for a custom @Inject annotation on fields, using reflection to instantiate and set the appropriate dependency, replicating a simplified version of what Spring's @Autowired does under the hood.
Reflection API;Design Patterns in Java
What is the difference between compile-time annotation processing (via javax.annotation.processing.Processor) and runtime reflection-based annotation reading?
IntermediateCompile-time annotation processing runs during compilation itself (implementing AbstractProcessor, registered via META-INF/services), generating new source files or validating code structure before the .class files even exist -- used by tools like Lombok (generating getters/setters) or Dagger (generating dependency injection code) -- while runtime reflection reads already-compiled annotation metadata during program execution, incurring reflection's inherent performance overhead but requiring no separate build step, making compile-time processing generally preferred for performance-sensitive, code-generation scenarios.
@SupportedAnnotationTypes("com.example.GenerateBuilder")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class BuilderProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// generates new .java source files at compile time
return true;
}
}
Real-world example
Lombok's @Data annotation is processed entirely at compile time via an annotation processor, generating getter/setter/equals/hashCode methods directly into the bytecode before the JVM ever runs the program, avoiding any runtime reflection overhead that a similar reflection-based approach would incur on every method call.
Reflection API;Build Tools: Maven & Gradle
@Override tells the compiler a method is intended to override a superclass/interface method, causing a compile error if no matching method actually exists (catching typos like a mismatched method signature); @Deprecated marks an element as discouraged for use, triggering a compiler warning at call sites and signaling to other developers (and tools) that it may be removed in the future; @SuppressWarnings suppresses specific compiler warnings (like "unchecked") for the annotated element, useful when a warning is a known, deliberately accepted trade-off.
public class Base {
public void process() { }
}
public class Derived extends Base {
@Override
public void proccess() { } // typo! Compiler catches this because @Override expects an actual override
}
Real-world example
A refactor accidentally misspells an overridden method name, but because the method was marked @Override, the compiler immediately flags the error at build time rather than silently creating an unrelated new method that would have caused a confusing runtime bug.
Interfaces & Abstract Classes;OOP & Classes
How do repeatable annotations work in Java (using @Repeatable), and what problem do they solve?
AdvancedPrior to Java 8, applying the same annotation type multiple times to one element wasn't allowed; @Repeatable (introduced in Java 8) lets you define a container annotation holding an array of the repeatable annotation, allowing multiple instances of the same annotation type to be applied to a single element -- the compiler handles the wrapping/unwrapping automatically, letting reflection code retrieve either the individual repeated annotations or the container transparently via getAnnotationsByType().
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Schedules.class)
public @interface Schedule {
String day();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Schedules {
Schedule[] value();
}
public class Job {
@Schedule(day = "MONDAY")
@Schedule(day = "FRIDAY")
public void run() { }
}
Real-world example
A job scheduling framework lets developers annotate a single method with multiple @Schedule annotations (one per day it should run), using @Repeatable to allow this multi-application syntax cleanly instead of requiring an awkward single annotation with an array-typed element.
Reflection API;OOP & Classes
What are Java's meta-annotations (@Retention, @Target, @Documented, @Inherited), and what does each control?
Intermediate@Retention controls how long the annotation persists (SOURCE/CLASS/RUNTIME); @Target restricts which element types it can be applied to; @Documented causes the annotation to appear in generated Javadoc for annotated elements; @Inherited allows a class-level annotation to be automatically inherited by subclasses (only applies to annotations on classes, not methods/fields), letting a subclass be treated as if it also carries the annotation without redeclaring it.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@Documented
public @interface Auditable { }
@Auditable
public class BaseEntity { }
public class Order extends BaseEntity { } // inherits @Auditable automatically via reflection checks
Real-world example
An auditing framework marks a base entity class with a custom @Inherited @Auditable annotation, and reflection-based checks on any subclass (like Order extends BaseEntity) correctly detect the annotation as present even though Order itself never explicitly declares it.
Reflection API;OOP & Classes
How would you implement annotation-based validation (similar to Bean Validation's @NotNull, @Size) using a custom annotation and a reflection-based validator?
AdvancedDefine a custom annotation with RUNTIME retention targeting FIELD, then write a validator class that uses reflection to iterate an object's fields, checking for the presence of your validation annotations and applying the corresponding validation logic (e.g., checking a String field's length against a @Size annotation's min/max elements), collecting any violations into a result list -- this pattern is exactly how Jakarta Bean Validation (Hibernate Validator) implements its own constraint annotations under the hood.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MinLength {
int value();
}
public class Validator {
public static List<String> validate(Object obj) throws Exception {
List<String> errors = new ArrayList<>();
for (Field field : obj.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(MinLength.class)) {
field.setAccessible(true);
String value = (String) field.get(obj);
int min = field.getAnnotation(MinLength.class).value();
if (value == null || value.length() < min) {
errors.add(field.getName() + " must be at least " + min + " characters");
}
}
}
return errors;
}
}
Real-world example
A lightweight internal validation utility implements a simplified @MinLength annotation processed via reflection, mirroring (at a smaller scale) exactly how Hibernate Validator's @Size annotation works internally to enforce field-level constraints declaratively.
Reflection API;Exceptions
What is the purpose of the @FunctionalInterface annotation, and is it required for an interface to actually be treated as a functional interface?
Intermediate@FunctionalInterface is a marker/documentation annotation that instructs the compiler to verify the annotated interface has exactly one abstract method (making it eligible for lambda expression assignment) -- it's not strictly required for an interface with a single abstract method to work as a functional interface (the compiler infers this structurally regardless), but omitting it removes the safety net where the compiler would otherwise immediately flag an accidental second abstract method being added later, which would silently break lambda compatibility.
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
// Adding a second abstract method here would now cause a COMPILE ERROR, not a runtime surprise
}
Calculator add = (a, b) -> a + b;
Real-world example
A shared library interface is marked @FunctionalInterface specifically so that if a future contributor accidentally adds a second abstract method (breaking every lambda-based usage across dozens of consumers), the compiler catches this immediately at the library's own build time rather than the error surfacing confusingly at each consumer's build.
Functional Interfaces & Method References;Interfaces & Abstract Classes
How does Java's annotation processing API (javax.annotation.processing) enable compile-time code generation frameworks like Lombok or Dagger, and what are its key limitations?
AdvancedThe Processor SPI lets a library register a class implementing AbstractProcessor, invoked automatically by javac during compilation for any annotations it declares support for via @SupportedAnnotationTypes -- it can generate entirely new source files (via the Filer API) that then get compiled alongside the original code in the same compilation round, but critically, standard annotation processors cannot modify existing source files (only generate new ones), which is why Lombok's field/method injection into existing classes actually relies on a lower-level, officially unsupported compiler API hack rather than the standard processor mechanism.
@SupportedAnnotationTypes("com.example.AutoValue")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class AutoValueProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(AutoValue.class)) {
// generates a NEW source file implementing the value class, via processingEnv.getFiler()
}
return true;
}
}
Real-world example
Google's AutoValue library uses the standard annotation processing API to generate a complete new immutable implementation class alongside an abstract class marked @AutoValue, whereas Lombok (needing to inject code into the existing class itself rather than generate a new file) has to rely on unofficial compiler internals, explaining why Lombok has historically had more compiler-version compatibility issues than AutoValue.
Build Tools: Maven & Gradle;Design Patterns in Java
Showing 1–10 of 15