Advanced Java August 30 ,2025

Introduction to Annotations in Java

What are Annotations?

Annotations in Java are a form of metadata that provide additional information about the program to the compiler, JVM, or frameworks. They do not directly affect the execution of the code but can influence how tools and libraries process the code.

Think of annotations as labels or markers attached to code elements (classes, methods, variables, parameters, packages, etc.) to give extra instructions.

Key Points about Annotations

  1. Metadata, Not Code

    • Annotations add information about the code, not the code itself.
    • For example:
    @Override  
    public String toString() {  
        return "Example";  
    }
    

    Here, @Override tells the compiler this method is overriding a parent class method.

  2. Introduced in Java 5
    1. They were introduced to reduce boilerplate code and provide better integration with frameworks and tools.
  3. Where Can Annotations Be Used?

    1. Classes
    2. Methods
    3. Variables/fields
    4. Parameters
    5. Constructors
    6. Packages

    Example:

    @Deprecated
    public void oldMethod() {
        System.out.println("This method is deprecated.");
    }
    
  4. Types of Annotations
    1. Standard Annotations – Provided by Java (@Override, @Deprecated, @SuppressWarnings).
    2. Meta-Annotations – Annotations used on annotations (@Retention, @Target).
    3. Custom Annotations – User-defined annotations for specific needs.
  5. Why Are They Useful?
    1. Compile-time checking → @Override prevents errors when overriding methods.
    2. Code documentation → @Deprecated helps developers avoid outdated APIs.
    3. Framework integration → Frameworks like Spring, Hibernate, JUnit, etc., use annotations for configuration (@Entity, @Autowired, @Test).
    4. Runtime processing → Using reflection, annotations can be read at runtime and drive application behavior.

Simple Example

public class Demo {
    
    @Override
    public String toString() {
        return "Demo Class";
    }

    @Deprecated
    public void oldMethod() {
        System.out.println("This method should not be used.");
    }

    @SuppressWarnings("unchecked")
    public void useList() {
        List list = new ArrayList();
        list.add("Hello");
    }
}
  • @Override → ensures correct overriding.
  • @Deprecated → warns not to use this method.
  • @SuppressWarnings → tells compiler to ignore warnings.

History and Need for Annotations in Java

1. Before Annotations (Pre-Java 5 era)

  • Prior to Java 5 (2004), developers relied on comments, naming conventions, marker interfaces, and external XML configuration files to provide metadata about code.
  • Example:
    • To indicate a method overrides another, developers just wrote comments:
// This method overrides parent class method
public String toString() {  
    return "Example";  
}
  • To indicate special behavior, frameworks like EJB, Servlets, Hibernate, Spring used XML configuration files.

Problems:

  • Comments were not checked by compiler → prone to errors.
  • XML files became large, complex, and hard to maintain.
  • Marker interfaces (like Serializable, Cloneable) had limited flexibility.
  • Code and configuration were scattered (harder to read & maintain).

2. Introduction of Annotations in Java 5

  • With Java SE 5, annotations were formally introduced as part of JSR 175 (Metadata Facility for the Java Programming Language).
  • Purpose: To make metadata first-class citizens in Java.
  • Annotations allowed developers to embed metadata directly into the code instead of maintaining it separately in XML or comments.
  • Compiler, JVM, and frameworks could process annotations at compile time or runtime.

3. Why Annotations Were Needed

  • Code Clarity: Instead of writing comments, you could use compiler-checked annotations.
    Example:

    @Override
    public String toString() { ... }
    

    → Compiler ensures you’re overriding a method correctly.

  • Reduced Boilerplate: XML configurations shrank drastically.
    Example: In Spring:


became:

@Service
public class MyService { ... }
  • Framework Integration: Frameworks like Hibernate, JPA, Spring, JUnit 4+ started using annotations to configure entities, dependency injection, testing, etc.
  • Compile-time & Runtime Processing:
    Tools and frameworks could read annotations via reflection and automate behaviors.

