Annotations are a form of metadata that provide data about a program that is not part of the program itself. They start with the @ symbol and can be applied to classes, methods, variables, parameters, and even packages.
Annotations serve three distinct purposes depending on when they are "read" by the system:
Used by the compiler to detect errors or suppress warnings (e.g., @Override, @SuppressWarnings).
Software tools can process annotations to generate code, XML files, or documentation (e.g., Lombok or Hibernate).
Frameworks like Spring or JUnit use Reflection to examine annotations at runtime to change behavior.
Java provides several standard annotations that every developer should know. These are primarily used to prevent common programming mistakes.
When you create a custom annotation, you need to define its "rules" using Meta-Annotations. These define where the annotation can be used and how long it lives.
| Meta-Annotation | Purpose |
|---|---|
@Retention |
Specifies how long the annotation is kept (SOURCE, CLASS, or RUNTIME). |
@Target |
Specifies where the annotation can be applied (METHOD, FIELD, TYPE, etc.). |
@Documented |
Indicates that the annotation should be documented by javadoc. |
@Inherited |
Allows subclasses to inherit the annotation from their parent class. |
Creating your own annotation looks like defining an interface, but with the @ symbol before the interface keyword.
Annotations are "passive" until someone reads them. Frameworks use the Reflection API to look for annotations at runtime and execute logic based on them. This is how JUnit knows which methods to run when you click "Test"—it looks for the @Test annotation.
If you look at a modern Java application (Spring Boot), annotations are everywhere. They eliminate thousands of lines of code:
@RestController: Tells Spring this class handles web requests.@Entity: Tells Hibernate this class maps to a database table.@Autowired: Tells Spring to automatically inject a dependency.This "Annotation-driven development" makes code cleaner, but it also makes it "magical"—it's important to understand what the frameworks are doing under the hood via Reflection.
Before Java 8, you couldn't apply the same annotation twice to the same element. Now, using @Repeatable, you can apply multiple instances of the same metadata, which is useful for things like roles or schedules.
@Schedule(day="Mon")@Schedule(day="Fri")public void runJob() { ... }
Q: What is the difference between SOURCE and RUNTIME retention?
A: SOURCE annotations are discarded by the compiler (like @Override). RUNTIME annotations are recorded in the class file and can be read by the JVM via reflection (like @Test).
Q: Can annotations have parameters?
A: Yes. They can have elements of primitive types, String, Class, Enums, or arrays of these types. They cannot contain complex objects.
Q: Why is @Override important?
A: It catches errors where you think you are overriding a method but actually have a typo in the method name or a different parameter list (method overloading), which would lead to subtle bugs.
Java Annotations are the modern way to decouple configuration from logic. By moving metadata from XML into the code itself, Java became much more readable and maintainable. Whether you are building a small utility or a massive Spring Boot microservice, understanding how annotations work—and how to create your own—is a hallmark of a professional Java developer.
Next: Understanding Java Reflection →