日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

JAVA注解

一、Java注解概述

注解(Annotation),也叫元數(shù)據(jù)。一種代碼級(jí)別的說明。它是JDK1.5及以后版本引入的一個(gè)特性,與類、接口、枚舉是在同一個(gè)層次。它可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對(duì)這些元素進(jìn)行說明,注釋。

二、注解的作用分類

  • 編寫文檔: 通過代碼里標(biāo)識(shí)的元數(shù)據(jù)生成文檔【生成文檔doc文檔】
  • 代碼分析: 通過代碼里標(biāo)識(shí)的元數(shù)據(jù)對(duì)代碼進(jìn)行分析【使用反射】
  • 編譯檢查: 通過代碼里標(biāo)識(shí)的元數(shù)據(jù)讓編譯器能夠?qū)崿F(xiàn)基本的編譯檢查【Override等】

編寫文檔

首先,我們要知道Java中是有三種注釋的,分別為單行注釋、多行注釋和文檔注釋。而文檔注釋中,也有@開頭的元注解,這就是基于文檔注釋的注解。我們可以使用javadoc命令來生成doc文檔,此時(shí)我們文檔的內(nèi)元注解也會(huì)生成對(duì)應(yīng)的文檔內(nèi)容。這就是編寫文檔的作用。

代碼分析

我們頻繁使用之一,也是包括使用反射來通過代碼里標(biāo)識(shí)的元數(shù)據(jù)對(duì)代碼進(jìn)行分析的,此內(nèi)容我們?cè)诤罄m(xù)展開講解。

編譯檢查

至于在編譯期間在代碼中標(biāo)識(shí)的注解,可以用來做特定的編譯檢查,它可以在編譯期間就檢查出“你是否按規(guī)定辦事”,如果不按照注解規(guī)定辦事的話,就會(huì)在編譯期間飄紅報(bào)錯(cuò),并予以提示信息。可以就可以為我們代碼提供了一種規(guī)范制約,避免我們后續(xù)在代碼中處理太多的代碼以及功能的規(guī)范。比如,@Override注解是在我們覆蓋父類(父接口)方法時(shí)出現(xiàn)的,這證明我們覆蓋方法是繼承于父類(父接口)的方法,如果該方法稍加改變就會(huì)報(bào)錯(cuò);@FunctionInterface注解是在編譯期檢查是否是函數(shù)式接口的,如果不遵循它的規(guī)范,同樣也會(huì)報(bào)錯(cuò)。

三、jdk的內(nèi)置注解

3.1 內(nèi)置注解分類

  • @Override: 標(biāo)記在成員方法上,用于標(biāo)識(shí)當(dāng)前方法是重寫父類(父接口)方法,編譯器在對(duì)該方法進(jìn)行編譯時(shí)會(huì)檢查是否符合重寫規(guī)則,如果不符合,編譯報(bào)錯(cuò)。
  • @Deprecated: 用于標(biāo)記當(dāng)前類、成員變量、成員方法或者構(gòu)造方法過時(shí)如果開發(fā)者調(diào)用了被標(biāo)記為過時(shí)的方法,編譯器在編譯期進(jìn)行警告。
  • @SuppressWarnings: 壓制警告注解,可放置在類和方法上,該注解的作用是阻止編譯器發(fā)出某些警告信息。

3.2 @Override注解

標(biāo)記在成員方法上,用于標(biāo)識(shí)當(dāng)前方法是重寫父類(父接口)方法,編譯器在對(duì)該方法進(jìn)行編譯時(shí)會(huì)檢查是否符合重寫規(guī)則,如果不符合,編譯報(bào)錯(cuò)。

這里解釋一下@Override注解,在我們的Object基類中有一個(gè)方法是toString方法,我們通常在實(shí)體類中去重寫此方法來達(dá)到打印對(duì)象信息的效果,這時(shí)候也會(huì)發(fā)現(xiàn)重寫的toString方法上方就有一個(gè)@Override注解。如下所示:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

于是,我們?cè)噲D去改變重寫后的toString方法名稱,將方法名改為toStrings。你會(huì)發(fā)現(xiàn)在編譯期就報(bào)錯(cuò)了!如下所示:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

那么這說明什么呢?這就說明該方法不是我們重寫其父類(Object)的方法。這就是@Override注解的作用。

3.3 @Deprecated注解

