Topic 99 of 119 12. Java Annotations Interactive Tutorial

Java Annotations Overview: Metadata, Built-In Annotations, and Retention

9 mins read Last updated: 22 Aug 2026 32,127 views

Direct Answer & Core Concept

Java annotations provide a way to attach metadata to classes, methods, fields, parameters, constructors, and other program elements. They begin with the @ symbol and can give useful information to the compiler, development tools, frameworks, or runtime code.

You have probably already seen annotations such as @Override, @Deprecated, and @SuppressWarnings. These annotations do not normally represent executable business logic. Instead, they describe how another tool or part of the Java platform should treat a program element.

Understanding annotations is important because modern Java frameworks use them extensively for configuration, dependency injection, validation, testing, serialization, web development, and many other tasks.

Detailed Explanation & Mechanics

What Is an Annotation in Java?

An annotation is metadata attached to a program element.

For example:

Main.java
Run Online
@Overridepublic String toString() {
    return "Employee";
}

Here:

is an annotation applied to the toString() method.

The annotation tells the compiler that the method is intended to override a method from a superclass.


Why Are Annotations Used?

Annotations can provide information for different purposes.

They can be used by:

  • The Java compiler

  • Development tools

  • Build tools

  • Annotation processors

  • Testing frameworks

  • Runtime frameworks

  • Libraries

  • Application code

For example:

Main.java
Run Online
@Deprecatedpublic void oldMethod() {
}

The annotation communicates that the method is deprecated.

An IDE can then display a warning when another part of the program uses it.


Annotation Syntax

The basic syntax starts with @ followed by the annotation name.

An annotation can also contain values:

Main.java
Run Online
@SuppressWarnings("unchecked")

Multiple values can be specified:

Main.java
Run Online
@SomeAnnotation( name = "Employee", version = 1)

The exact values allowed depend on how the annotation type is defined.


Annotations Are Metadata

It is useful to think of annotations as information attached to code.

For example:

Main.java
Run Online
Class | +-- Annotation | +-- Fields | +-- Methods | +-- Constructors

The annotation itself does not automatically execute application logic.

Another component may inspect or process the annotation and take some action based on its presence or values.


Common Built-In Java Annotations

Java provides several important built-in annotations.

Some commonly encountered annotations include:

  • @Override

  • @Deprecated

  • @SuppressWarnings

  • @SafeVarargs

  • @FunctionalInterface

There are also meta-annotations used to define how annotations behave, such as:

  • @Retention

  • @Target

  • @Documented

  • @Inherited

  • @Repeatable


The @Override Annotation

@Override indicates that a method is intended to override a method declared by a superclass or interface.

Example:

Main.java
Run Online
class Animal {
    void sound() {
        System.out.println("Animal sound");
    }
}class Dog extends Animal {
    @Override void sound() {
        System.out.println("Dog barks");
    }
}

The annotation helps the compiler detect mistakes.

For example, suppose you accidentally change the method name:

Main.java
Run Online
class Dog extends Animal {
    @Override void sounds() {
        System.out.println("Dog barks");
    }
}

The compiler can report an error because sounds() does not override the expected method.

Without @Override, the mistake could instead result in an accidental overload or unrelated method.


Why @Override Is Useful

Consider:

Main.java
Run Online
@Overridepublic String toString() {
    return "Employee";
}

The annotation communicates the programmer's intention:

Main.java
Run Online
"This method should override an inherited method."

It therefore provides both documentation and compile-time checking.

A good practice is to use @Override whenever a method is intentionally overriding another method.


The @Deprecated Annotation

@Deprecated indicates that a program element should generally no longer be used for new code.

Example:

Main.java
Run Online
@Deprecatedpublic void oldMethod() {
    System.out.println("Old implementation");
}

When another part of the program calls it:

the compiler or IDE can produce a deprecation warning.


@Deprecated With since and forRemoval

Modern Java also supports additional information with @Deprecated.

For example:

Main.java
Run Online
@Deprecated( since = "2.0", forRemoval = true)public void oldMethod() {
}

This communicates that the API was deprecated starting with a particular version and may be intended for removal.

The annotation itself does not automatically delete the method.

