Skip to content

Annotations in Java

Annotations are metadata added to Java code. They do not directly change the normal logic of a program, but they give extra information to the compiler, tools, frameworks, or runtime.

Annotations start with @.

@Override
public String toString() {
return "Student object";
}

Here, @Override tells the compiler that this method should override a method from a parent class.


Why Use Annotations?

Annotations help Java code become more expressive and tool-friendly.

Common uses:

  • Ask the compiler to check something
  • Mark old code as deprecated
  • Reduce configuration in frameworks
  • Describe tests, routes, database entities, or validations
  • Generate code or documentation using tools

Frameworks like Spring, JUnit, Hibernate, and Jakarta EE use annotations heavily.


Built-in Java Annotations

Java provides several built-in annotations.

AnnotationPurpose
@OverrideConfirms that a method overrides a parent method
@DeprecatedMarks code as old or not recommended
@SuppressWarningsTells the compiler to hide specific warnings
@FunctionalInterfaceEnsures an interface has exactly one abstract method
@SafeVarargsSuppresses warnings for safe generic varargs usage

@Override

Use @Override when a method is intended to override a method from a parent class or interface.

class Animal {
void sound() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Bark");
}
}

If the method name or parameters are wrong, the compiler will catch the mistake.

class Dog extends Animal {
@Override
void sounds() {
System.out.println("Bark");
}
}

This causes a compile-time error because sounds() does not override sound().


@Deprecated

Use @Deprecated when code still exists but should no longer be used.

class Calculator {
@Deprecated
int addOld(int a, int b) {
return a + b;
}
int add(int a, int b) {
return a + b;
}
}

When another developer uses addOld(), the compiler or IDE can show a warning.

You can also provide more detail:

@Deprecated(since = "2.0", forRemoval = true)
void oldMethod() {
System.out.println("Use newMethod() instead.");
}

@SuppressWarnings

Use @SuppressWarnings when you intentionally want to hide a compiler warning.

@SuppressWarnings("unchecked")
public void printItems() {
List list = new ArrayList();
list.add("Java");
}

Use this carefully. It should not be used to hide warnings that should actually be fixed.


@FunctionalInterface

Use @FunctionalInterface to confirm that an interface has exactly one abstract method.

@FunctionalInterface
interface Greeting {
void sayHello(String name);
}

This is useful when writing interfaces meant for lambda expressions.

Greeting greeting = name -> System.out.println("Hello " + name);
greeting.sayHello("Priya");

Creating a Custom Annotation

Use @interface to create a custom annotation.

public @interface Author {
String name();
String date();
}

Use it like this:

@Author(name = "Saurabh", date = "2026-07-28")
class JavaGuide {
void show() {
System.out.println("Annotations guide");
}
}

The values inside an annotation are called elements.


Default Values

Annotation elements can have default values.

public @interface Version {
int value() default 1;
}

Now the annotation can be used with or without a value.

@Version
class FirstGuide {}
@Version(2)
class SecondGuide {}

When an annotation has only one element named value, Java lets you pass the value directly.


Meta-Annotations

Annotations that describe other annotations are called meta-annotations.

Meta-AnnotationPurpose
@TargetDefines where an annotation can be used
@RetentionDefines how long an annotation is kept
@DocumentedIncludes the annotation in generated documentation
@InheritedAllows subclasses to inherit the annotation
@RepeatableAllows the same annotation to be used more than once

@Target

@Target controls where an annotation is allowed.

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
public @interface RunOnce {
}

Now @RunOnce can only be used on methods.

Common targets:

  • ElementType.TYPE - class, interface, or enum
  • ElementType.FIELD - field
  • ElementType.METHOD - method
  • ElementType.PARAMETER - method parameter
  • ElementType.CONSTRUCTOR - constructor

@Retention

@Retention controls how long an annotation is available.

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface TaskInfo {
String value();
}

Retention policies:

PolicyMeaning
SOURCEAvailable only in source code, removed during compilation
CLASSStored in the .class file but usually not available at runtime
RUNTIMEAvailable at runtime using reflection

Use RUNTIME when a framework or program needs to inspect the annotation while the application is running.


Reading Annotations with Reflection

Runtime annotations can be read using reflection.

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@interface Route {
String path();
}
@Route(path = "/users")
class UserController {
}
public class Main {
public static void main(String[] args) {
Class<UserController> clazz = UserController.class;
if (clazz.isAnnotationPresent(Route.class)) {
Route route = clazz.getAnnotation(Route.class);
System.out.println(route.path());
}
}
}

Output:

/users

This is the same basic idea used by many Java frameworks.


Real-World Examples

JUnit

@Test
void shouldAddTwoNumbers() {
assertEquals(4, 2 + 2);
}

@Test marks a method as a test case.

Spring

@RestController
class UserController {
@GetMapping("/users")
List<String> getUsers() {
return List.of("Amit", "Priya");
}
}

Spring reads these annotations to create web routes.

Hibernate

@Entity
class Student {
@Id
private Long id;
}

Hibernate reads these annotations to map Java classes to database tables.


Best Practices

  • Use built-in annotations like @Override consistently.
  • Keep custom annotations simple and focused.
  • Choose @Retention(RetentionPolicy.RUNTIME) only when runtime access is needed.
  • Use @Target to prevent annotations from being used in the wrong place.
  • Do not overuse annotations when normal Java code is clearer.

Key Points

  • Annotations provide metadata for code.
  • Built-in annotations help the compiler and developers catch issues.
  • Custom annotations are created using @interface.
  • Meta-annotations like @Target and @Retention control annotation behavior.
  • Runtime annotations can be inspected using reflection.