Understanding Java Annotations: Concepts, Uses, Meta‑Annotations, and Custom Examples
This article explains Java annotations introduced in Java 5, covering their purpose, usage, built‑in meta‑annotations, retention policies, target elements, and provides step‑by‑step examples of defining and applying custom annotations with accompanying code and output.
Java annotations, introduced in Java 5, are a language feature that allows developers to associate metadata with program elements such as classes, methods, fields, parameters, and local variables. They do not affect program logic but can be processed by tools and frameworks at compile time or runtime.
Common uses of annotations include generating documentation (e.g., @param , @return ), configuring dependency‑injection frameworks like Dagger 2, and enabling compile‑time checks such as @Override which verifies that a method truly overrides a superclass method.
The underlying mechanism works by defining a special interface that extends java.lang.annotation.Annotation . At runtime, the JVM creates a dynamic proxy (e.g., $Proxy1 ) that implements the annotation interface. When an annotation is accessed via reflection, the proxy forwards calls to an AnnotationInvocationHandler , which retrieves values from a map called memberValues stored in the class’s constant pool.
Java provides four meta‑annotations in the java.lang.annotation package that are used to annotate other annotations:
@Documented – indicates that the annotation should be included in Javadoc.
@Retention – specifies how long the annotation is retained (SOURCE, CLASS, or RUNTIME).
@Target – defines the kinds of program elements the annotation can be applied to (e.g., CONSTRUCTOR, FIELD, METHOD, TYPE, etc.).
@Inherited – determines whether the annotation is inherited by subclasses.
Standard annotations such as @Override , @Deprecated , and @SuppressWarnings illustrate typical usage patterns.
When creating custom annotations, the following rules apply:
Define the annotation with @interface ; it implicitly extends java.lang.annotation.Annotation .
Members must be public or have default (package‑private) visibility.
Member types are limited to primitive types, String , Class , Enum , other annotations, or arrays of these.
Access annotation information at runtime via Java reflection.
Annotations may have no members, but then they provide little value.
Example custom annotations and their usage:
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Fruit name annotation
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
} import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Fruit color annotation
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitColor {
/** Color enum */
public enum Color { BLUE, RED, GREEN }
/** Color attribute */
Color fruitColor() default Color.GREEN;
} import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Fruit provider annotation
*/
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitProvider {
/** Provider ID */
public int id() default -1;
/** Provider name */
public String name() default "";
/** Provider address */
public String address() default "";
}A utility class uses reflection to read these annotations and print the associated information:
import java.lang.reflect.Field;
/** Annotation processor */
public class FruitInfoUtil {
public static void getFruitInfo(Class
clazz) {
String strFruitName = " 水果名称:";
String strFruitColor = " 水果颜色:";
String strFruitProvicer = "供应商信息:";
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(FruitName.class)) {
FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
strFruitName += fruitName.value();
System.out.println(strFruitName);
} else if (field.isAnnotationPresent(FruitColor.class)) {
FruitColor fruitColor = (FruitColor) field.getAnnotation(FruitColor.class);
strFruitColor += fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
} else if (field.isAnnotationPresent(FruitProvider.class)) {
FruitProvider fruitProvider = (FruitProvider) field.getAnnotation(FruitProvider.class);
strFruitProvicer = " 供应商编号:" + fruitProvider.id() + " 供应商名称:" + fruitProvider.name() + " 供应商地址:" + fruitProvider.address();
System.out.println(strFruitProvicer);
}
}
}
}The annotated class applies the custom annotations to its fields:
import test.FruitColor.Color;
/** Annotation usage */
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor = Color.RED)
private String appleColor;
@FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路89号红富士大厦")
private String appleProvider;
// getters, setters, and a display method omitted for brevity
}Running the utility with FruitRun produces the following output:
水果名称:Apple 水果颜色:RED 供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦
References are provided for further reading.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.