It provides metadata that tools and developers can use.


The @SuppressWarnings Annotation

@SuppressWarnings tells the compiler that certain warnings should be suppressed for the annotated program element.

For example:

Main.java
Run Online
@SuppressWarnings("unchecked")public void process() {
    // code that produces an unchecked warning
}

It can also be applied to a local variable, method, class, or other supported target.

Another example:

Main.java
Run Online
@SuppressWarnings( {
    "unchecked", "rawtypes"
})

Multiple warning categories can be specified.


Use @SuppressWarnings Carefully

Suppressing warnings does not fix the underlying problem.

For example:

Main.java
Run Online
@SuppressWarnings("unchecked")

does not make an unsafe operation safe.

It simply tells the compiler not to report that particular warning in the applicable scope.

A good practice is to keep the suppression as narrow as reasonably possible.


The @SafeVarargs Annotation

@SafeVarargs can be used on certain methods or constructors involving variable-arity parameters when the implementation is known to be safe with respect to heap pollution concerns.

For example:

Main.java
Run Online
@SafeVarargsstatic <T> void printItems(T... items) {
    for (T item : items) {
        System.out.println(item);
    }
}

It is intended for cases where the programmer can guarantee that the variable-arity operation does not perform unsafe operations that would violate the intended type safety.

This annotation should not be used simply to hide warnings without understanding why the operation is safe.


The @FunctionalInterface Annotation

@FunctionalInterface indicates that an interface is intended to be a functional interface.

A functional interface has one abstract method, although it can also contain methods that do not count as abstract methods, such as appropriate default or static methods.

Example:

Main.java
Run Online
@FunctionalInterfaceinterface Calculator {
    int calculate(int a, int b);
}

The annotation allows the compiler to verify the intended structure.

For example, this would violate the functional-interface requirement:

Main.java
Run Online
@FunctionalInterfaceinterface Calculator {
    int calculate(int a, int b);
    int multiply(int a, int b);
}

The compiler can report an error because the interface has more than one abstract method.


Annotation Values

Annotations can contain elements, which are similar to parameters.

For example:

Main.java
Run Online
@SuppressWarnings("unchecked")

The value is supplied to the annotation's value element.

Some annotations use named elements:

Main.java
Run Online
@Example( name = "Java", version = 1)

Annotation element values must be compatible with the types allowed by the annotation declaration.


Default Annotation Values

An annotation element can define a default value.

Example:

Main.java
Run Online
@interface Info {
    String author() default "Unknown";
    int version() default 1;
}

Now the annotation can be used without specifying every element:

Main.java
Run Online
@Infoclass Application {
}

The defaults are used for omitted elements.

You can also override them:

Main.java
Run Online
@Info( author = "John", version = 2)class Application {
}

What Is a Custom Annotation?

Java allows developers to define their own annotation types.

The basic syntax uses @interface.

Example:

Main.java
Run Online
@interface Author {
    String name();
}

Now it can be applied like this:

Main.java
Run Online
@Author(name = "John")class Book {
}

The custom annotation describes metadata associated with the Book class.


Custom Annotation Example

Let's create a simple annotation for identifying application components.

Main.java
Run Online
@interface Component {
    String name();
}

Use it:

Main.java
Run Online
@Component(name = "PaymentService")class PaymentService {
}

The annotation itself does not automatically create or register the service.

Some other code, framework, or annotation processor would need to inspect the annotation and implement that behavior.

This distinction is important:

Defining an annotation does not automatically give it behavior.


Annotation Interfaces

An annotation declaration looks unusual because it uses:

rather than:

For example:

Main.java
Run Online
@interface Author {
    String name();
}

The result is an annotation type.

Annotations can define elements, but those elements have restrictions on their allowed types.

Common annotation element types include:

  • primitive types

  • String

  • Class

  • enum types

  • annotation types

  • arrays of these types


Using Class in an Annotation

An annotation can contain a Class value.

Example:

Main.java
Run Online
@interface Handler {
    Class<?> type();
}

Usage:

Main.java
Run Online
@Handler(type = String.class)class TextHandler {
}

This can be useful when metadata needs to refer to a particular Java type.


