最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解Java中的注解Annotation

 更新時間:2023年10月16日 10:29:20   作者:發(fā)光吖  
這篇文章主要介紹了深入理解Java中的注解Annotation,注解在Java中確實也很常見,但是人們常常不會自己定義一個注解拿來用,我們雖然很少去自定義注解,但是學會注解的寫法,注解的定義,學會利用反射解析注解中的信息,在開發(fā)中能夠使用到,這是很關(guān)鍵的,需要的朋友可以參考下

Java中的注解(Annotation)

單詞Annotation可翻譯為:注釋、注解。

單詞Comment可翻譯為:評論、議論、解釋。

在Java當中,Comment充當注釋的含義,Annotation充當注解的含義。

注解和注釋有啥區(qū)別呢?

Java中有單行注釋、多行注釋、文檔注釋。

注釋Comment

  • 單行注釋 //
  • 多行注釋 /* */
  • 文檔注釋 /** */

這些注釋都是在編譯以后不會出現(xiàn)在字節(jié)碼中的,僅僅是存在于java源文件中給程序員看的,它們對于程序的執(zhí)行沒有任何影響。

而注解就不同了,它是代碼級別的,是會編譯到class字節(jié)碼當中,對程序的運行產(chǎn)生影響或者對編譯產(chǎn)生影響。

注解嚴格意義上來說就是Java中的類成員,它和屬性、方法、構(gòu)造方法是一樣的級別。

初學注解

注解在Java中確實也很常見,但是人們常常不會自己定義一個注解拿來用。我們雖然很少去自定義注解,但是學會注解的寫法,注解的定義,學會利用反射解析注解中的信息,在開發(fā)中能夠使用到,這是很關(guān)鍵的。

代碼運行離不開一些配置信息的支撐,有些配置信息我們選擇保存在文件中(.xml .properties),再利用IO流的技術(shù)讀取這些配置信息去使用,它們都是配置和代碼分離的形式,這種方式的好處是低耦合,代碼已經(jīng)打包壓縮好了不用動,而配置信息修改起來很方便,通過修改配置,可以讓代碼完成不同的任務(wù),這就很方便。不好的地方在于:開發(fā)人員寫的代碼和配置不在一個文件中,開發(fā)過程中翻看就不是很方便。相當于開發(fā)麻煩了,維護卻簡單了。

隨著開發(fā)的越來越多,人們發(fā)現(xiàn)文件中的一些配置,是寫到那里很少去更改的,沒有人會輕易修改那些重要的配置信息,這些信息就像被寫死了一樣,所以這些信息就可以用注解寫,寫到代碼里去,因為注解和代碼是內(nèi)聚在一起的,開發(fā)過程就很方便??偨Y(jié)來說是各有優(yōu)劣吧。

1.注解的寫法

@XXX [(一些信息)]

這是使用注解的寫法,使用注解前必須先定義注解,我們可以使用像@Override這樣的注解,那是因為這些注解在Java中已經(jīng)寫好了,我們直接拿來用就好(下面會講到如何自定義注解)。

注解中的[一些信息]可能存在,也可能不存在,這需要看注解的定義者是如何定義該注解的。

2.注解放置在哪里

注解可以放置在:類的上面、屬性上面、方法上面、構(gòu)造方法上面、局部變量上面、參數(shù)前面。

注解能夠放置在哪里,這也需要看注解的定義者是如何定義該注解的。

3.注解的作用

  • 用來充當注釋的作用(僅僅是一個文字的說明) 例如:@Deprecated
  • 用來做代碼的檢測(驗證) 例如:@Override
  • 可以攜帶一些信息(內(nèi)容) 就類似于:文件.properties .xml 的作用