4. Evolution After Java 5

  • Java 6: Introduced annotation processing API (APT).
  • Java 7: Added more annotations (@SafeVarargs).
  • Java 8: Introduced repeatable annotations and annotations for type use (JSR 308).
  • Java 9+: Continued enhancements for modular applications.

Quick Example – Before vs After

Before (XML-based Hibernate mapping):


    
    

After (Annotation-based JPA):

@Entity
@Table(name="EMP")
public class Employee {
    @Id
    @Column(name="EMP_ID")
    private int id;

    @Column(name="EMP_NAME")
    private String name;
}

Difference between annotations, comments, and metadata

1. Comments

  • Definition: Notes written in the source code to make it understandable for humans.
  • Purpose: Explain the code for developers.
  • Effect on Program: Ignored by compiler & JVM (no effect on execution).
  • Types:
    • Single-line (//)
    • Multi-line (/* ... */)
    • Documentation comments (/** ... */)

Example:

// This method calculates area of circle
public double area(double r) {
    return Math.PI * r * r;
}

Compiler ignores the comment, it’s only for the programmer.

2. Metadata

  • Definition: Data about data. In Java context, metadata means information about the program, such as class names, methods, variables, access modifiers, etc.
  • Purpose: Helps tools, compilers, and frameworks understand program structure.
  • Effect on Program: Stored in .class file by the compiler, used by JVM & frameworks (through reflection).
  • Examples in Java:
    • Class information (class name, methods, fields).
    • Method parameter types.
    • Package and module information.

Example:

public class Employee {
    private String name; // metadata: field name & type
}

Here, name and its type (String) are part of metadata.

3. Annotations

  • Definition: A special type of metadata introduced in Java 5, written using @, that provides instructions to compiler, JVM, and frameworks.
  • Purpose: Add extra behavioral information directly in the code.
  • Effect on Program: Can be checked at compile-time or runtime depending on @Retention policy.
  • Examples:
    • @Override (compile-time check)
    • @Entity (runtime for frameworks like Hibernate)
    • @Test (used by JUnit for testing)

Example:

@Override
public String toString() {
    return "Employee Class";
}

Tabular Difference

FeatureCommentsMetadataAnnotations
DefinitionNotes for developersInformation about program structureSpecial metadata using @ syntax
IntroducedFrom Java 1.0Implicit in Java since beginningJava 5 (JSR 175)
UsageExplain code (human-readable)Describe classes, fields, methods, etc.Give instructions to compiler/frameworks
Compiler EffectIgnoredStored in .class filesCan be processed at compile-time/runtime
Examples//, /* */, /** */Method names, field types, modifiers@Override, @Deprecated, @Entity
Role of annotations in simplifying development

Annotations play a huge role in reducing boilerplate code, improving readability, and integrating smoothly with frameworks. They act like shortcuts that tell the compiler, JVM, or frameworks how a piece of code should behave, without writing extra configuration files or lengthy code.

1. Reducing Boilerplate Code

  • Before annotations, developers had to write long XML configuration files (especially in frameworks like Spring, Hibernate, and EJB).
  • Annotations allow configuration to be written directly in code.
  • Simplifies development → less code, easier to maintain.

Example (Spring Bean):



// After (Annotation based)
@Service
public class MyService { }

2. Improving Code Readability

  • Annotations act like labels in code.
  • They make the purpose of methods, classes, and variables clear at a glance.

Example:

@Test   // clearly shows it's a test method
public void checkLogin() { ... }

3. Providing Compile-Time Checks

  • Some annotations help catch errors early during compilation.
  • Example:
@Override
public void toStrings() { } // ERROR: no method "toStrings()" in parent
  • Saves time by preventing logical mistakes.

4. Enabling Dependency Injection & Inversion of Control

  • Frameworks like Spring use annotations (@Autowired, @Component) to automatically inject dependencies.
  • Developers don’t need to manually create and manage objects.

Example:

@Autowired
private UserService userService;  // Automatically injected

5. Simplifying Persistence & Database Mapping

  • JPA/Hibernate uses annotations (@Entity, @Table, @Column) to map Java objects to database tables.
  • No need for large mapping files.

Example:

@Entity
@Table(name="EMPLOYEES")
public class Employee {
    @Id
    private int id;

    @Column(name="EMP_NAME")
    private String name;
}

6. Supporting Cross-Cutting Concerns (AOP)

  • In frameworks like Spring, annotations handle concerns like security, logging, and transactions.
  • Example:
@Transactional
public void saveData() { ... }

7. Enhancing Testing

  • Testing frameworks (JUnit, TestNG) use annotations (@Test, @Before, @After) to simplify test case writing.
  • No need to inherit base classes or use special naming conventions.

Example:

@Before
public void setup() { ... }

@Test
public void testLogin() { ... }

8. Enabling Runtime Processing & Framework Automation

  • Frameworks can use reflection to read annotations at runtime and automate behaviors (like REST APIs, security, serialization).
  • Example (Spring Boot REST API):
@RestController
public class UserController {

    @GetMapping("/users")
    public List getUsers() { ... }
}

Here, @RestController and @GetMapping automatically expose methods as web APIs.

Annotations simplify development by:

  • Removing boilerplate XML/configuration.
  • Improving readability with inline metadata.
  • Providing compile-time error checks.
  • Enabling dependency injection and ORM.
  • Streamlining testing and API development.

Built-in Annotations in Java (java.lang package)

Java provides some basic annotations in the java.lang package, mainly for compiler-level instructions. These are used very frequently in day-to-day coding.

1. @Override

  • Definition: Indicates that a method is intended to override a method from its superclass.
  • Retention Policy: SOURCE (checked only at compile time, not available at runtime).
  • Purpose:
    • Prevents mistakes when overriding methods.
    • Helps the compiler catch errors if the method signature doesn’t match a parent method.

Example (Correct Usage):

class Parent {
    void display() { 
        System.out.println("Parent class method"); 
    }
}

class Child extends Parent {
    @Override
    void display() {   // correctly overrides
        System.out.println("Child class method");
    }
}

Example (Incorrect Usage):

class Child extends Parent {
    @Override
    void displays() {  // ERROR: no method "displays()" in parent
        System.out.println("Wrong method name");
    }
}

Compiler will give an error because the method doesn’t actually override anything.

2. @Deprecated

  • Definition: Marks a method, class, or field as deprecated, meaning it should no longer be used.
  • Retention Policy: RUNTIME (still visible at runtime via reflection).
  • Purpose:
    • Warns developers that a method/class is outdated and may be removed in future versions.
    • Helps maintain backward compatibility while encouraging migration to newer APIs.

Example:

class Demo {
    @Deprecated
    void oldMethod() {
        System.out.println("This method is deprecated!");
    }

    void newMethod() {
        System.out.println("Use this method instead.");
    }
}

public class Test {
    public static void main(String[] args) {
        Demo d = new Demo();
        d.oldMethod(); // COMPILER WARNING: deprecated method used
    }
}

3. @SuppressWarnings

  • Definition: Tells the compiler to ignore specific warnings that it would normally generate.
  • Retention Policy: SOURCE (used only by compiler).
  • Purpose:
    • Useful when you know the warning is safe to ignore.
    • Common warnings: "unchecked", "deprecation", "rawtypes".
    • Should be used carefully → ignoring warnings may hide real issues.

Example:

import java.util.*;

public class Test {
    @SuppressWarnings("unchecked")
    public void addData() {
        List list = new ArrayList();  // raw type warning ignored
        list.add("Hello");
        list.add("World");
    }
}

Without @SuppressWarnings, the compiler would warn about unchecked operations (because we used raw types without generics).

AnnotationPackageRetentionPurposeExample Use
@Overridejava.langSOURCEEnsures method overrides parent class method correctlyMethod overriding
@Deprecatedjava.langRUNTIMEMarks APIs as outdated, warns developers to use alternativeOld APIs
@SuppressWarningsjava.langSOURCESuppresses compiler warnings like unchecked, deprecation, rawtypesIgnoring warnings

Meta-Annotations in Java (java.lang.annotation Package)

Meta-annotations are special annotations defined in the java.lang.annotation package.
They are not applied directly on code but on other annotations.

👉 Their purpose is to define rules and behavior for custom annotations — such as:

  • How long they should be retained.
  • Where they can be applied.
  • Whether they are inherited.
  • Whether they can be repeated.
  • Whether they should appear in Javadoc.

Without meta-annotations, custom annotations would have no scope or lifecycle.

The main meta-annotations are:

1. @Retention

Specifies how long annotations are retained in the program lifecycle.

RetentionPolicy values:

  • SOURCE → Discarded during compilation, not present in .class file. (e.g., @Override, @SuppressWarnings)
  • CLASS → Stored in .class file but not available at runtime (default).
  • RUNTIME → Stored in .class file and available at runtime via Reflection. (used in frameworks like Spring, Hibernate, JUnit)

Example:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)  // annotation retained at runtime
@interface MyAnnotation {
    String value();
}

