天天看點

面試官:說說你對【注解】的了解

本文主要内容如下:

面試官:說說你對【注解】的了解

現在已經處于注解盛行時代,注解@Override ,這個注解是再熟悉不過了,還有@Controller、@RequestMapping、@Service.....

注解已經是作為一個開發中必備的技能了。

如果在面試中被問到注解,說不出個123,就隻能回去等通知了。

注解annotation是JavaSE5.0中新增功能。可以了解為注解是一種标記,這種标記可以在編譯、類加載、運作時被讀取,并執行相應的處理。

它可以添加到程式的任何元素上:包聲明、類型聲明、構造方法、普通方法、成員變量、參數。

注解的老大:

JDK自帶為我們提供了元注解,下面就來聊聊JDK的元注解。

JDK為我們提供五個元注解,位于java.lang.annotation 包目錄下。分别為:

@Retention

@Target

@Documented

@Inherited

@Repeatable

字面翻譯:

面試官:說說你對【注解】的了解

該注解就是定義該注解是作用于什麼階段。

編譯、類加載、運作(使用最多)

注解源碼:

保留政策

SOURCE:隻在Java源代碼中,編譯器編譯的時候會把它直接丢棄。

CLASS:編譯器将注解記錄在.class檔案中。當運作Java程式時,JVM不能擷取注解資訊。這是預設值。

RUNTIME:編譯器将注解記錄在.class檔案中。當運作Java程式時,JVM也能擷取注解資訊。程式可以通過反射擷取注解資訊。

注意:如果使用該注解的時候必須指定value的值。

字面意義為目标,下面來看看器源碼:

Target注解是在聲明建立一個注解的時候,訓示該注解可以作用于程式中的哪些元素。

它裡邊也包含一個名為value的成員變量,value成員變量的值有如下幾種

ANNOTATION_TYPE:指定目前注解隻能修飾其它注解

CONSTRUCTOR:指定目前注解隻能修飾構造方法

FIELD:指定目前注解隻能修飾成員變量

LOCAL_VARIABLE:指定目前注解隻能修飾局部變量

METHOD:指定目前注解隻能修飾方法

PACKAGE:指定目前注解隻能修飾包

PARAMETER:指定目前注解隻能修飾參數

TYPE:指定目前注解可以修飾類,接口,其它注解,枚舉等類型

比如說:常用的Spring的注解@Controller,是用來作用于類上的。

面試官:說說你對【注解】的了解

字面意義就是文檔。@Documented用于描述其它類型的annotation應該被作為被标注的程式成員的公共API,是以可以被例如javadoc此類的工具文    檔化。Documented是一個标記注解,沒有成員 。

主要是用來生成文檔的,工作中基本上很少使用,作為了解就可以了。

字面意義:

面試官:說說你對【注解】的了解

@Inherited注解是在聲明建立一個注解的時候,指定該注解将具有繼承性。

Inherited 源碼:

如果某個類使用了該注解@Xx,則其子類也将自動被此注解@Xx所修飾。

使用模闆:

@Repeatable元注解,顧名思義,重複注解,就是在聲明建立注解的時候,指定該注解可以被同一個程式元素多次使用。這是JDK8新增的注解,重複注解隻是一種簡單化寫法,這種簡單化寫法是一種假象。多個重複注解其實會被作為“容器”注解的value成員變量的數組元素。

比如下面Spring中注解ComponentScan:

使用案例

注解必須使用工具來處理,工具負責提取注解中包含的中繼資料,工具還會根據這些中繼資料增加額外的功能。下面我們先來了解一下5個基本注解的用法。

Override

SafeVarargs

SuppressWarnings

FunctionalInterface

Deprecated

下面我們就來說說這五個注解。

用于辨別方法,辨別該方法屬于重寫父類的方法 。

此注解表面是重寫父類的方法,隻能用于方法上,并且用于編譯階段,我們在開發的時候,如果注解使用不當,在源碼編譯時立馬就會做出提示。

@SafeVarargs注解是在JDK7中引入的。此注解适用于接受varargs參數的final和static方法或構造函數。此注解用于確定方法不會對其varargs參數執行不安全的操作。從Java9開始,@SafeVarargs注解也适用于私有執行個體方法。

@SafeVarargs源碼

如果不使用此注解

display方法會提示

面試官:說說你對【注解】的了解

如果我們給這個方法添加此注解後

面試官:說說你對【注解】的了解

上面的代碼在可變參數display前添加了@SafeVarargs注解,當然也可以使用 @SuppressWarnings("unchecked") 注解,可是,兩者相比較來說的話@SafeVarargs注解更适合。

注意:@SafeVarargs注解不适用于非static或非final聲明的方法,對于未聲明為static或final的方法,假如,要抑制unchecked警告,可以使用@SuppressWarnings注解。

用于有選擇的關閉編譯器對類、方法、成員變量、變量初始化的警告。

編譯時期就會做出提示,使用範圍也是比較廣泛,可以适用于類、接口、枚舉、方法、字段等。

我們開發過程中見過最多的應該是集合存放資料的時候,比如下面的例子。

但是如果我們把第一行代碼注釋放開。

這樣就不會提示了。

JDK8新增注解。@FunctionalInterface标記在接口上,“函數式接口”是指僅僅隻包含一個抽象方法的接口。

源碼

1、該注解隻能标記在”有且僅有一個抽象方法”的接口上。

2、JDK8接口中的靜态方法和預設方法,都不算是抽象方法。

3、接口預設繼承java.lang.Object,是以如果接口顯示聲明覆寫了Object中方法,那麼也不算抽象方法。

4、該注解不是必須的,如果一個接口符合”函數式接口”定義,那麼加不加該注解都沒有影響。加上該注解能夠更好地讓編譯器進行檢查。如果編寫的不是函數式接口,但是加上了@FunctionInterface,那麼編譯器會報錯。

5、@FunctionalInterface 注解的interface。它的特點是其中隻有一個子類必須要實作的abstract方法。

使用場景

Callable、Runnable等就有使用到。

用于辨別方法或類,辨別該類或方法已過時,建議不要使用 。

使用範圍就想到廣泛了,構造方法、字段、方法等。

注意:隻是提示過時了,不建議使用,不代表不能用,但是我們如果想用某個使用此注解标記的方法或者類的時候,建議找找有沒有替換方案,實在沒有替換方案,搞清楚為什麼它會被設定成過時,使用不當可能會對我們的程式造成你意想不到問題,也可能會挖坑。

終于來到自定義了,下面我們來自定義一個注解。

使用@interface自定義注解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程式自動完成其他細節。在定義注解時,不能繼承其他的注解或接口。@interface用來聲明一個注解,其中的每一個方法實際上是聲明了一個配置參數。方法的名稱就是參數的名稱,傳回值類型就是參數的類型(傳回值類型隻能是基本類型、Class、String、enum)。可以通過default來聲明參數的預設值。

自定義我們的注解

使用階段為運作階段,目标是在方法上。定義一個屬性value預設值為空字元串。

下面我們就來使用我們定義的注解。

如果沒有指定value的值,那就使用預設值,value兩種方式指派:

然後我們運作上面的代碼,輸出結果為:

面試官:說說你對【注解】的了解

以上我們就搞定了自定義注解以及使用,擷取屬性值。

使用javap檢視我們自定義的注解.class檔案内容。

面試官:說說你對【注解】的了解

這裡Annotation是接口:

那麼證明,我們自定義的注解是extend接口Annotation,由此可知注解就是接口。

解參數的可支援資料類型:

所有基本資料類型(int,float,boolean,byte,double,char,long,short)

String類型

Class類型

enum類型

Annotation類型

以上所有類型的數組

Annotation類型裡面的參數該怎麼設定:

隻能用public或預設(default)這兩個通路權修飾.例如,String value();這裡把方法設為defaul預設類型。

參數成員隻能用基本類型byte,short,char,int,long,float,double,boolean八種基本資料類型 和 String,Enum,Class,annotations等資料類型,以及這一些類型的數組.例如,String value();這裡的參數成員就為String。

如果隻有一個參數成員,最好把參數名稱設為"value",後加小括号.例:下面的例子FruitName注解就隻有一個參數成員。

共有以下五種方式:

擷取類上的注解:Class類的getAnnotation()

擷取方法上的注解:Method類的getAnnotation()

擷取字段上的注解:Field類的 getAnnotation()

擷取構造方法上的注解:Constructor類的getAnnotation()

擷取包上的注解:Package類的getAnnotation()

如果此元素上存在指定的注釋類型,則此方法傳回該元素的注釋,否則傳回null。從上面的的集中方式中發現,都是使用getAnnotation()方法擷取的,相信大多數人都能猜到為什麼都是同一個方法名稱。

下面就來說說Java中注解擷取的鼻祖:

此接口所有方法

面試官:說說你對【注解】的了解

看看AnnotatedElement類圖:

面試官:說說你對【注解】的了解

發現前面說的擷取注解的類,全部都實作了AnnotatedElement接口。

是以程式通過反射擷取了某個類的AnnotatedElement對象之後,程式就可以調用該對象的方法。

如下四個個方法來通路Annotation資訊:

「getAnnotation」

傳回該程式元素上存在的、指定類型的注解,如果該類型注解不存在,則傳回null。

「getAnnotations」

傳回該程式元素上存在的所有注解。

「isAnnotationPresent」

判斷該程式元素上是否包指定類型的注解,存在則傳回true,否則傳回false。

「getDeclaredAnnotations」

傳回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法将忽略繼承的注釋。(如果沒有注釋直接存在于此元素上,則傳回長度為零的一個數組。)該方法的調用者可以随意修改傳回的數組;這不會對其他調用者傳回的數組産生任何影響

為什麼要學習注解?元注解有哪些?基本注解有哪些?如何自定義注解?注解是普通類還是接口?自定義注解需要注意些什麼?擷取擷取注解?