用于標(biāo)記當(dāng)前類、成員變量、成員方法或者構(gòu)造方法過時(shí)如果開發(fā)者調(diào)用了被標(biāo)記為過時(shí)的方法,編譯器在編譯期進(jìn)行警告。

我們解釋@Deprecated注解就需要模擬一種場(chǎng)景了。假設(shè)我們公司的產(chǎn)品,目前是V1.0版本,它為用戶提供了show1方法的功能。這時(shí)候我們?yōu)楫a(chǎn)品的show1方法的功能又進(jìn)行了擴(kuò)展,打算發(fā)布V2.0版本。但是,我們V1.0版本的產(chǎn)品需要拋棄嗎?也就是說我們V1.0的產(chǎn)品功能還繼續(xù)讓用戶使用嗎?答案肯定是不能拋棄的,因?yàn)橛幸徊糠钟脩羰且恢庇肰1.0版本的。如果拋棄了該版本會(huì)損失很多的用戶量,所以我們不能拋棄該版本。這時(shí)候,我們對(duì)功能進(jìn)行了擴(kuò)展后,發(fā)布了V2.0版本,我們給予用戶的通知就可以了,也就是告知用戶我們?cè)赩2.0版本中為功能進(jìn)行了擴(kuò)展。可以讓用戶自行選擇版本。

但是,除了發(fā)布告知用戶版本情況之外,我們還需要在原來版本的功能上給予提示,在上面的模擬場(chǎng)景中我們需要在show1方法上方加@Deprecated注解給予提示。通過這種方式也告知用戶“這是舊版本時(shí)候的功能了,我們不建議再繼續(xù)使用舊版本的功能”,這句話的意思也就正是給用戶做了提示。用戶也會(huì)這么想“奧,這版本的這個(gè)功能不好用了,肯定有新版本,又更好用的功能。我要去官網(wǎng)查一下下載新版本”,還會(huì)有用戶這么想“我明白了,又更新出更好的功能了,但是這個(gè)版本的功能我已經(jīng)夠用了,不需要重新下載新版本了”。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

那么我們?cè)趺床榭次疑鲜鏊f的在功能上給予的提示呢?這時(shí)候我需要去創(chuàng)建一個(gè)方法,然后去調(diào)用show1方法,并查看調(diào)用時(shí)它是如何提示的。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

圖已經(jīng)貼出來了,你是否發(fā)現(xiàn)的新舊版本功能的異同點(diǎn)呢?很明顯,該方法中的提示是在調(diào)用的方法名上加了一道橫線把該方法劃掉了。這就體現(xiàn)了show1方法過時(shí)了,已經(jīng)不建議使用了,我們?yōu)槟闾峁┝烁玫摹?/p>

回想起來,在我們的api中也會(huì)有方法是過時(shí)的,比如我們的Date日期類中的方法有很多都已經(jīng)過時(shí)了。如下圖:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 


不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

如你所見,是不是有很多方法都過時(shí)了呢?那它的方法上是加了@Deprecated注解嗎?來跟著我的腳步,我?guī)銈兛匆幌隆?/p>不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

我們已經(jīng)知道的Date類中的這些方法已經(jīng)是過時(shí)的了,如果我們使用該方法并執(zhí)行該程序的話。執(zhí)行的過程中就會(huì)提示該方法已過時(shí)的內(nèi)容,但是只是提示,并不影響你使用該方法。如下:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

OK!這也就是@Deprecated注解的作用了。

3.4 @SuppressWarnings注解

壓制警告注解,可放置在類和方法上,該注解的作用是阻止編譯器發(fā)出某些警告信息,該注解為單值注解,只有 一個(gè)value參數(shù),該參數(shù)為字符串?dāng)?shù)組類型,參數(shù)值常用的有如下幾個(gè)。

  • unchecked:未檢查的轉(zhuǎn)化,如集合沒有指定類型還添加元素
  • unused:未使用的變量
  • resource:有泛型未指定類型
  • path:該類路徑,原文件路徑中有不存在的路徑
  • deprecation:使用了某些不贊成使用的類和方法
  • fallthrough:switch語句執(zhí)行到底沒有break關(guān)鍵字
  • rawtypes:沒有寫泛型,比如: List list = new ArrayList();
  • all:全部類型的警告