public class RetentionExample {
    @MyAnnotation("Hello Retention")
    public void display() {
        System.out.println("Method executed!");
    }

    public static void main(String[] args) throws Exception {
        MyAnnotation ann = RetentionExample.class
                .getMethod("display")
                .getAnnotation(MyAnnotation.class);
        System.out.println("Annotation Value: " + ann.value());
    }
}

Output:

Annotation Value: Hello Retention

2. @Target

Specifies where an annotation can be applied in the program.

Common ElementType values:

  • TYPE → class, interface, enum
  • METHOD → methods
  • FIELD → variables
  • PARAMETER → method parameters
  • CONSTRUCTOR → constructors
  • LOCAL_VARIABLE → local variables
  • ANNOTATION_TYPE → annotations themselves
  • PACKAGE → package declaration

Example:

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@interface MyTargetAnnotation {
    String info();
}

@MyTargetAnnotation(info = "Applied on Class")
public class TargetExample {

    @MyTargetAnnotation(info = "Applied on Method")
    public void show() {
        System.out.println("Target annotation example");
    }
}

3. @Documented

Indicates that an annotation should appear in the Javadoc of the annotated element.
Without it, annotations are ignored by Javadoc.

Example:

import java.lang.annotation.Documented;

@Documented
@interface API {
    String version();
    String developer();
}

@API(version = "1.0", developer = "Dr. Muskan Biyani")
public class DocumentedExample {
    public void show() {
        System.out.println("This is documented annotation example");
    }
}

👉 When Javadoc is generated, the @API annotation will be visible.

4. @Inherited

Allows subclasses to inherit annotations from their superclass.

  • Works only for class-level annotations.
  • Does not apply to methods or fields.

Example:

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface ParentAnnotation {
    String value();
}

@ParentAnnotation("SuperClass Annotation")
class Parent {}

class Child extends Parent {}

public class InheritedExample {
    public static void main(String[] args) {
        ParentAnnotation annotation = Child.class.getAnnotation(ParentAnnotation.class);
        System.out.println("Inherited Annotation: " + annotation.value());
    }
}

Output:

Inherited Annotation: SuperClass Annotation

5. @Repeatable (Java 8+)

Allows the same annotation to be used multiple times on the same element.

Example:

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Authors.class)
@interface Author {
    String name();
}

// Container annotation
@Retention(RetentionPolicy.RUNTIME)
@interface Authors {
    Author[] value();
}

@Author(name = "Muskan")
@Author(name = "Varun")
public class RepeatableExample {
    public static void main(String[] args) {
        Author[] authors = RepeatableExample.class.getAnnotationsByType(Author.class);
        for (Author a : authors) {
            System.out.println("Author: " + a.name());
        }
    }
}

Output:

Author: Muskan
Author: Varun

🔹 Summary Table: Meta-Annotations in Java