4.Java中有一些寫好的注解供我們使用

  • @Deprecated 用來說明方法是廢棄的
  • @Override 用來做代碼檢測,檢測此方法是否是一個重寫方法,如果不是,該注解會報錯
  • @SuppressWarnings({"","",""}) 它里面的參數(shù)是一個String類型的數(shù)組,如果數(shù)組內(nèi)的元素只有一個長度 ,則大括號{}可以省略,該注解中可以定義的有意義參數(shù)值包括:
    • unused:變量定義后未被使用
    • serial:類實現(xiàn)了序列化接口 不添加序列化ID號
    • rawtypes:集合沒有定義泛型
    • deprecation:方法已經(jīng)廢棄
    • *unchecked:出現(xiàn)了泛型的問題 可以不檢測
    • all:包含了以上所有(不推薦)

5.注解中可以攜帶信息,也可以不攜帶

注意:注解信息不能隨意寫,注解信息的類型只能是如下的類型:

  • 基本數(shù)據(jù)類型
  • String類型
  • 枚舉類型enum
  • 注解類型@
  • 數(shù)組類型[],數(shù)組的內(nèi)部只能存儲如上的四種類型

基本數(shù)據(jù)類型String類型枚舉類型enum注解類型@數(shù)組類型[],數(shù)組的內(nèi)部只能存儲如上的四種類型

自定義一個注解類型

通過@interface 定義一個新的注解類型

public @interface MyAnnatation {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->}
  • 可以發(fā)現(xiàn)注解的寫法與接口非常相似(可以利用接口的特點來記憶注解)
  • 可以描述public static final的屬性 比較少見可以描述public abstract的方法 方法要求必須有返回值 返回值類型必須是如上那些(基本數(shù)據(jù)類型、String類型、枚舉類型、注解類型、數(shù)組類型)
public @interface MyAnnatation {
	int NUM = 9;//注解中寫屬性,很少見
	String test();//方法要求必須有返回值
}

注解元素的默認值

注解元素必須有確定的值,要么在定義注解的默認值中指定,要么在使用注解時指定,非基本類型的注解元素的值不可為null。因此, 使用空字符串或0作為默認值是一種常用的做法。這個約束使得處理器很難表現(xiàn)一個元素的存在或缺失的狀態(tài),因為每個注解的聲明中,所有元素都存在,并且都具有相應的值,為了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負數(shù),一次表示某個元素不存在,在定義注解時,這已經(jīng)成為一個習慣用法。

元注解

我們自己定義的注解如果想要拿來使用,光定義還不夠 ,還需要做很多細致的說明(需要利用Java提供好的注解來說明)

這就需要使用到元注解(也是注解 不是拿來使用的 是用來說明注解的):

@Target

@Target說明了Annotation所修飾的對象范圍:Annotation可被用于 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構(gòu)造方法、成員變量、枚舉值)、方法參數(shù)和本地變量(如循環(huán)變量、catch參數(shù))。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。

作用:

用于描述注解的使用范圍(即:被描述的注解可以用在什么地方)

取值(ElementType)有:

  • CONSTRUCTOR:用于描述構(gòu)造器
  • FIELD:用于描述域
  • LOCAL_VARIABLE:用于描述局部變量
  • METHOD:用于描述方法
  • PACKAGE:用于描述包
  • PARAMETER:用于描述參數(shù)
  • TYPE:用于描述類、接口(包括注解類型) 或enum聲明

更詳細的@Target定義可參照下表:

Target類型描述
ElementType.TYPE應用于類、接口(包括注解類型)、枚舉
ElementType.FIELD應用于屬性(包括枚舉中的常量)
ElementType.METHOD應用于方法
ElementType.PARAMETER應用于方法的形參
ElementType.CONSTRUCTOR應用于構(gòu)造函數(shù)
ElementType.LOCAL_VARIABLE應用于局部變量
ElementType.ANNOTATION_TYPE應用于注解類型
ElementType.PACKAGE應用于包
ElementType.TYPE_PARAMETER1.8版本新增,應用于類型變量)
ElementType.TYPE_USE1.8版本新增,應用于任何使用類型的語句中(例如聲明語句、泛型和強制轉(zhuǎn)換語句中的類型)

@Retention

  • 描述當前的這個注解存在什么作用域中
  • 注解存在的三種作用域:SOURCE、CLASS、RUNTIME
  • 對應于:源代碼文件—>編譯—>字節(jié)碼文件—>加載—>內(nèi)存執(zhí)行

