Annotations
TL;DR
- Annotations (
@) are metadata added to Java code (classes, methods, variables). - They do not directly affect the execution of the code; they just provide information.
- They are consumed by the Compiler (e.g.,
@Override), Build Tools, or Runtime Frameworks (e.g., Spring’s@RestControllervia Reflection).
Concept
Before annotations, frameworks relied heavily on XML configuration files. If you wanted a class to be a web controller, you wrote an XML file linking the class name to a URL route. This was tedious and separated the configuration from the code.
Annotations bring the metadata directly into the source file.
When you write @Deprecated, the Java compiler reads that metadata and throws a warning if someone uses the method.
When you write @Entity, Hibernate reads that metadata at runtime (using Reflection) and knows to map that class to a SQL database table.
Examples
// Standard Java Annotations
@Deprecated // Tells the compiler this is old and shouldn't be used
public class LegacySystem {
// Suppresses compiler warnings about unused variables
@SuppressWarnings("unused")
private int deadVariable = 42;
// Tells the compiler to verify this method actually exists in the parent class
@Override
public String toString() {
return "Legacy";
}
}
// Framework Annotations (e.g., Spring Boot)
@RestController
@RequestMapping("/api")
public class MyController {
@GetMapping("/hello")
public String sayHello() {
return "Hello World";
}
}
Interview Questions
Q: Does an Annotation change how a method executes?
A: No. An annotation is completely passive. It is just a sticker placed on the code. If you create a @LogExecutionTime annotation and put it on a method, absolutely nothing will happen when the method runs. To make it work, you must write a completely separate piece of code (using Reflection or AOP proxies) that looks for that sticker, reads it, and executes the logging logic.
Q: What is @FunctionalInterface used for?
A: It is a built-in Java annotation applied to interfaces. It instructs the compiler to verify that the interface has exactly one abstract method. If a second abstract method is added, the compiler throws an error. It is used to ensure the interface can safely be used as a target for Lambda expressions.