Java注解詳解


注解

注解概述

  • JDK5.0開始,Java增加了對元數據(MetaData)的支持,也就是Annotation(注解)

  • Annotation其實就是代碼里的特殊標記,這些標記可以在編譯,類加載運行時被讀取,並執行相應的處理。通過使用Annotation,程序員可以在不改變原有邏輯的情況下,在源文件中嵌入一些補充信息。代碼分析工具、開發工具和部署工具可以通過這些補充信息進行驗證或者進行部署。

  • Annotation可以像修飾符一樣被使用,可用於修飾包,類,構造器,方法,成員變量,參數,局部變量的聲明,這些信息被保存在Annotation的"name=value"對中。

  • 在JavaSE中,注解的使用目的比較簡單,例如標記過時的功能,忽略警告等。在JavaEE/Android中注解占據了更重要的角色,例如用來配置應用程序的任何切面,代替JavaEEI舊版中所遺留的繁冗代碼和XML配置等。

  • 未來的開發模式都是基於注解的,JPA是基於注解的,Spring2.5以上都是基於注解的,Hibernate3.x以后也是基於注解的,現在的Struts2有一部分也是基於注解的了,注解是一種趨勢,一定程度上可以說:框架=注解+反射+設計模式。

常見注解示例

  • 使用Annotation時要在其前面增加@符號,並把該Annotation當成一個修飾符使用。用於修飾它支持的程序元素

示例一:生成文檔相關的注解

  • @author標明開發該類模塊的作者,多個作者之間使用,分割
  • @version標明該類模塊的版本
  • @see參考轉向,也就是相關主題
  • @since從哪個版本開始增加的
  • @param對方法中某參數的說明,如果沒有參數就不能寫
  • @return對方法返回值的說明,如果方法的返回值類型是void就不能寫
  • @exception對方法可能拋出的異常進行說明,如果方法沒有用throws顯式拋出的異常就不能寫
    其中.
  • @param @return和@exception這三個標記都是只用於方法的。
  • @param的格式要求: @param 形參名形參類型形參說明
  • @return的格式要求: @return 返回值類型返回值說明
  • @exception的格式要求: @exception 異常類型異常說明
  • @param和@exception可以並列多個

示例二:在編譯時進行格式檢查JDK內置的三個基本注解)

  • @Override:限定重寫父類方法,該注解只能用於方法
  • @Deprecated:用於表示所修飾的元素(類,方法等)已過時。通常是因為所修飾的結構危險或存在更好的選擇
  • @SuppressWarnings:抑制編譯器警告

示例三:跟蹤代碼依賴性,實現替代配置文件功能

image-20210131153932149

image-20210131154026702

JDK注解代碼示例

@Override:重寫父類方法

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void eat(){
        System.out.println("人吃飯");
    }

    public void walk(){
        System.out.println("人走路");
    }

}

interface Info{
    void show();
}

class Student extends Person implements  Info{
    @Override
    public void show() {
        
    }

    @Override
    public void eat() {
        super.eat();
    }

    @Override
    public void walk() {
        super.walk();
    }
}

編譯器會為我們檢查該方法是否是實現接口方法或者是重寫父類的方法,如果我們故意寫一個方法不是重寫方法而加上注解,會給出一個提示警告

image-20210131155054609

@SuppressWarnings:抑制編譯器警告

@SuppressWarnings("unused")
int num=10;

自定義注解

  • 定義新的Annotation類型使用@interface關鍵字

  • 自定義注解自動繼承了java.lang.annotation.Annotation接口

  • Annotatiodn 的成員變量在Annotation 定義中以無參數方法的形式來聲明。其方法名和返回值定了該成員的名字和類型。我們稱為配置參數。類型只能是八種基本數據類型、String類型、Class類型、enum類型、Annotation 類型
    以上所有類型的數組。

  • 可以在定義Annotation的成員變量時為其指定初始值,指定成員變量的初始值可使用default關鍵字

    • 如果只有一個參數成員,建議使用參數名為value

    • 如果定義的注解含有配置參數,那么使用時必須指定參數值,除非它有默認值。格式是“參數名=參數值”,如果只有一個參數成員,且名稱為value,可以省略“value=”

  • 沒有成員定義的Annotation稱為標記;包含成員變量的Annotation 稱為元數據Annotation注意:自定義注解必須配上注解的信息處理流程才有意義。