Annotation Arrays

Annotation elements can also be arrays.

Example:

Main.java
Run Online
@interface Roles {
    String[] value();
}

Usage:

Main.java
Run Online
@Roles( {
    "ADMIN", "USER"
})class Account {
}

You can also explicitly write:

Main.java
Run Online
@Roles(value = {
    "ADMIN", "USER"
})class Account {
}

The @Retention Annotation

One of the most important annotation concepts is retention.

@Retention determines how long an annotation should be retained.

The three main retention policies are:

These determine whether annotation information is available at different stages.


RetentionPolicy.SOURCE

SOURCE means the annotation is available in source code but is not retained in the compiled class file.

Example:

Main.java
Run Online
@Retention(RetentionPolicy.SOURCE)@interface MyAnnotation {
}

The annotation can be used while compiling source code, but it is not stored in the resulting .class file.

This can be useful for annotations processed only during compilation.


RetentionPolicy.CLASS

CLASS means the annotation is recorded in the class file but is not necessarily available through normal runtime reflection.

Example:

Main.java
Run Online
@Retention(RetentionPolicy.CLASS)@interface MyAnnotation {
}

CLASS is the default retention policy when no @Retention annotation is specified.

This makes the annotation information available in compiled class files for tools that inspect them, while ordinary runtime reflection does not expose it as a runtime annotation.


RetentionPolicy.RUNTIME

RUNTIME means the annotation is retained in the class file and available at runtime through reflection.

Example:

Main.java
Run Online
@Retention(RetentionPolicy.RUNTIME)@interface MyAnnotation {
    String value();
}

Now code can inspect it at runtime.

For example:

Main.java
Run Online
@MyAnnotation("Java")class Example {
}

Reflection can access the annotation:

Main.java
Run Online
MyAnnotation annotation = Example.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.value());

This is one reason runtime annotations are heavily used by frameworks.


Retention Policy Comparison

RetentionSource CodeClass FileRuntime Reflection
SOURCEYesNoNo
CLASSYesYesNot normally available
RUNTIMEYesYesYes

A simple mental model is:

Main.java
Run Online
SOURCE↓Source onlyCLASS↓Source + .class fileRUNTIME↓Source + .class file + runtime reflection

The @Target Annotation

@Target specifies where an annotation can be used.

For example:

Main.java
Run Online
@Target(ElementType.METHOD)@interface Loggable {
}

This annotation can be applied to methods.

Main.java
Run Online
@Loggablepublic void save() {
}

But it cannot be freely applied to every kind of program element.


Common Element Types

ElementType includes targets such as:

  • TYPE

  • FIELD

  • METHOD

  • PARAMETER

  • CONSTRUCTOR

  • LOCAL_VARIABLE

  • PACKAGE

  • MODULE

  • TYPE_PARAMETER

  • TYPE_USE

For example:

Main.java
Run Online
@Target(ElementType.TYPE)@interface Entity {
}

This indicates that the annotation is intended for types such as classes or interfaces.


Combining @Target and @Retention

A custom annotation often specifies both.

Main.java
Run Online
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@interface Loggable {
}

Now the annotation:

  • can be applied to methods

  • remains available at runtime

Example:

Main.java
Run Online
class UserService {
    @Loggable public void createUser() {
        System.out.println("Creating user");
    }
}

A framework can inspect the method and discover the annotation at runtime.


Reading Runtime Annotations With Reflection

Consider:

Main.java
Run Online
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@interface Component {
    String value();
}

Apply it:

Main.java
Run Online
@Component("PaymentService")class PaymentService {
}

Now inspect it:

Main.java
Run Online
public class Main {
    public static void main(String[] args) {
        Component annotation = PaymentService.class .getAnnotation(Component.class);
        if (annotation != null) {
            System.out.println( annotation.value());
        }
    }
}

Possible output:

The important part is that the annotation uses:

Without runtime retention, ordinary reflection cannot retrieve it as a runtime annotation.


The @Documented Annotation

@Documented indicates that an annotation should be included in generated API documentation when the documentation tool supports it.

Example:

