<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java注解詳解

          共 5384字,需瀏覽 11分鐘

           ·

          2020-12-04 15:49

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          ? 作者?|??低吟不作語(yǔ)

          來(lái)源 |? urlify.cn/I7FJvm

          66套java從入門到精通實(shí)戰(zhàn)課程分享

          1、基本語(yǔ)法

          注解是 Java 5 所引入的眾多語(yǔ)言變化之一,是附加在代碼中的一些元信息,用于一些工具在編譯、運(yùn)行時(shí)進(jìn)行解析和使用,起到說(shuō)明、配置的功能。注解不會(huì)也不能影響代碼的實(shí)際邏輯,僅僅起到輔助性的作用,包含在 java.lang.annotation 包中

          注解的語(yǔ)法十分簡(jiǎn)單,只要在現(xiàn)有語(yǔ)法中添加 @ 符號(hào)即可,java.lang 包提供了如下五種注解:

          • @Override

            表示當(dāng)前的方法定義將覆蓋基類的方法,如果你不小心把方法簽名拼錯(cuò)了,編譯器就會(huì)發(fā)出錯(cuò)誤提示

          • @Deprecated

            如果使用該注解的元素被調(diào)用,編譯器就會(huì)發(fā)出警告信息,表示不鼓勵(lì)程序員使用

          • @SuppressWarnings

            關(guān)閉不當(dāng)?shù)木幾g器警告信息

          • @SafeVarargs

            禁止對(duì)具有泛型可變參數(shù)的方法或構(gòu)造函數(shù)的調(diào)用方發(fā)出警告

          • @FunctionalInterface

            聲明接口類型為函數(shù)式接口


          2、定義注解

          注解的定義看起來(lái)和接口的定義很像,事實(shí)上它們和其他 Java 接口一樣,也會(huì)被編譯成 class 文件

          @Target(ElementType.METHOD)
          @Retention(RetentionPolicy.RUNTIME)
          public?@interface?TestAnnotation?{}

          除開(kāi) @ 符號(hào), @Test 的定義看起來(lái)更像一個(gè)空接口。注解的定義也需要一些元注解,元注解用于注解其他的注解

          注解解釋
          @Target表示注解可以用于哪些地方。可能的 ElementType 參數(shù)包括:
          CONSTRUCTOR:構(gòu)造器的聲明
          FIELD:字段聲明(包括 enum 實(shí)例)
          LOCAL_VARIABLE:局部變量聲明
          METHOD:方法聲明
          PACKAGE:包聲明
          PARAMETER:參數(shù)聲明
          TYPE:類、接口(包括注解類型)或者 enum 聲明
          @Retention表示注解信息保存的時(shí)長(zhǎng)。可選的 RetentionPolicy 參數(shù)包括:
          SOURCE:注解將被編譯器丟棄
          CLASS:注解在 class 文件中可用,但是會(huì)被 VM 丟棄
          RUNTIME:VM 將在運(yùn)行期也保留注解,因此可以通過(guò)反射機(jī)制讀取注解的信息
          @Documented將此注解保存在 Javadoc 中
          @Inherited允許子類繼承父類的注解
          @Repeatable允許一個(gè)注解可以被使用一次或者多次(Java8)

          不包含任何元素的注解稱為標(biāo)記注解,上例中的 @Test 就是標(biāo)記注解。注解通常也會(huì)包含一些表示特定值的元素,當(dāng)分析處理注解的時(shí)候,程序可以利用這些值。注解的元素看起來(lái)就像接口的方法,但可以為其指定默認(rèn)值

          @Target(ElementType.METHOD)
          @Retention(RetentionPolicy.RUNTIME)
          public?@interface?TestAnnotation?{
          ????int?id();
          ????String?description()?default?"no?description";
          }

          ....

          public?class?TestUtils?{
          ????//?在方法上使用注解?@TestAnnotation
          ????@UseCase(id?=?47,?description?=?"description")
          ????public?void?test()?{
          ????????...
          ????}
          }


          注解元素可用的類型如下所示,如果使用了其他類型,編譯器就會(huì)報(bào)錯(cuò):

          • 所有基本類型(int、float、boolean 等)

          • String

          • Class

          • enum

          • Annotation

          • 以上類型的數(shù)組

          如果沒(méi)有給出 description 的值,在分析處理這個(gè)類的時(shí)候會(huì)使用該元素的默認(rèn)值。元素的默認(rèn)值不能有不確定的值,也就是說(shuō),元素要么有默認(rèn)值,要么就在使用注解時(shí)提供元素的值

          這里還有另外一個(gè)限制:任何非基本類型的元素,無(wú)論是在源代碼聲明時(shí)還是在注解接口中定義默認(rèn)值時(shí),都不能使用 null 作為值。如果我們希望表現(xiàn)一個(gè)元素的存在或者缺失的狀態(tài),可以自定義一些特殊的值,比如空字符串或者負(fù)數(shù)用于表達(dá)某個(gè)元素不存在

          注解不支持繼承,你不能使用 extends 關(guān)鍵字來(lái)繼承 @interface


          3、注解處理器

          如果沒(méi)有用于讀取注解的工具,那么注解不會(huì)比注釋更有用。使用注解中一個(gè)很重要的作用就是創(chuàng)建與使用注解處理器。Java 拓展了反射機(jī)制的 API 用于幫助你創(chuàng)造這類工具。同時(shí)他還提供了 javac 編譯器鉤子在編譯時(shí)使用注解

          下面是一個(gè)非常簡(jiǎn)單的注解處理器,我們用它來(lái)讀取被注解的 TestUtils 類,并且使用反射機(jī)制來(lái)尋找 @TestAnnotation 標(biāo)記

          public?class?TestAnnotationTracker?{
          ????
          ????public?static?void?trackTestAnnotation(Class?cl)?{
          ????????for(Method?m?:?cl.getDeclaredMethods())?{
          ????????????TestAnnotation?ta?=?m.getAnnotation(TestAnnotation.class);
          ????????????if(ta?!=?null)?{
          ????????????????System.out.println(ta.id()?+?"\n?"?+?ta.description());
          ????????????}
          ????????}
          ????}
          ????
          ????public?static?void?main(String[]?args)?{
          ????????trackTestAnnotation(TestUtils.class);
          ????}
          }

          這里用到了兩個(gè)反射的方法:getDeclaredMethods() 和 getAnnotation(),getAnnotation() 方法返回指定類型的注解對(duì)象,在本例中就是 TestAnnotation,如果被注解的方法上沒(méi)有該類型的注解,返回值就為 null。通過(guò)調(diào)用 id() 和 description() 方法來(lái)提取元素值

          4、使用注解實(shí)現(xiàn)對(duì)象 - 數(shù)據(jù)庫(kù)映射

          當(dāng)有些框架需要一些額外的信息才能與你的源代碼協(xié)同工作,這種情況下注解就會(huì)變得十分有用。自定義例如對(duì)象/關(guān)系映射工具(Hibernate 和 MyBatis)通常都需要 XML 描述文件,而這些文件脫離于代碼之外。除了定義 Java 類,程序員還必須重復(fù)的提供某些信息,而例如類名和包名等信息已經(jīng)在原始類中提供過(guò)了,經(jīng)常會(huì)導(dǎo)致代碼和描述文件的同步問(wèn)題

          假設(shè)你現(xiàn)在想提供一些基本的對(duì)象/關(guān)系映射功能,能夠自動(dòng)生成數(shù)據(jù)庫(kù)表。你可以使用 XML 描述文件來(lái)指明類的名字、每個(gè)成員以及數(shù)據(jù)庫(kù)映射的相關(guān)信息。但是,通過(guò)使用注解,你可以把所有信息都保存在 JavaBean 源文件中。為此你需要一些用于定義數(shù)據(jù)庫(kù)表名稱、數(shù)據(jù)庫(kù)列以及將 SQL 類型映射到屬性的注解

          首先創(chuàng)建一個(gè)用來(lái)映射數(shù)據(jù)庫(kù)表的注解,用來(lái)修飾類、接口(包括注解類型)或者 enum 聲明

          @Target(ElementType.TYPE)
          @Retention(RetentionPolicy.RUNTIME)
          public?@interface?DBTable?{
          ????String?name()?default?"";
          }

          如下是修飾字段的注解

          @Target(ElementType.FIELD)
          @Retention(RetentionPolicy.RUNTIME)
          public?@interface?Constraints?{
          ????boolean?primaryKey()?default?false;
          ????boolean?allowNull()?default?true;
          ????boolean?unique()?default?false;
          }

          public?@interface?SQLString?{
          ????int?value()?default?0;
          ????String?name()?default?"";
          ????Constraints?constraints()?default?@Constraints;
          }

          @Target(ElementType.FIELD)
          @Retention(RetentionPolicy.RUNTIME)
          public?@interface?SQLInteger?{
          ????String?name()?default?"";
          ????Constraints?constraints()?default?@Constraints;
          }

          @Constraints 代表了數(shù)據(jù)庫(kù)通常提供的約束的一小部分,primaryKey(),allowNull() 和 unique() 元素都提供了默認(rèn)值,大多數(shù)情況下,注解的使用者都不需要輸入太多東西

          另外兩個(gè) @interface 定義的是 SQL 類型。如果希望這個(gè)框架更有價(jià)值的話,我們應(yīng)該為每個(gè) SQL 類型都定義相應(yīng)的注解。不過(guò)作為示例,兩個(gè)元素足夠了。這些 SQL 類型具有 name() 元素和 constraints() 元素。后者利用了嵌套注解的功能,將數(shù)據(jù)庫(kù)列的類型約束信息嵌入其中。注意 constraints() 元素的默認(rèn)值是 @Constraints,沒(méi)有在括號(hào)中指明 @Constraints 元素的值,因此,constraints() 的默認(rèn)值為所有元素都為默認(rèn)值。如果要使得嵌入的 @Constraints 注解中的 unique() 元素為 true,并作為 constraints() 元素的默認(rèn)值,你可以像如下定義:

          public?@interface?Uniqueness?{
          ????Constraints?constraints()?default?@Constraints(unique?=?true);
          }

          下面是一個(gè)簡(jiǎn)單的,使用了如上注解的類

          @DBTable(name?=?"MEMBER")
          public?class?Member?{
          ????@SQLString(30)
          ????String?firstName;
          ????@SQLString(50)
          ????String?lastName;
          ????@SQLInteger
          ????Integer?age;
          ????@SQLString(value?=?30,?constraints?=?@Constraints(primaryKey?=?true))
          ????String?reference;
          ????static?int?memberCount;
          ????public?String?getReference()?{?return?reference;?}
          ????public?String?getFirstName()?{?return?firstName;?}
          ????public?String?getLastName()?{?return?lastName;?}
          ????@Override
          ????public?String?toString()?{?return?reference;?}
          ????public?Integer?getAge()?{?return?age;?}
          }


          類注解 @DBTable 注解給定了元素值 MEMBER,它將會(huì)作為表的名字。類的屬性 firstName 和 lastName 都被注解為 @SQLString 類型并且給了默認(rèn)元素值分別為 30 和 50,并在嵌入的 @Constraint 注解中設(shè)定 primaryKey 元素的值

          下面是一個(gè)注解處理器的例子,它將讀取一個(gè)類文件,檢查上面的數(shù)據(jù)庫(kù)注解,并生成用于創(chuàng)建數(shù)據(jù)庫(kù)的 SQL 命令:

          public?class?TableCreator?{
          ????
          ????public?static?void?generateSql(String[]?classnames)?throws?Exception?{
          ????????for?(String?className?:?classnames)?{
          ????????????Class?cl?=?Class.forName(className);
          ????????????DBTable?dbTable?=?cl.getAnnotation(DBTable.class);
          ????????????String?tableName?=?dbTable.name();
          ????????????//?如果表名為空字符串,則使用類名
          ????????????if?(tableName.length()?????????????????tableName?=?cl.getName().toUpperCase();
          ????????????}
          ????????????List?columnDefs?=?new?ArrayList<>();
          ????????????for?(Field?field?:?cl.getDeclaredFields())?{
          ????????????????String?columnName?=?null;
          ????????????????Annotation[]?anns?=?field.getDeclaredAnnotations();
          ????????????????//?該屬性不是列
          ????????????????if?(anns.length?????????????????????continue;
          ????????????????}
          ????????????????//?處理整數(shù)類型
          ????????????????if?(anns[0]?instanceof?SQLInteger)?{
          ????????????????????SQLInteger?sInt?=?(SQLInteger)?anns[0];
          ????????????????????//?如果列名為空字符串,則使用屬性名
          ????????????????????if?(sInt.name().length()?????????????????????????columnName?=?field.getName().toUpperCase();
          ????????????????????}?else?{
          ????????????????????????columnName?=?sInt.name();
          ????????????????????}
          ????????????????????columnDefs.add(columnName?+?"?INT"?+?getConstraints(sInt.constraints()));
          ????????????????}
          ????????????????//?處理字符串類型
          ????????????????if?(anns[0]?instanceof?SQLString)?{
          ????????????????????SQLString?sString?=?(SQLString)?anns[0];
          ????????????????????if?(sString.name().length()?????????????????????????columnName?=?field.getName().toUpperCase();
          ????????????????????}?else?{
          ????????????????????????columnName?=?sString.name();
          ????????????????????}
          ????????????????????columnDefs.add(columnName?+?"?VARCHAR("?+?sString.value()?+?")"?+
          ????????????????????????????getConstraints(sString.constraints()));
          ????????????????}
          ????????????????//?構(gòu)造并輸出?sql?字符串
          ????????????????StringBuilder?createCommand?=?new?StringBuilder("CREATE?TABLE?"?+?tableName?+?"(");
          ????????????????for?(String?columnDef?:?columnDefs)?{
          ????????????????????createCommand.append("\n?"?+?columnDef?+?",");
          ????????????????}
          ????????????????String?tableCreate?=?createCommand.substring(0,?createCommand.length()?-?1)?+?");";
          ????????????????System.out.println("Table?Creation?SQL?for?"?+?className?+?"?is:\n"?+?tableCreate);
          ????????????}
          ????????}
          ????}

          ????private?static?String?getConstraints(Constraints?con)?{
          ????????String?constraints?=?"";
          ????????if?(!con.allowNull())
          ????????????constraints?+=?"?NOT?NULL";
          ????????if?(con.primaryKey())
          ????????????constraints?+=?"?PRIMARY?KEY";
          ????????if?(con.unique())
          ????????????constraints?+=?"?UNIQUE";
          ????????return?constraints;
          ????}
          }



          5、編譯時(shí)注解處理


          當(dāng) @Retention 的 RetentionPolicy 參數(shù)被標(biāo)注為 SOURCE 或 CLASS,此時(shí)你無(wú)法通過(guò)反射去獲取注解信息,因?yàn)樽⒔庠谶\(yùn)行期是不存在的。使用 javac 可以創(chuàng)建編譯時(shí)注解處理器,在編譯時(shí)掃描和處理注解。你可以自定義注解,并注冊(cè)到對(duì)應(yīng)的注解處理器。注解處理器可以生成 Java 代碼,這些生成的 Java 代碼會(huì)組成新的 Java 源文件,但不能修改已經(jīng)存在的 Java 類,例如向已有的類中添加方法。如果你的注解處理器創(chuàng)建了新的源文件,在新一輪處理中注解處理器會(huì)檢查源文件本身,在檢測(cè)一輪之后持續(xù)循環(huán),直到不再有新的源文件產(chǎn)生,然后編譯所有的源文件

          我們來(lái)編寫一個(gè)簡(jiǎn)單的注解處理器,如下是注解的定義

          @Retention(RetentionPolicy.SOURCE)
          @Target({ElementType.TYPE,?ElementType.METHOD,
          ????????ElementType.CONSTRUCTOR,
          ????????ElementType.ANNOTATION_TYPE,
          ????????ElementType.PACKAGE,?ElementType.FIELD,
          ????????ElementType.LOCAL_VARIABLE})
          public?@interface?Simple?{
          ????String?value()?default?"-default-";
          }

          @Retention 的參數(shù)為 SOURCE,這意味著注解不會(huì)存留在編譯后的 class 文件,因?yàn)檫@對(duì)應(yīng)編譯時(shí)處理注解是沒(méi)有必要的,在這里,javac 是唯一有機(jī)會(huì)處理注解的方式

          package?annotations.simplest;
          @Simple
          public?class?SimpleTest?{
          ????@Simple
          ????int?i;
          ????@Simple
          ????public?SimpleTest()?{}
          ????@Simple
          ????public?void?foo()?{
          ????????System.out.println("SimpleTest.foo()");
          ????}
          ????@Simple
          ????public?void?bar(String?s,?int?i,?float?f)?{
          ????????System.out.println("SimpleTest.bar()");
          ????}
          ????@Simple
          ????public?static?void?main(String[]?args)?{
          ????????@Simple
          ????????SimpleTest?st?=?new?SimpleTest();
          ????????st.foo();
          ????}
          }


          運(yùn)行 main 方法,程序就會(huì)開(kāi)始編譯,如下是一個(gè)簡(jiǎn)單的處理器,作用就是把注解相關(guān)的信息打印出來(lái)

          package?annotations.simplest;
          import?javax.annotation.processing.*;
          import?javax.lang.model.SourceVersion;
          import?javax.lang.model.element.*;
          import?java.util.*;
          @SupportedAnnotationTypes("annotations.simplest.Simple")
          @SupportedSourceVersion(SourceVersion.RELEASE_8)
          public?class?SimpleProcessor?extends?AbstractProcessor?{
          ????
          ????@Override
          ????public?boolean?process(Set?annotations,
          ?????RoundEnvironment?env)?{
          ????????for(TypeElement?t?:?annotations)?{
          ????????????System.out.println(t);
          ????????}
          ????????for(Element?el?:?env.getElementsAnnotatedWith(Simple.class))?{
          ????????????display(el);
          ????????}
          ????????return?false;
          ????}
          ????
          ????private?void?display(Element?el)?{
          ????????System.out.println("====?"?+?el?+?"?====");
          ????????System.out.println(el.getKind()?+?//?返回此元素的種類,字段,方法,或是類
          ????????????????"?:?"?+?el.getModifiers()?+?//?返回此元素的修飾符
          ????????????????"?:?"?+?el.getSimpleName()?+?//?返回此元素的簡(jiǎn)單名稱
          ????????????????"?:?"?+?el.asType());?//?返回此元素定義的類型
          ????????//?如果元素為CLASS類型,動(dòng)態(tài)向下轉(zhuǎn)型為更具體的元素類型,并打印相關(guān)信息
          ????????if(el.getKind().equals(ElementKind.CLASS))?{
          ????????????TypeElement?te?=?(TypeElement)el;
          ????????????System.out.println(te.getQualifiedName());
          ????????????System.out.println(te.getSuperclass());
          ????????????System.out.println(te.getEnclosedElements());
          ????????}
          ????????//?如果元素為METHOD類型,動(dòng)態(tài)向下轉(zhuǎn)型為更具體的元素類型,并打印相關(guān)信息
          ????????if(el.getKind().equals(ElementKind.METHOD))?{
          ????????????ExecutableElement?ex?=?(ExecutableElement)el;
          ????????????System.out.print(ex.getReturnType()?+?"?");
          ????????????System.out.print(ex.getSimpleName()?+?"(");
          ????????????System.out.println(ex.getParameters()?+?")");
          ????????}
          ????}
          }


          使用 @SupportedAnnotationTypes 和 @SupportedSourceVersion 注解來(lái)確定支持哪些注解以及支持的 Java 版本

          注解處理器需要繼承抽象類 javax.annotation.processing.AbstractProcessor,唯一需要實(shí)現(xiàn)的方法就是 process(),這里是所有行為發(fā)生的地方。第一個(gè)參數(shù)獲取到此注解處理器所要處理的注解集合,第二個(gè)參數(shù)保留了剩余信息,這里我們所做的事情只是打印了注解(只存在一個(gè))。process() 中實(shí)現(xiàn)的第二個(gè)操作是循環(huán)所有被 @Simple 注解的元素,并且針對(duì)每一個(gè)元素調(diào)用 display() 方法。展示所有 Element 自身的基本信息





          粉絲福利:實(shí)戰(zhàn)springboot+CAS單點(diǎn)登錄系統(tǒng)視頻教程免費(fèi)領(lǐng)取

          ???

          ?長(zhǎng)按上方微信二維碼?2 秒
          即可獲取資料



          感謝點(diǎn)贊支持下哈?

          瀏覽 133
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  av无码国产电影在线观看 | 色天堂网| 国产精品婷婷久久久久 | 九一福利在线观看 | 日熟妇|