IDEA中的定義操作

  1. IDEA中new ->選擇注解類型

image-20210131155820212

  1. 生成如下的模板
package com.dreamcold.annotions;

public @interface MyAnnotation {
}

定義示例

標注:

  • 注解聲明為: @interface
  • 內部定義成員,通常使用value表示
  • 可以指定成員的默認值,使用default定義
  • 如果自定義注解沒有成員,表明是一個標識作用。
  • 如果注解有成員,在使用注解時,需要指明成員的值
  • 自定義注解必須配上注解的信息處理流程(使用反射)才有意義。

MyAnnotation.java

package com.dreamcold.annotions;

public @interface MyAnnotation {
    String value();
}

Person.java

@MyAnnotation(value = "hello")
class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void eat(){
        System.out.println("人吃飯");
    }

    public void walk(){
        System.out.println("人走路");
    }

}

這種情況下一定要給注解傳一個值,否則就會報錯

image-20210131161028555

這種情況下我們可以給對應屬性一個默認值,使用default關鍵字:

package com.dreamcold.annotions;

public @interface MyAnnotation {
    String value() default "hello";
}

jdk提供的4種元注解

  • JDK的元Annotation用於修飾其他Annotation定義
  • JDK5.0提供了4個標准的meta annotation類型, 分別是:
    • Retention
    • Target
    • Documented
    • Inherited
  • 元數據的理解:
    • String name = "dreamcold";

Retention

@Retention:只能用於修飾一個Annotation定義,如於指定該Annotation的生命周期,@Rentention包含- - 個RetentionPolicy 類型的成員變量,使用@Rentention時必須為該value成員變量指定值:

  • RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),編譯器直接丟 棄這種策略的注釋.
  • RetentionPolicy.CLASS:在class文件中有效(即class保留),當運行Java程序時,JVM不會保留注解。這是默認值,不指定的話默認是它
  • RetentionPolicy.RUNTIME:在運行時有效(即運行時保留) , 當運行Java程序時, JVM會保留注釋。程序可以通過反射獲取該注釋。只有聲明為RUNTIME生命周期的注解,才能通過反射獲取。

image-20210131163853364

比如以@SuppressWarnings:抑制編譯器警告為例:

image-20210131164113978

我們再次點進去SOURCE:我們發現實際上是一個枚舉類:

image-20210131164240708

注釋告訴我們注解將在被編譯器丟棄,也就是在javac命令將.java文件編譯成為.class后,.class文件中不會保留該注解,其他值類型效果如上。

Target

自定義注解通過都會指明兩個元注解: Retention、 Target

@Target:用於修飾Annotation定義,用於指定被修飾的Annotation能用於修飾哪些程序元素。@Target 也包含-一個名為value的成員變量。

image-20210131165123755

比如以@SuppressWarnings:抑制編譯器警告為例:

image-20210131165206348

@Target中的類型是一個枚舉類,這些枚舉類如下:其枚舉類一些該注解可以寫在的位置比如屬性、構造器、變量等等

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

我們之前自己定義的注解什么都沒有加,所以其可以在任何地方標注,我們可以給其加上一些限制:

package com.dreamcold.annotions;

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

//我們的注解可以修飾方法
@Target({ElementType.METHOD})
public @interface MyAnnotation {
    String value() default "hello";
}

這時候我們用我們的注解修飾一個變量,就會報錯:會提示我們的注解不能修飾變量!

image-20210131165811801