壓制警告注解,顧名思義就是壓制警告的出現(xiàn)。我們都知道,在Java代碼的編寫過程中,是有很多黃色警告出現(xiàn)的。但是我不知道你的導(dǎo)師是否教過你,程序員只需要處理紅色的error,不需要理會(huì)黃色的warning。如果你的導(dǎo)師說過此問題,那是有原因的。因?yàn)樵谀銓W(xué)習(xí)階段,我們認(rèn)清處理紅色的error即可,這樣可以減輕你學(xué)習(xí)階段在腦部的記憶內(nèi)容。如果你剛剛加入學(xué)習(xí)Java的隊(duì)列中,需要大腦記憶的東西就有太多了,也就是我們目前不需要額外記憶其他的東西,只記憶重點(diǎn)即可。至于黃色warning嘛,在你的學(xué)習(xí)過程中慢慢就會(huì)有所了解的,而不是死記硬背的。

那為了解釋@SuppressWarnings注解,我們還使用上一個(gè)例子,因?yàn)樵谀莻€(gè)例子中就有黃色的warning出現(xiàn)。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

而每一個(gè)黃色的warning都會(huì)有警告信息的。比如,這一個(gè)圖中的警告信息,就告知你show2()方法沒有被使用,簡(jiǎn)單來說,你創(chuàng)建的show2方法,但是你在代碼中并沒有調(diào)用過此方法。以后你便會(huì)遇到各種各樣黃色的warning。然后, 我們就可以使用不同的注解參數(shù)來壓制不同的注解。但是在該注解的參數(shù)中,提供了一個(gè)all參數(shù)可以壓制全部類型的警告。而這個(gè)注解是需要加到類的上方,并賦予all參數(shù),即可壓制所有警告。如下:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

我們加入注解并賦予all參數(shù)后,你會(huì)發(fā)現(xiàn)use方法和show2方法的警告沒有了,實(shí)際上導(dǎo)Date包的警告還在,因?yàn)槲覀僁ate包導(dǎo)入到了該類中,但是我們并沒有創(chuàng)建Date對(duì)象,也就是并沒有寫入Date在代碼中,你也會(huì)發(fā)現(xiàn)那一行是灰色的,也就證明了我們沒有去使用導(dǎo)入這個(gè)包的任何信息的說法,出現(xiàn)這種情況我們就需要把這個(gè)沒有用的導(dǎo)包內(nèi)容刪除掉,使用Ctrl + X刪除導(dǎo)入沒有用到的包即可。還有一種辦法就是在包的上方修飾壓制警告注解,但是我認(rèn)為在一個(gè)沒有用的包上加壓制注解是毫無意義的,所以,我們直接刪除就好。

然后,我們還見到上圖,注解那一行出現(xiàn)了警告信息提示。這一行的意思是冗余的警告壓制。這就是說我們壓制以下的警告并沒有什么意義而造成的冗余,但是如果我們使用了該類并做了點(diǎn)什么的話,壓制注解的冗余警告就會(huì)消失,畢竟我們使用了該類,此時(shí)就不會(huì)早場(chǎng)冗余了。

上述解釋@SuppressWarnings注解也差不多就這些了。OK,繼續(xù)向下看吧。持續(xù)為大家講解。

3.5 @Repeatable注解

@Repeatable 表明標(biāo)記的注解可以多次應(yīng)用于相同的聲明或類型,此注解由Java8版本引入。我們知道注解是不能重復(fù)定義的,其實(shí)該注解就是一個(gè)語法糖,它可以重復(fù)多次使用,更適用于我們的特殊場(chǎng)景。

首先,我們先創(chuàng)建一個(gè)可以重復(fù)使用的注解。

package com.mylifes1110.anno;

import java.lang.annotation.Repeatable;

@Repeatable(Hour.class)
public @interface Hours {
    double[] hours() default 0;
}

你會(huì)發(fā)現(xiàn)注解要求傳入的只是一個(gè)類對(duì)象,此類對(duì)象就需要傳入另外一個(gè)注解,這里也就是另外一個(gè)注解容器的類對(duì)象。我們?nèi)?chuàng)建一下。

package com.mylifes1110.anno;

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

//容器
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Hour {
    Hours[] value();
}

其實(shí),這兩個(gè)注解的套用,就是將一個(gè)普通的注解封裝了一個(gè)可重復(fù)使用的注解,來達(dá)到注解的復(fù)用性。最后,我們創(chuàng)建一下測(cè)試類,隨后帶你去看一下源碼。