@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現(xiàn)在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀?。ㄕ堊⒁獠⒉挥绊慶lass的執(zhí)行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。

作用:表示需要在什么級別保存該注釋信息,用于描述注解的生命周期(即:被描述的注解在什么范圍內(nèi)有效)

取值(RetentionPoicy)有:

  • SOURCE:在源文件中有效(即源文件保留)
  • CLASS:在class文件中有效(即class保留)
  • RUNTIME:在運行時有效(即運行時保留)
生命周期類型描述
RetentionPolicy.SOURCE編譯時被丟棄,不包含在類文件中
RetentionPolicy.CLASSJVM加載時被丟棄,包含在類文件中,默認值
RetentionPolicy.RUNTIME由JVM 加載,包含在類文件中,在運行時可以被獲取到

Retention meta-annotation類型有唯一的value作為成員,它的取值來自java.lang.annotation.RetentionPolicy的枚舉類型值。

@Inherited

描述當前這個注解是否能被子類對象繼承(不太常用)

@Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation將被用于該class的子類。

注意:@Inherited annotation類型是被標注過的class的子類所繼承。類并不從它所實現(xiàn)的接口繼承annotation,方法并不從它所重載的方法繼承annotation。

當@Inherited annotation類型標注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發(fā)現(xiàn)指定的annotation類型被發(fā)現(xiàn),或者到達類繼承結(jié)構(gòu)的頂層。

@Document

描述這個注解是否能被Javadoc 或類似的工具文檔化(不常用)

自己使用自己描述的注解

自定義一個注解:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;