Meta-AnnotationPurposeExample
@RetentionDefines how long annotation is retained (SOURCE, CLASS, RUNTIME)@Retention(RetentionPolicy.RUNTIME)
@TargetDefines where annotation can be applied@Target({ElementType.METHOD, ElementType.TYPE})
@DocumentedMakes annotation appear in Javadoc@Documented
@InheritedAllows subclass to inherit superclass annotations@Inherited on class-level annotation
@RepeatableAllows multiple instances of the same annotation@Author("A") @Author("B")

✅ Now everything is merged under one clean section → “Meta-Annotations in Java”.
No duplication, clear structure, examples + outputs, and a quick reference table.

4. Custom Annotations

Java not only provides built-in annotations but also allows developers to create user-defined annotations to make code more readable, reusable, and meaningful for frameworks, tools, or custom processing.

Creating User-defined Annotations

A custom annotation is defined using the @interface keyword instead of class or interface.

Syntax:

@interface AnnotationName {
    // elements (methods)
}
  • An annotation definition looks similar to an interface but uses @interface.
  • Methods inside annotations are called elements or members.
  • Annotations cannot extend classes or interfaces, but they can extend java.lang.annotation.Annotation implicitly.

Rules for Custom Annotations

  1. Defined with @interface.
  2. Elements must be abstract methods without parameters.
  3. Elements can have default values.
  4. Return types of elements are restricted to:
    • Primitives (int, float, boolean, etc.)
    • String
    • Class
    • Enums
    • Other annotations
    • Arrays of the above types
  5. No throws clause is allowed in annotation methods.

Defining Elements/Parameters in Annotations

Example:

@interface MyAnnotation {
    String author();
    String date();
    int revision() default 1;   // with default value
}
  • Here author and date must be provided whenever annotation is used.
  • revision is optional since it has a default value.

Default Values in Annotations

  • Use default keyword to specify a value.
  • If no value is provided, default will be applied.

Example:

@interface Info {
    String createdBy() default "Admin";
    String version() default "1.0";
}

Examples of Custom Annotations

1. Simple Custom Annotation

@interface MyAnnotation {
    String value();  // element
}

public class Demo {
    @MyAnnotation(value = "Hello World")
    public void sayHello() {
        System.out.println("Hello Annotation!");
    }
}
2. Annotation with Multiple Elements
@interface Developer {
    String name();
    String date();
    String[] reviewers() default {}; // array element
}

public class Project {
    @Developer(
        name = "John Doe",
        date = "30-08-2025",
        reviewers = {"Alice", "Bob"}
    )
    public void module() {
        System.out.println("Module developed!");
    }
}

3. Annotation with Default Values

@interface BugReport {
    String assignedTo() default "Unassigned";
    int severity() default 1;
}

public class Software {
    @BugReport(severity = 5)
    public void feature() {
        System.out.println("Critical bug found!");
    }
}

4. Using Custom Annotation with Reflection

Annotations are useful when combined with reflection to read metadata at runtime.

import java.lang.annotation.*;
import java.lang.reflect.*;

@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
    String message();
}

class Task {
    @Todo(message = "Implement login feature")
    public void login() {}
}

public class Main {
    public static void main(String[] args) throws Exception {
        Method m = Task.class.getMethod("login");
        Todo todo = m.getAnnotation(Todo.class);
        System.out.println("TODO: " + todo.message());
    }
}

Output:

TODO: Implement login feature

 Summary

Meta-annotations in Java (defined in the java.lang.annotation package) are used to control how, where, and how long custom annotations are applied. They provide essential rules like retention policy, applicable targets, Javadoc visibility, inheritance, and repeatable usage. These meta-annotations form the foundation of Java’s annotation mechanism and are heavily used in frameworks like Spring, Hibernate, and JUnit.

👉 To be continued in the next part, where we’ll explore built-in annotations in Java (like @Override, @Deprecated, @SuppressWarnings) and see how they are applied in real-world scenarios.

 

Next Blog- Annotations in Java (Part-2)

Sanjiv
0

You must logged in to post comments.

Get In Touch

123 Street, New York, USA

+012 345 67890

techiefreak87@gmail.com

© Design & Developed by HW Infotech