package com.mylifes1110.java;

import com.mylifes1110.anno.Hours;

@Hours(hours = 4)
@Hours(hours = 4.5)
@Hours(hours = 2)
public class Worker {
    public static void main(String[] args) {
        //通過Hours注解類型來獲取Worker中的值數(shù)組對(duì)象
        Hours[] hours = Worker.class.getAnnotationsByType(Hours.class);
        //遍歷數(shù)組
        for (Hours h : hours) {
            System.out.println(h);
        }
    }
}

測(cè)試類,是一個(gè)工人測(cè)試類,該工人使用注解記錄早中晚的工作時(shí)間。測(cè)試結(jié)果如下:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

然后我們進(jìn)入到源碼一探究竟。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

我們發(fā)現(xiàn)進(jìn)入到源碼后,就只看見一個(gè)返回值為類對(duì)象的抽象方法。這也就驗(yàn)證了該注解只是一個(gè)可實(shí)現(xiàn)重復(fù)性注解的語法糖而已。

四、注解分類

4.1 注解分類

注解可以根據(jù)注解參數(shù)分為三大類:

  • 標(biāo)記注解: 沒有參數(shù)的注解,僅用自身的存在與否為程序提供信息,如@Override注解,該注解沒有參數(shù),用于表示當(dāng)前方法為重寫方法。
  • 單值注解: 只有一個(gè)參數(shù)的注解,如果該參數(shù)的名字為value,那么可以省略參數(shù)名,如 @SuppressWarnings(value = “all”),可以簡(jiǎn)寫為@SuppressWarnings(“all”)。
  • 完整注解: 有多個(gè)參數(shù)的注解。

4.2 標(biāo)記注解

說到@Override注解是一個(gè)標(biāo)記注解,那我們進(jìn)入到該注解的源碼查看一下。從上往下看該注解源碼,發(fā)現(xiàn)它繼承了導(dǎo)入了java.lang.annotation.*,也就是有使用到該包的內(nèi)容。然后下面就又是兩個(gè)看不懂的注解,其實(shí)發(fā)現(xiàn)注解的定義格式是public修飾的@Interface,最終看到該注解中方法體并沒有任何參數(shù),也就是只起到標(biāo)記作用。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

4.3 單值注解

在上面我們用到的@SuppressWarnings注解就是一個(gè)單值注解。那我們進(jìn)入到它的源碼看一下是怎么個(gè)情況。其實(shí),和標(biāo)記注解比較,它就多一個(gè)value參數(shù)而已,而這就是單值注解的必要條件,即只有一個(gè)參數(shù)。并且這一個(gè)參數(shù)為value時(shí),我們可以省略value。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

4.4 完整注解

上述兩個(gè)類型注解講解完,至于完整注解嘛,這下就能更明白了。其中的方法體就是有多個(gè)參數(shù)而已。

五、自定義注解

5.1 自定義注解格式

格式: public @Interface 注解名 {屬性列表/無屬性}

注意: 如果注解體中無任何屬性,其本質(zhì)就是標(biāo)記注解。但是與其標(biāo)注注解還少了上邊修飾的元注解。

如下,這就是一個(gè)注解。但是它與jdk自定義注解有點(diǎn)區(qū)別,jdk自定義注解的上方還有注解來修飾該注解,而那注解就叫做元注解。元注解我會(huì)在后面詳細(xì)的說到。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

這里我們的確不知道@Interface是什么,那我們就把自定義的這個(gè)注解反編譯一下,看一下反編譯信息。反編譯操作如下:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

反編譯后的反編譯內(nèi)容如下:

public interface com.mylifes1110.anno.MyAnno extends java.lang.annotation.Annotation {
}

首先,看過反編譯內(nèi)容后,我們可以直觀的得知它是一個(gè)接口,因?yàn)樗膒ublic修飾符后面的關(guān)鍵字是interface。

其次,我們發(fā)現(xiàn)MyAnno這個(gè)接口是繼承了java.lang.annotation包下的Annotation接口。

所以,我們可以得知注解的本質(zhì)就是一個(gè)接口,該接口默認(rèn)繼承了Annotation接口。

既然,是繼承的Annotation接口,那我們就去進(jìn)入到這個(gè)接口中,看它定義了什么。以下是我抽取出來的接口內(nèi)容。我們發(fā)現(xiàn)它看似很常見,其實(shí)它們不是很常用,作為了解即可。

public interface Annotation {
    boolean equals(Object obj);
    int hashCode();
    String toString();
	Class<? extends Annotation> annotationType();
}

最后,我們的注解中也是可以寫有屬性的,它的屬性不同于普通的屬性,它的屬性是抽象方法。既然注解也是一個(gè)接口,那么我們可以說接口體中可以定義什么,它同樣也可以定義,而它的修飾符與接口一樣,也是默認(rèn)被public abstract修飾。

而注解體中的屬性也是有要求的。其屬性要求如下:

屬性的返回值類型必須是以下幾種:

  • 基本數(shù)據(jù)類型
  • String類型
  • 枚舉類型
  • 注解
  • 以上類型的數(shù)組
  • 注意: 在這里不能有void的無返回值類型和以上類型以外的類型

定義的屬性,在使用時(shí)需要給注解中的屬性賦值

  • 如果定義屬性時(shí),使用default關(guān)鍵字給屬性默認(rèn)初始化值,則使用注解時(shí)可以不為屬性賦值,它取的是默認(rèn)值。如果為它再次傳入值,那么就發(fā)生了對(duì)原值的覆蓋。
  • 如果只有一個(gè)屬性需要賦值,并且屬性的名稱為value,則賦值時(shí)value可以省略,可以直接定義值
  • 數(shù)組賦值時(shí),值使用{}存儲(chǔ)值。如果數(shù)組中只有一個(gè)值,則可以省略{}。

5.2 自定義注解屬性的返回值

屬性返回值既然有以上幾種,那么我就在這里寫出這幾種演示一下是如何寫的。

首先,定義一個(gè)枚舉類和另外一個(gè)注解備用。

package com.mylifes1110.enums;

public enum Lamp {
    RED, GREEN, YELLOW
}
package com.mylifes1110.anno;

public @interface MyAnno2 {
}

其次,我們來定義上述幾種類型,如下:

package com.mylifes1110.anno;

import com.mylifes1110.enums.Lamp;

public @interface MyAnno {
    //基本數(shù)據(jù)類型
    int num();

    //String類型
    String value();

    //枚舉類型
    Lamp lamp();

    //注解類型
    MyAnno2 myAnno2();
    
    //以上類型的數(shù)組
    String[] values();
    Lamp[] lamps();
    MyAnno2[] myAnno2s();
    int[] nums();
}

5.3 自定義注解的屬性賦值

這里我們演示一下,首先,我們使用該注解來進(jìn)行演示。

package com.mylifes1110.anno;

public @interface MyAnno {
    //基本數(shù)據(jù)類型
    int num();

    //String類型
    String value();
}

隨后創(chuàng)建一個(gè)測(cè)試類,在類的上方寫上注解,你會(huì)發(fā)現(xiàn),注解的參數(shù)中會(huì)讓你寫這兩個(gè)參數(shù)(int、String)。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

此時(shí),傳參是這樣來做的。格式為:名稱 = 返回值類型參數(shù)。如下:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

上述所說,如果使用default關(guān)鍵字給屬性默認(rèn)初始化值,就不需要為其參數(shù)賦值,如果賦值的話,就把默認(rèn)初始化的值覆蓋掉了。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

當(dāng)然還有一個(gè)規(guī)則,如果只有一個(gè)屬性需要賦值,并且屬性的名稱為value,則賦值時(shí)value可以省略,可以直接定義值。那么,我們的num已經(jīng)有了默認(rèn)值,就可以不為它傳值。我們發(fā)現(xiàn),注解中定義的屬性就剩下了一個(gè)value屬性值,那么我們就可以來演示這個(gè)規(guī)則了。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

這里,我并沒有寫屬性名稱value,而是直接為value賦值。如果我將num的default關(guān)鍵字修飾去掉呢,那意思也就是說在使用該注解時(shí)必須為num賦值,這樣可以省略value嗎?那我們看一下。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

結(jié)果,就是我們所想的,它報(bào)錯(cuò)了,必須讓我們給num賦值。其實(shí)想想這個(gè)規(guī)則也是很容易懂的,定義一個(gè)為value的值,就可以省略其value名稱。如果定義多個(gè)值,它們可以省略名稱就無法區(qū)分定義的是哪個(gè)值了,關(guān)鍵是還有數(shù)組,數(shù)組內(nèi)定義的是多個(gè)值呢,對(duì)吧。

