Fundamentals 11 min read

Understanding Java Annotations: Concepts, Built‑in Annotations, Custom Annotations, and a Simple Test Framework

This article explains the purpose and syntax of Java annotations, describes built‑in annotations such as @Override, @Deprecated and @SuppressWarnings, shows how to create and use custom annotations with meta‑annotations, and demonstrates a lightweight testing framework that records annotation‑driven test failures.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Understanding Java Annotations: Concepts, Built‑in Annotations, Custom Annotations, and a Simple Test Framework

Concept

Concept: describes the program for the computer.

Comment: textual description for developers.

Definition: An annotation (metadata) introduced in JDK 1.5, can be placed on packages, classes, fields, methods, local variables, parameters, etc., to provide additional information.

Purpose

Purpose categories:

Documentation generation: generate docs from annotated code.

Code analysis: use reflection to analyze code based on annotations.

Compilation checks: enable compiler checks such as @Override.

Predefined Annotations in JDK

@Override: checks whether the annotated method overrides a method from a superclass or interface.

@Deprecated: marks the annotated element as outdated.

@SuppressWarnings: suppresses compiler warnings, e.g., @SuppressWarnings("all") .

Annotation Documentation Generation Example

Example API class for generating Javadoc:

/**
 * 注解javadoc演示
 *
 * @author zjq
 * @version 1.0
 * @since 1.5
 */
public class AnnoDoc {
    /**
     * 计算两数的和
     * @param a 整数
     * @param b 整数
     * @return 两数的和
     */
    public int add(int a, int b) {
        return a + b;
    }
}

Run the following command in the class directory:

javadoc AnnoDoc.java

After execution, many HTML and JS files are generated; opening index.html shows the documentation.

Custom Annotations

Format

Meta‑annotation example:

public @interface 注解名称{
    属性列表;
}

Essence

A custom annotation is essentially an interface that implicitly extends Annotation :

public interface MyAnno extends java.lang.annotation.Annotation {}

Attributes: Abstract Methods in Interface

Requirements:

Attribute return types can be primitive, String, enum, annotation, or arrays of these types.

If a default value is provided, the attribute can be omitted when using the annotation.

If there is a single attribute named value , the name can be omitted.

Array values are enclosed in {} ; braces can be omitted for a single element.

Example

public @interface MyAnno {
    int value();
    Person per();
    MyAnno2 anno2();
    String[] strs();
}

public enum Person {
    P1, P2;
}

Usage:

@MyAnno(value=12, per=Person.P1, anno2=@MyAnno2, strs="bbb")
public class Worker {}

Meta‑annotations

@Target : specifies where the annotation can be applied (TYPE, METHOD, FIELD, etc.).

@Retention : defines the retention policy, e.g., @Retention(RetentionPolicy.RUNTIME) keeps the annotation in the bytecode for runtime reflection.

@Documented : indicates whether the annotation should appear in Javadoc.

@Inherited : determines if the annotation is inherited by subclasses.

Using Annotations for Reflection

Previously, configuration files were read to create objects and invoke methods. The following code shows the traditional approach:

// Load properties file
Properties pro = new Properties();
InputStream is = ReflectTest.class.getClassLoader().getResourceAsStream("pro.properties");
pro.load(is);
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
// Load class, create instance, invoke method
Class cls = Class.forName(className);
Object obj = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(obj);

We can replace this with an annotation:

/**
 * 描述需要执行的类名,和方法名
 * @author zjq
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
    String className();
    String methodName();
}

Parsing the annotation:

@Pro(className = "com.zjq.javabase.base25.annotation.Demo1", methodName = "show")
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        // Get annotation
        Pro an = ReflectTest.class.getAnnotation(Pro.class);
        String className = an.className();
        String methodName = an.methodName();
        // Load class, create object, invoke method
        Class cls = Class.forName(className);
        Object obj = cls.newInstance();
        Method method = cls.getMethod(methodName);
        method.invoke(obj);
    }
}

Example: Simple Test Framework Using Custom Annotation

Define a test annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {}

Define a calculator class with methods annotated by @Check :

/**
 * 小明定义的计算器类
 * @author zjq
 */
public class Calculator {
    @Check
    public void add() {
        String str = null;
        str.toString(); // will cause NullPointerException
        System.out.println("1 + 0 =" + (1 + 0));
    }
    @Check
    public void sub() { System.out.println("1 - 0 =" + (1 - 0)); }
    @Check
    public void mul() { System.out.println("1 * 0 =" + (1 * 0)); }
    @Check
    public void div() { System.out.println("1 / 0 =" + (1 / 0)); }
    public void show() { System.out.println("永无bug..."); }
}

Test framework that runs all @Check methods and records failures to bug.txt :

/**
 * 简单的测试框架
 * 当主方法执行后,会自动检测所有标有Check注解的方法,记录异常到文件中
 * @author zjq
 */
public class TestCheck {
    public static void main(String[] args) throws IOException {
        Calculator c = new Calculator();
        Class cls = c.getClass();
        Method[] methods = cls.getMethods();
        int number = 0;
        BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
        for (Method method : methods) {
            if (method.isAnnotationPresent(Check.class)) {
                try {
                    method.invoke(c);
                } catch (Exception e) {
                    number++;
                    bw.write(method.getName() + " 方法出异常了");
                    bw.newLine();
                    bw.write("异常的名称:" + e.getCause().getClass().getSimpleName());
                    bw.newLine();
                    bw.write("异常的原因:" + e.getCause().getMessage());
                    bw.newLine();
                    bw.write("--------------------------");
                    bw.newLine();
                }
            }
        }
        bw.write("本次测试一共出现 " + number + " 次异常");
        bw.flush();
        bw.close();
    }
}

Resulting bug.txt content after execution:

add 方法出异常了

异常的名称:NullPointerException

异常的原因:null

--------------------------

div 方法出异常了

异常的名称:ArithmeticException

异常的原因:/ by zero

--------------------------

本次测试一共出现 2 次异常

Summary

Most of the time we use built‑in annotations rather than creating custom ones.

Annotations are not part of the program logic; they act as metadata tags.

Who uses annotations?

Compilers

Runtime processing tools

JavaTestingreflectionAnnotationsJavadoccustom annotations
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.