Main.java
Run Online
@Documented@Retention(RetentionPolicy.RUNTIME)@interface ApiInfo {
    String value();
}

It is mainly useful for annotations intended to be part of a public API's documentation.


The @Inherited Annotation

@Inherited affects annotation inheritance for class annotations.

Example:

Main.java
Run Online
@Inherited@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@interface Role {
    String value();
}

Apply it to a superclass:

Main.java
Run Online
@Role("ADMIN")class Parent {
}

A subclass can inherit the annotation when queried through the class-level reflection mechanism that supports @Inherited:

Main.java
Run Online
class Child extends Parent {
}

However, @Inherited does not mean that annotations are automatically inherited in every context.

In particular, it applies to class annotations and does not provide general inheritance behavior for methods or fields.


The @Repeatable Annotation

@Repeatable allows the same annotation type to be applied multiple times to the same declaration.

For example:

Main.java
Run Online
@Repeatable(Roles.class)@interface Role {
    String value();
}

Define the container annotation:

Main.java
Run Online
@interface Roles {
    Role[] value();
}

Now you can write:

Main.java
Run Online
@Role("ADMIN")@Role("USER")class Account {
}

This is useful when a program element needs multiple metadata entries of the same annotation type.


Meta-Annotations

Annotations that describe other annotations are called meta-annotations.

Important examples include:

Main.java
Run Online
@Retention@Target@Documented@Inherited@Repeatable

For example:

Main.java
Run Online
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@interface TestMethod {
}

Here:

  • @Retention describes how long @TestMethod is retained.

  • @Target describes where @TestMethod can be used.


Annotations and Compilation

Annotations can be useful during compilation.

Some tools and annotation processors inspect annotations while source code is being compiled.

For example, an annotation could mark a class:

Main.java
Run Online
@GenerateCodeclass User {
}

An annotation processor could detect the annotation and generate additional source code or other compiler-time artifacts.

This is one reason annotations are important in modern Java development.


Annotations and Runtime Reflection

Runtime annotations can also be inspected after the application starts.

For example:

Main.java
Run Online
@Retention(RetentionPolicy.RUNTIME)@interface Version {
    int value();
}

Use:

Main.java
Run Online
@Version(2)class Application {
}

Read:

Main.java
Run Online
Version version = Application.class .getAnnotation(Version.class);
System.out.println(version.value());

Output:

Frameworks can use this technique to discover configuration metadata dynamically.


Annotations Do Not Automatically Execute Code

Consider:

Main.java
Run Online
@MyAnnotationclass Example {
}

The annotation does not automatically call a method.

There must be something that interprets it.

For example:

Main.java
Run Online
Annotation↓Compiler / Processor / Framework / Reflection↓Action

This is a critical concept for beginners.

Annotations provide metadata; another component gives that metadata meaning.


Annotations in Frameworks

Annotations are widely used by Java frameworks.

For example, frameworks can use annotations to describe:

  • components

  • configuration

  • routes

  • database entities

  • validation rules

  • dependency injection

  • test methods

  • serialization rules

The framework reads the metadata and performs the appropriate operation.

This is why understanding annotations is useful even if you do not create custom annotations yourself.


Common Built-In Annotation Summary

AnnotationMain Purpose
@OverrideIndicates an overriding method
@DeprecatedMarks an API as deprecated
@SuppressWarningsSuppresses selected compiler warnings
@SafeVarargsIndicates safe handling of certain varargs operations
@FunctionalInterfaceIndicates an intended functional interface
@RetentionDefines annotation retention
@TargetDefines valid annotation targets
@DocumentedIncludes annotation information in generated documentation
@InheritedEnables inheritance of certain class annotations
@RepeatableAllows repeated use of an annotation

Common Mistakes With Annotations

Mistake 1: Thinking Every Annotation Runs Code

Annotations are metadata.

They require a compiler, processor, framework, or runtime code to interpret them.


Mistake 2: Choosing the Wrong Retention

If an annotation needs runtime reflection, use:

Main.java
Run Online
@Retention(RetentionPolicy.RUNTIME)

A SOURCE annotation will not be available at runtime.


Mistake 3: Forgetting @Target

If an annotation should only apply to methods, make that restriction explicit:

Main.java
Run Online
@Target(ElementType.METHOD)

This helps catch incorrect usage.


Mistake 4: Suppressing Warnings Without Understanding Them

Avoid using:

Main.java
Run Online
@SuppressWarnings("unchecked")

simply to make warnings disappear.

Understand the warning first and keep suppression as narrow as practical.


Mistake 5: Assuming @Inherited Applies Everywhere

@Inherited applies to class-level annotation inheritance through the relevant reflection behavior. It does not automatically make method or field annotations inherit through subclasses.


Best Practices for Java Annotations

Use standard annotations when they already solve the problem

For example:

is preferable to creating your own annotation for the same purpose.

Keep custom annotations focused

An annotation should communicate one clear piece of metadata or configuration.

Choose retention deliberately

Ask:

Main.java
Run Online
Does this annotation need to exist only in source?Does a tool need it in the class file?Does runtime code need to inspect it?

Then select the appropriate retention policy.

Restrict annotation targets

Use @Target when the annotation has specific valid locations.

Document public annotations

If an annotation is part of a public API, @Documented may be appropriate.


Frequently Asked Questions

What is an annotation in Java?

An annotation is metadata attached to a Java program element. It can provide information to the compiler, tools, annotation processors, frameworks, or runtime code.

What does @Override do?

It tells the compiler that a method is intended to override an inherited method and allows the compiler to detect certain mistakes.

What is @Deprecated used for?

It marks an API element as deprecated so developers and tools can be warned that it should generally no longer be used for new code.

What does @SuppressWarnings do?

It suppresses specified compiler warnings within its applicable scope. It does not fix the underlying issue.

What are annotation retention policies?

The main policies are SOURCE, CLASS, and RUNTIME. They determine how long annotation information is retained.

Which retention policy allows reflection?

RetentionPolicy.RUNTIME allows an annotation to be available through runtime reflection.

What is @Target?

@Target specifies the kinds of program elements where an annotation can be used.

What is a custom annotation?

A custom annotation is an annotation type created by the developer using syntax such as:

Main.java
Run Online
@interface MyAnnotation {
}

What is a meta-annotation?

A meta-annotation is an annotation used to describe another annotation type, such as @Target or @Retention.

Do annotations execute code?

Not by themselves. A compiler, annotation processor, framework, tool, or application code must interpret the annotation and perform some action.

What is @FunctionalInterface?

It indicates that an interface is intended to have one abstract method and allows the compiler to verify that intention.

What is @Repeatable?

It allows an annotation type to be applied multiple times to the same declaration.

Suggested Graphic

Create a visual diagram showing how an annotation moves through the Java development lifecycle:

Main.java
Run Online
JAVA ANNOTATION | +--------+--------+ | | Built-in Custom | | @Override @MyAnnotation @Deprecated | @FunctionalInterface | +--------+--------+ | @Retention | +--------------+--------------+ | | | SOURCE CLASS RUNTIME | | | Source .class file Reflection | Framework

The graphic should emphasize the three retention levels and show that annotations provide metadata that other tools or runtime code can interpret.

Runnable Code Example: Java Annotations Overview

Java 17+
Main.java
Run Online
public class AnnotationsOverviewDemo {
    @Deprecated(since = "2.0", forRemoval = true) public static void legacyMethod() {
        System.out.println("Deprecated method called");
    } @SuppressWarnings("deprecation") public static void main(String[] args) {
        legacyMethod();
        System.out.println("Annotations provide powerful compile-time and runtime metadata!");
    }
}

Common Java Technical Interview Questions & Answers

Interview Prep
Q1

What are the three RetentionPolicy options in Java?

SOURCE (discarded during compilation), CLASS (kept in bytecode, stripped at runtime), and RUNTIME (accessible via reflection at runtime).
Q2

What does the @FunctionalInterface annotation do?

It instructs the compiler to verify that the annotated interface has exactly one abstract method.

Key Takeaways, Best Practices & Clean Code Rules

Java Best Practice & Production Rules

Use @Override on every overridden method and @Deprecated(forRemoval=true) when phasing out obsolete public APIs.