5.4 自定義注解的多種返回值類型賦值

這里我們演示一下,上述的多種返回值類型是如何賦值的。這里我們定義這幾個(gè)參數(shù)來看一下,是如何為屬性賦值的。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

num是一個(gè)int基本數(shù)據(jù)類型,即num = 1

value是一個(gè)String類型,即value = "str"

lamp是一個(gè)枚舉類型,即lamp = Lamp.RED

myAnno2是一個(gè)注解類型,即myAnno2 = @MyAnno2

values是一個(gè)String類型數(shù)組,即values = {"s1", "s2", "s3"}

values是一個(gè)String類型數(shù)組,其數(shù)組中只有一個(gè)值,即values = "s4"

注意: 值與值之間是,隔開的;數(shù)組是用{}來存儲(chǔ)值的,如果數(shù)組中只有一個(gè)值可以省略{};枚舉類型是枚舉名.枚舉值

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

六、元注解

6.1 元注解分類

元注解就是用來描述注解的注解。一般使用元注解來限制自定義注解的使用范圍、生命周期等等。

而在jdk的中java.lang.annotation包中定義了四個(gè)元注解,如下:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

6.2 @Target

@Target 指定被修飾的注解的作用范圍。其作用范圍可以在源碼中找到參數(shù)值。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

由此可見,該注解體內(nèi)只有一個(gè)value屬性值,但是它的類型是一個(gè)ElementType數(shù)組。那我們進(jìn)入到這個(gè)數(shù)組中繼續(xù)查看。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

進(jìn)入到該數(shù)組中,你會(huì)發(fā)現(xiàn)它是一個(gè)枚舉類,其中定義了上述表格中的各個(gè)屬性。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

了解了@Target的作用和屬性值后,我們來使用一下該注解。首先,我們要先用該注解來修飾一個(gè)自定義注解,定義該注解的指定作用在類上。如下:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

而你觀察如下測(cè)試類,我們把注解作用在類上時(shí)是沒有錯(cuò)誤的。而當(dāng)我們的注解作用在其他地方就會(huì)報(bào)錯(cuò)。這也就說明了,我們@Target的屬性起了作用。

注意: 如果我們定義多個(gè)作用范圍時(shí),也是可以省略該參數(shù)名稱了,因?yàn)樵擃愋褪且粋€(gè)數(shù)組,雖然能省略名稱但是,我們還需要用{}來存儲(chǔ)。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

6.3 @Retention

@Retention 指定了被修飾的注解的生命周期

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

注意: 我們常用的定義即是RetentionPolicy.RUNTIME,因?yàn)槲覀兪褂梅瓷鋪韺?shí)現(xiàn)的時(shí)候是需要從JVM中獲取class類對(duì)象并操作類對(duì)象的。

首先,我們要了解反射的三個(gè)生命周期階段,這部分內(nèi)容我在Java反射機(jī)制中也是做了非常詳細(xì)的說明,有興趣的小伙伴可以去看看我寫的Java反射機(jī)制,相信你在其中也會(huì)有所收獲。

這里我再次強(qiáng)調(diào)一下這三個(gè)生命周期是源碼階段 - > class類對(duì)象階段 - > Runtime運(yùn)行時(shí)階段。

那我們進(jìn)入到源碼,看看@Retention注解中是否有這些參數(shù)。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

我們看到該注解中的屬性只有一個(gè)value,而它的類型是一個(gè)RetentionPolicy類型,我們進(jìn)入到該類型中看看有什么參數(shù),是否與表格中的參數(shù)相同呢?

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

至于該注解怎么使用,其實(shí)是相同的,用法如下:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

這就證明了我們的注解可以保留到Runtime運(yùn)行階段,而我們?cè)诜瓷渲写蠖鄶?shù)是定義到Runtime運(yùn)行時(shí)階段的,因?yàn)槲覀冃枰獜腏VM中獲取class類對(duì)象并操作類對(duì)象。

6.4 @Documented

@Documented 指定了被修飾的注解是可以Javadoc等工具文檔化

@Documented注解是比較好理解的,它是一個(gè)標(biāo)記注解。被該標(biāo)記注解標(biāo)記的注解,生成doc文檔時(shí),注解是可以被加載到文檔中顯示的。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