@Target({METHOD,CONSTRUCTOR,FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String[] value();//方法不是做事情 為了攜帶信息 搬運給該注解的解析者使用
    //按道理講 注解定義者肯定和注解解析者是同一個人,而注解的使用者,它們無需定義和解析注解
    /*方法名剛好是value,并且只有一個方法,使用該注解的時候就可以不指定方法名*/
}

使用自己的注解:

由注解的定義可知,該注解可以放置在方法、構(gòu)造方法、屬性上。

作用范圍是運行時RUNTIME

public class Person {
    @MyAnnotation("TOM")
    private String name;
}

問題1. 在注解里面描述了一個方法,方法沒有參數(shù),方法有返回值String[]

使用注解的時候,讓我們傳遞參數(shù),如何理解該過程?

可以理解為:注解的方法做事,把我們傳遞給它的參數(shù)搬運走了,給了別人,別人解析這些注解中的參數(shù),做相應的處理

問題2. 使用別人寫好的注解不用寫方法名,我們自己定義的方法必須寫名字*

  • 如果我們自己定義的注解 只有一個方法 方法名字叫value
  • 在使用的時候就可以省略方法名
  • 如果傳遞的信息是一個數(shù)組 數(shù)組內(nèi)只有一個元素 可以省略數(shù)組大括號{}
  • 如果方法是兩個以上 每一個方法都要使用 并且每一個方法必須寫名字

舉例:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;

@Target({METHOD,FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface YouAnnotation {
    String [] value();
    int count();
    double price();
}
public class Student {
    //注解中有3個方法,則3個方法都要使用到,并且要指明方法名
    //如果數(shù)組中只有一個數(shù)據(jù),則{}可以省略
    @YouAnnotation(value = "qa",count = 8,price = 9.9)
    private String name;
}

如何解析注解內(nèi)攜帶的信息(反射機制)

解析注解其實也很簡單,它的思路是:先看該注解聲明在了哪里,比如,一個屬性name上面聲明了一個注解,那么就利用反射,先找到這個類,然后找到這個屬性name,根據(jù)這個屬性,調(diào)用getAnnotation()方法,得到這個注解,然后調(diào)用這個注解對象的getClass方法獲取注解的類型,用注解的類型,調(diào)用getMethod(methodName)方法,根據(jù)方法名獲取注解中的方法,然后,這個方法對象調(diào)用 invoke方法去執(zhí)行方法,方法的參數(shù)是那個annotation對象,方法的返回值就是這個注解中攜帶的信息了。

public class Demo {
    public static void main(String[] args) {
        Class<?> clazz = Person.class;
        try {
            Field field = clazz.getDeclaredField("name");
            MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
            Class<? extends MyAnnotation> aClass = annotation.getClass();
            Method method = aClass.getMethod("value");
            String[] values = (String[])method.invoke(annotation);
            System.out.println(Arrays.toString(values));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

做個小案例,感受一下控制反轉(zhuǎn)和依賴注入的基本原理

Spring的核心特性是控制反轉(zhuǎn)(IOC)和面向切面編程(AOP)??刂品崔D(zhuǎn)是指對象的控制權(quán)不在我們手里了,而是交給了Spring給我們創(chuàng)建。我們只需要把實體類定義好,提供好無參構(gòu)造方法和get、set方法就好了,它給你的對象自動賦值了,這就叫依賴注入(DI)。該案例它只能夠處理9種屬性類型,包括8種基本類型的包裝類以及String,其他的類型還不能支持。

package test_annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author 喬澳
 * @version 1.0
 * @title: MySpringDemo
 * @projectName Demo1
 * @description:
 * @date 2020/8/17   18:22
 */
public class MySpringDemo {
    public Object getBean(String className){
        Object obj = null;
        Class<?> clazz = null;
        try {
            clazz = Class.forName(className);
            //獲取無參構(gòu)造方法
            Constructor con = clazz.getConstructor();
            //調(diào)用無參構(gòu)造方法創(chuàng)建對象
            obj = con.newInstance();
            //解析注解中的信息
            //注解放在屬性上面,首先獲取所有的屬性
            Field[] fields = clazz.getDeclaredFields();
            for(int i = 0;i<fields.length;i++){
                //根據(jù)屬性獲取屬性上面聲明的注解
                Annotation annotation = fields[i].getAnnotation(MyAnnotation.class);
                Class<?> aClass = annotation.getClass();
                Method aMethod = aClass.getMethod("value");
                //獲取注解中的值
                String[] values = (String[]) aMethod.invoke(annotation);
                //獲取屬性名
                String fieldName = fields[i].getName();
                //要給屬性賦值,屬性是私有的,雖然反射可以操作私有屬性,但是很不合理
                //我們利用字符串的拼接,得到set方法的名字,再拿到set方法給屬性賦值
                String firstLetter = fieldName.substring(0,1).toUpperCase();//首字母大寫
                String otherLetters = fieldName.substring(1);
                StringBuilder setMethodName = new StringBuilder("set");
                setMethodName.append(firstLetter);
                setMethodName.append(otherLetters);
                //拿到屬性的類型,下面要用到
                Class<?> fieldType = fields[i].getType();
                //根據(jù)set方法名字拿到set方法
                Method setMethod = clazz.getMethod(setMethodName.toString(),fieldType);
                //調(diào)用set方法,給對象賦值,如果屬性不是String類型,是基本類型的包裝類
                //如果屬性是Character類型,做單獨處理
                if (fieldType==Character.class){
                    //把字符串轉(zhuǎn)化為字符
                    Character c = values[0].toCharArray()[0];
                    setMethod.invoke(obj,c);
                }else{
                    //不是Character類型,而是String 或其他7種包裝類,調(diào)用包裝類的帶String參數(shù)的構(gòu)造方法,構(gòu)造值  比如;new Integer(String v)
                    setMethod.invoke(obj,fieldType.getConstructor(String.class).newInstance(values[0]));
                }
                //對于屬性為數(shù)組、集合、對象的情況,這里沒有做處理
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }
}

測試:

寫一個實體類

package test_annotation;

public class Person {
    @MyAnnotation("Tom")
    private String name;
    @MyAnnotation("18")
    private Integer age;
    @MyAnnotation("男")
    private String sex;
    @MyAnnotation("A")
    private Character bloodType;//血型
    public Person(){}
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Character getBloodType() {
        return bloodType;
    }
    public void setBloodType(Character bloodType) {
        this.bloodType = bloodType;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", bloodType=" + bloodType +
                '}';
    }
}

main方法:

package test_annotation;

public class TestMain {
    public static void main(String[] args) {
        MySpringDemo msd = new MySpringDemo();
        Person p = (Person) msd.getBean("test_annotation.Person");
        System.out.println(p);
    }
}

到此這篇關(guān)于深入理解Java中的注解Annotation的文章就介紹到這了,更多相關(guān)深入理解Java注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中的RocketMQ消費冪等詳解

    Java中的RocketMQ消費冪等詳解

    這篇文章主要介紹了Java中的RocketMQ消費冪等詳解,當出現(xiàn)消費者對某條消息重復消費的情況時,重復消費的結(jié)果與消費一次的結(jié)果是相同的,并且多次消費并未對業(yè)務(wù)系統(tǒng)業(yè)務(wù)產(chǎn)生任何負面影響,那么這個消費過程就是消息冪等,需要的朋友可以參考下
    2023-10-10
  • JUnit5常用注解的使用

    JUnit5常用注解的使用

    注解是JUnit的標志性技術(shù),本文就來對它的20個注解,以及元注解和組合注解進行學習,感興趣的可以了解一下
    2021-07-07
  • Idea中導入新模塊無法被識別的問題

    Idea中導入新模塊無法被識別的問題

    這篇文章主要介紹了Idea中導入新模塊無法被識別的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Java 多線程同步 鎖機制與synchronized深入解析

    Java 多線程同步 鎖機制與synchronized深入解析

    從尺寸上講,同步代碼塊比同步方法小。你可以把同步代碼塊看成是沒上鎖房間里的一塊用帶鎖的屏風隔開的空間
    2013-09-09
  • 一行命令同時修改maven項目中多個module的版本號的方法

    一行命令同時修改maven項目中多個module的版本號的方法

    這篇文章主要介紹了一行命令同時修改maven項目中多個module的版本號的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-06-06
  • SpringMvc之HandlerMapping詳解

    SpringMvc之HandlerMapping詳解

    這篇文章主要介紹了SpringMvc之HandlerMapping詳解,Handler可以理解為具體干活的,也就是我們的業(yè)務(wù)處理邏輯,Handler最終是要通過url 來訪問到,這樣url 與Handler之間就有一個映射關(guān)系了,需要的朋友可以參考下
    2023-08-08
  • Java中ResponseBodyEmitter的實現(xiàn)

    Java中ResponseBodyEmitter的實現(xiàn)

    這篇文章主要介紹了Java中ResponseBodyEmitter的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • SpringBoot整合Dubbo框架,實現(xiàn)RPC服務(wù)遠程調(diào)用

    SpringBoot整合Dubbo框架,實現(xiàn)RPC服務(wù)遠程調(diào)用

    Dubbo是一款高性能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向接口的遠程方法調(diào)用,智能容錯和負載均衡,以及服務(wù)自動注冊和發(fā)現(xiàn)。今天就來看下SpringBoot整合Dubbo框架的步驟
    2021-06-06
  • Java的靜態(tài)類型檢查示例代碼詳解

    Java的靜態(tài)類型檢查示例代碼詳解

    本文將使用幾個代碼示例,深入講解Java中的類型檢查機制。一旦完全理解了本文的例子,就完全掌握了Java中的靜態(tài)類型檢查,感興趣的朋友一起看看吧
    2017-11-11
  • 淺談Java線程池是如何運行的

    淺談Java線程池是如何運行的

    這篇文章主要介紹了淺談Java線程池是如何運行的,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11

最新評論

眉山市| 辽阳市| 松原市| 平江县| 吉木乃县| 肃南| 江山市| 长春市| 沁源县| 纳雍县| 屏南县| 怀集县| 天津市| 肇源县| 峡江县| 泰兴市| 苗栗县| 阳新县| 大荔县| 马龙县| 杭州市| 鹿泉市| 海原县| 巴林右旗| 泗水县| 新安县| 阿荣旗| 威海市| 康平县| 信丰县| 江口县| 甘孜县| 突泉县| 利川市| 万山特区| 牡丹江市| 巴林右旗| 德庆县| 斗六市| 新和县| 康定县|