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

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

testingannotationsJavadocCustom 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

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.