還拿api中過時(shí)的Date中的方法來說,在api中顯示Date中的getYear方法是這樣的。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

正如你看到的,注解在api中顯示了出來,證明該注解是@Documented注解修飾并文檔化的。那我們就看看這個(gè)注解是否被@Documented修飾吧。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

然后,我們發(fā)現(xiàn)該注解的確是被文檔化了。所以在api中才會(huì)顯示該注解的。如果不信,你可以自己使用javadoc命令來生成一下doc文檔,看看被該注解修飾的注解是否存在。

6.5 @Inherited

@Inherited 指定了被修飾的注解修飾程序元素的時(shí)候是可以被子類繼承的

首先進(jìn)入到源碼中,我們也可以清楚的知道,該注解也是一個(gè)標(biāo)記注解。而且它也是被文檔化的注解。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

其次,我們?nèi)ピ谧远x注解中,標(biāo)注上@Inherited注解。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

演示@Inherited注解,我需要?jiǎng)?chuàng)建兩個(gè)類,同時(shí)兩個(gè)類中有一層的繼承關(guān)系。如下:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

我們?cè)赑erson類中標(biāo)記了@MyAnno注解,由于該注解被@Inherited注解修飾,我們就可以得出繼承于Person類的Student類也同樣被@MyAnno注解標(biāo)記了,如果你要獲取該注解的值的話,肯定獲取的也是父類上注解值的那個(gè)"1"。

七、使用反射機(jī)制解析注解

自定義注解

package com.mylifes1110.anno;

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

/**
 * @InterfaceName Sign
 * @Description 描述需要執(zhí)行的類名和方法名
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sign {
    String methodName();

    String className();
}

Cat

package com.mylifes1110.java;

/**
 * @ClassName Cat
 * @Description 描述一只貓的類
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */
public class Cat {
    /*
     * @Description 描述一只貓吃魚的方法 
     * @Author Ziph
     * @Date 2020/6/6
     * @Param []
     * @return void
     */
    public void eat() {
        System.out.println("貓吃魚");
    }
}

準(zhǔn)備好,上述代碼后,我們就可以開始編寫使用反射技術(shù)來解析注解的測(cè)試類。如下:

首先,我們先通過反射來獲取注解中的methodName和className參數(shù)。

package com.mylifes1110.java;

import com.mylifes1110.anno.Sign;

/**
 * @ClassName SignTest
 * @Description 要求創(chuàng)建cat對(duì)象并執(zhí)行其類中eat方法
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */
@Sign(className = "com.mylifes1110.java.Cat", methodName = "eat")
public class SignTest {
    public static void main(String[] args) {
        //獲取該類的類對(duì)象
        Class<SignTest> signTestClass = SignTest.class;
        //獲取類對(duì)象中的注解對(duì)象
        //原理實(shí)際上是在內(nèi)存中生成了一個(gè)注解接口的子類實(shí)現(xiàn)對(duì)象
        Sign sign = signTestClass.getAnnotation(Sign.class);
        //調(diào)用注解對(duì)象中定義的抽象方法(注解中的屬性)來獲取返回值
        String className = sign.className();
        String methodName = sign.methodName();
        System.out.println(className);
        System.out.println(methodName);
    }
}

此時(shí)的打印結(jié)果證明我們已經(jīng)成功獲取到了該注解的兩個(gè)參數(shù)。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

注意: 獲取類對(duì)象中的注解對(duì)象時(shí),其原理實(shí)際上是在內(nèi)存中生成了一個(gè)注解接口的子類實(shí)現(xiàn)對(duì)象并返回的字符串內(nèi)容。如下:

public class SignImpl implements Sign {
    public String methodName() {
        return "eat";
    }
    
    public String className() {
        return "com.mylifes1110.java.Cat";
    }
}

繼續(xù)編寫我們后面的代碼,代碼完整版如下:

完整版代碼

package com.mylifes1110.java;

import com.mylifes1110.anno.Sign;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @ClassName SignTest
 * @Description 要求創(chuàng)建cat對(duì)象并執(zhí)行其類中eat方法
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */
@Sign(className = "com.mylifes1110.java.Cat", methodName = "eat")
public class SignTest {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //獲取該類的類對(duì)象
        Class<SignTest> signTestClass = SignTest.class;
        //獲取類對(duì)象中的注解對(duì)象
        //原理實(shí)際上是在內(nèi)存中生成了一個(gè)注解接口的子類實(shí)現(xiàn)對(duì)象
        Sign sign = signTestClass.getAnnotation(Sign.class);
        //調(diào)用注解對(duì)象中定義的抽象方法(注解中的屬性)來獲取返回值
        String className = sign.className();
        String methodName = sign.methodName();
        //獲取className名稱的類對(duì)象
        Class<?> clazz = Class.forName(className);
        //創(chuàng)建對(duì)象
        Object o = clazz.newInstance();
        //獲取methodName名稱的方法對(duì)象
        Method method = clazz.getMethod(methodName);
        //執(zhí)行該方法
        method.invoke(o);
    }
}

執(zhí)行結(jié)果

執(zhí)行后成功的調(diào)用了eat方法,并打印了貓吃魚的結(jié)果,如下:

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

八、自定義注解改變JDBC工具類

首先,我們?cè)谑褂肑DBC的時(shí)候是需要通過properties文件來獲取配置JDBC的配置信息的,這次我們通過自定義注解來獲取配置信息。其實(shí)使用注解并沒有用配置文件好,但是我們需要了解這是怎么做的,獲取方法也是魚使用反射機(jī)制解析注解,所謂“萬變不離其宗”,它就是這樣的。

自定義注解

package com.mylifes1110.java.anno;

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

/**
 * @InterfaceName DBInfo
 * @Description 給予注解聲明周期為運(yùn)行時(shí)并限定注解只能用在類上
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBInfo {
    String driver() default "com.MySQL.jdbc.Driver";

    String url() default "jdbc:mysql://localhost:3306/temp?useUnicode=true&characterEncoding=utf8";

    String username() default "root";

    String password() default "123456";
}

數(shù)據(jù)庫連接工具類

為了代碼的健全我也在里面加了properties文件獲取連接的方式。

package com.mylifes1110.java.utils;

import com.mylifes1110.java.anno.DBInfo;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * @ClassName DBUtils
 * @Description 數(shù)據(jù)庫連接工具類
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */
@DBInfo()
public class DBUtils {
    private static final Properties PROPERTIES = new Properties();
    private static String driver;
    private static String url;
    private static String username;
    private static String password;

    static {
        Class<DBUtils> dbUtilsClass = DBUtils.class;
        boolean annotationPresent = dbUtilsClass.isAnnotationPresent(DBInfo.class);
        if (annotationPresent) {
            /**
             * DBUilts類上有DBInfo注解,并獲取該注解
             */
            DBInfo dbInfo = dbUtilsClass.getAnnotation(DBInfo.class);
//            System.out.println(dbInfo);
            driver = dbInfo.driver();
            url = dbInfo.url();
            username = dbInfo.username();
            password = dbInfo.password();
        } else {
            InputStream inputStream = DBUtils.class.getResourceAsStream("db.properties");
            try {
                PROPERTIES.load(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
        try {
            return DriverManager.getConnection(url, username, password);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    public static void closeAll(Connection connection, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
                resultSet = null;
            }

            if (statement != null) {
                statement.close();
                statement = null;
            }
            if (connection != null) {
                connection.close();
                connection = null;
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

測(cè)試類

package com.mylifes1110.java.test;

import com.mylifes1110.java.utils.DBUtils;

import java.sql.Connection;

/**
 * @ClassName GetConnectionDemo
 * @Description 測(cè)試連接是否可以獲取到
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */
public class GetConnectionDemo {
    public static void main(String[] args) {
        Connection connection = DBUtils.getConnection();
        System.out.println(connection);
    }
}

測(cè)試結(jié)果

為了證明獲取的連接是由注解的配置信息獲取到的連接,我將properties文件中的所有配置信息刪除后測(cè)試的。

不知道什么是Java注解?莫慌,十分鐘一篇文章就能深度學(xué)習(xí)

 

九、自定義@MyTest注解實(shí)現(xiàn)單元測(cè)試

我不清楚小伙伴們是否了解,Junit單元測(cè)試。@Test是單元測(cè)試的測(cè)試方法上方修飾的注解。此注解的核心原理也是由反射來實(shí)現(xiàn)的。

作者:Ziph

原文鏈接:
https://blog.csdn.net/weixin_44170221/article/details/106590823

分享到:
標(biāo)簽:注解 Java
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定