兩種不常用的元注解

  • @Documented:用於指定被該元Annotation修飾的Annotation類將被
    javadoc工具提取成文檔。默認情況下,javadpc是不包括注解的。
    • 定義為Documented的注解必須設置Retention值為RUNTIME.
    • 表示所修飾的注解在被javadoc解析時, 保留下來。
  • @Inherited:被它修飾的Annotation將具有繼承性。如果某個類使用了被
    • @Inherited修飾的Annotation,則其子類將自動具有該注解。
    • 比如:如果把標有@Inherited注解的自定義的注解標注在類級別上,子類則可以
      繼承父類類級別的注解
    • 實際應用中,使用較少

JDK8中的注解新特性

可重復注解

什么是可重復注解呢?

image-20210131222256034

我們想給一個類Person類加上兩個重復的注解,比如兩個重復的@MyAnnotation,但是這時候IDEA會報錯:

image-20210131222434435

也就是注解重復了,我們要想實現這樣的效果,在JDK8之前是按照這樣實現的:

MyAnnotation

package com.dreamcold.annotions;

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

@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation {
    String value() default "hello";
}

MyAnnotations.java

package com.dreamcold.annotions;

public @interface MyAnnotations {
    MyAnnotation[] value();
}

Person.java

@MyAnnotations({@MyAnnotation,@MyAnnotation})
class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void eat(){
        System.out.println("人吃飯");
    }

    public void walk(){
        System.out.println("人走路");
    }

}

JDK8之后的寫法:

MyAnnotation.java

package com.dreamcold.annotions;

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

@Repeatable(MyAnnotations.class)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation {
    String value() default "hello";
}

MyAnnotations.java

package com.dreamcold.annotions;

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

@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotations {
    MyAnnotation[] value();
}
  1. 注意要注意@Repeatable的生命周期不可以小於注解的生命周期
  2. 注意MyAnnotation可以標注注解的位置,MyAnnotations可以標注注解的位置一定要包括MyAnnotation可以標注注解的位置

Person.java

@MyAnnotation
@MyAnnotation
class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void eat(){
        System.out.println("人吃飯");
    }

    public void walk(){
        System.out.println("人走路");
    }

}

總結

  • MyAnnotation.上聲明@Repeatable, 成員值為MyAnnotations.class

  • MyAnnotation的Target Retent ion和MyAnnotations相同

類型注解

在JDK1.8中ElementType中新增了兩種類型:

image-20210131224442565

類型注解:

  • JDK1.8之后,關於元注解@ Target的參數類型ElementType枚舉值多了兩個:TYPE_PARAMETER,TYPEUSE
  • 在Java8之前,注解只能是在聲明的地方所使用,Java8開始,注解可以應用.在任何地方。
    • ElementType.TYPE_PARAMETER 表示該注解能寫在類型變量的聲明語句中(如:泛型聲明)
    • ElementType.TYPE_USE 表示該注解能寫在使用類型的任何語句中

image-20210131224728589

示例一

MyAnnotation.java

package com.dreamcold.annotions;

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

@Repeatable(MyAnnotations.class)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.TYPE_PARAMETER})
public @interface MyAnnotation {
    String value() default "hello";
}

Generic.java

package com.dreamcold.annotions;

public class Generic <@ MyAnnotation T>{
}

示例二:加上ElementType.TYPE_USE在哪里都可以標注

MyAnnotation.java

package com.dreamcold.annotions;

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

@Repeatable(MyAnnotations.class)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.TYPE_PARAMETER,ElementType.TYPE_USE})
public @interface MyAnnotation {
    String value() default "hello";
}

Generic.java

package com.dreamcold.annotions;

import java.util.ArrayList;

public class Generic <@ MyAnnotation T>{
    public void show() throws @MyAnnotation RuntimeException{
        ArrayList<@MyAnnotation String> list=new ArrayList<>();
        int num=(@MyAnnotation int)10L;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM