突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

今日分享開始啦,請大家多多指教~

本篇將通過幾點來分析Java註解:

註解就是標籤,註解為了解釋程式碼

註解的基本語法@interface

註解的元註解

註解的屬性

註解主要給編譯器及工具型別的軟體用的

註解的提取要藉助於Java的反射技術,反射比較慢,所以註解使用時也需要謹慎計較時間成本

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

一、註解簡介

Java註解用於為Java程式碼提供元資料。

元資料是指用來描述資料的資料,通俗一點,就是描述程式碼間關係,或者程式碼與其它資源(例如資料庫表)之間內在聯絡的資料。在一些技術框架中,如Struts、hibernate就不知不覺用到了元資料。對於Struts來說,元資料指的是struts-config。xml;對hibernate來說就是hbm檔案。以上闡述的幾種元資料都是基於xml檔案的或者其他形式的單獨配置檔案。這樣表示有些不便之處。

1、與被描述的檔案分離,不利於一致性的維護;

2、所有這樣的檔案都是ASCII檔案,沒有顯式的型別支援。基於元資料的廣泛使用,JDK5。0引入了Annotation的概念來描述元資料。在Java中,元資料以標籤的形式存在於Java程式碼中,元資料標籤的存在並不影響程式程式碼的編譯和執行。簡而言之,言而總之,註解就是標籤的意思。

二、如何建立註解

JDK5。0出來後,Java語言中就有了四種類型,即類class、列舉enum、介面interface、註解@interface,它們處於同一級別,Java就是透過註解來表示元資料的。

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

Java註解本質上就是介面,是繼承了Annotation介面的介面。

三、元註解

元註解是可以註解到註解上的註解,或者說元註解是一種基本註解,它能夠應用到其它的註解上面。

元標籤有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 種。

1、@Retention

Retention,中文釋義保留期的意思

當@Retention應用到註解上的時候,它解釋說明了這個註解的生命週期。

RetentionPolicy。SOURCE 註解只在原始碼階段保留,在編譯器進行編譯時它將被丟棄忽視。

RetentionPolicy。CLASS 註解只被保留到編譯進行的時候,它並不會被載入到JVM中。

RetentionPolicy。RUNTIME 註解可以保留到程式執行的時候,它會被載入到JVM中。

2、@Documented

顧名思義,這個元註解肯定和文件有關。它的作用是能夠將註解中的元素包含到Javadoc中去。

3、@Target

標明註解運用的地方。

ElementType。ANNOTATION_TYPE 可以給一個註解進行註解

ElementType。CONSTRUCTOR 可以給構造方法進行註解

ElementType。FIELD 可以給屬性進行註解

ElementType。LOCAL_VARIABLE 可以給區域性變數進行註解

ElementType。METHOD 可以給方法進行註解

ElementType。PACKAGE 可以給一個包進行註解

ElementType。PARAMETER 可以給一個方法內的引數進行註解

ElementType。TYPE 可以給一個型別進行註解,比如類、介面、列舉

4、@Inherited

lnherited是繼承的意思。

如果一個超類被@Inherited註解過的註解進行註解的話,那麼如果它的子類沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解。

程式碼例項

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

5、@Repeatable

Repeatable 自然是可重複的意思。@Repeatable 是 Java 1。8 才加進來的,所以算是一個新的特性。

什麼樣的註解會多次應用呢?通常是註解的值可以同時取多個。

在生活中一個人往往是具有多種身份,如果我把每種身份當成一種註解該如何使用???

先宣告一個Persons類用來包含所有的身份

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

這裡@Target是宣告Persons註解的作用範圍,引數ElementType。Type代表可以給一個型別進行註解,比如類,介面,列舉。

@Retention是註解的有效時間,RetentionPolicy。RUNTIME是指程式執行的時候。

Person註解:

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

@Repeatable括號內的就相當於用來儲存該註解內容的容器。

宣告一個Man類,給該類加上一些身份。

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

在主方法中訪問該註解:

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

下面的程式碼結果輸出相同,但是可以先判斷是否是相應的註解,比較嚴謹。

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

執行結果:

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

四、註解的屬性

註解的屬性也叫做成員變數,註解只有成員變數,沒有方法。註解的成員變數在註解的定義中以“無參的方法”形式來宣告,其方法名定義了該成員變數的名字,其返回值定義了該成員變數的型別。

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

上面程式碼中定義了TestAnnotation這個註解中擁有id和msg兩個屬性。在使用的時候,我們應該給他們進行賦值。

賦值的方式是在註解的括號內以value=“”形式,多個屬性之前用,隔開。

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

需要注意的是,在註解中定義屬性時它的型別必須是 8 種基本資料型別外加 類、介面、註解及它們的陣列。

註解中屬性可以有預設值,預設值需要用 default 關鍵值指定。比如:

@Target(ElementType。TYPE)

@Retention(RetentionPolicy。RUNTIME)

public @interface TestAnnotation {

public int id() default -1;

public String msg() default “江疏影”;

}

TestAnnotation 中 id 屬性預設值為 -1,msg 屬性預設值為 江疏影。

它可以這樣應用。

@TestAnnotation()

public class Test {}

因為有預設值,所以不需要再在 @TestAnnotation 後面的括號裡面進行賦值了,這一步可以省略。

另外,還有一種情況。如果一個註解內僅僅只有一個名字為 value 的屬性時,應用這個註解時可以直接接屬性值填寫到括號內。

public @interface Check {

String value();

}

上面程式碼中,Check 這個註解只有 value 這個屬性。所以可以這樣應用。

@Check(“hi”)

int a;

這和下面的效果是一樣的

@Check(value=“hi”)

int a;

最後,還需要注意的一種情況是一個註解沒有任何屬性。比如

public @interface Perform {}

那麼在應用這個註解的時候,括號都可以省略。

@Perform

public void testMethod(){}

五、Java預置的註解

學習了上面相關的知識,我們已經可以自己定義一個註解了。其實 Java 語言本身已經提供了幾個現成的註解。

1、@Override

這個大家應該很熟悉了,提示子類要複寫父類中被 @Override 修飾的方法

2、@Deprecated

加上這個註解之後,表示此方法或類不再建議使用,呼叫時會出現刪除線,但不代表不能用,只是說,不推薦使用,因為有更好的方法可以呼叫。

那麼直接刪掉不就完了?

因為在一個專案中,工程比較大,程式碼比較多,而在後續的開發過程中,可能之前的某個方法實現的並不是很合理,這個時候要重新寫一個方法,而之前的方法還不能隨便刪,因為別的地方可能在呼叫它,所以加上這個註解,就OK啦!

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

3、@SuppressWarnings

阻止警告的意思。

該批註的作用是給編譯器一條指令,告訴它對被批註的程式碼元素內部的某些警告保持靜默。

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

注:這個註解有很多引數,這裡就不多做贅述了,如有需要,請自行百度!

4、@SafeVarargs

引數安全型別註解。

它的目的是提醒開發者不要用引數做一些不安全的操作,它的存在會阻止編譯器產生unchecked這樣的警告。

在宣告具有模糊型別(比如:泛型)的可變引數的建構函式或方法時,Java編譯器會報unchecked警告。鑑於這種情況,如果程式猿斷定宣告的建構函式和方法的主體no problem,可使用@SafeVarargs進行標記,這樣Java編譯器就不會報unchecked警告了!

先看看@SafeVarargs在Java SE中的宣告:

package java。lang;

import java。lang。annotation。*;

@Documented

@Retention(RetentionPolicy。RUNTIME)@Target({ElementType。CONSTRUCTOR, ElementType。METHOD})

public @interface SafeVarargs {}

由Java原始碼宣告我們瞭解到:@SafeVarargs註解,只能用於標記建構函式和方法,由於保留策略宣告為RUNTIME,所以此註解可以在執行時生效。

@SafeVarargs註解,只能用於static或final的方法。

程式碼例項:

泛型引數的方法,不加註解的情況:

package com。guor。ClientNew;

public class SafeVarargsAnnotation {

private S[] args;

public SafeVarargsAnnotation(S。。。 args){

this。args = args;

}

public void loopPrintArgs(S。。。 args){

for (S arg : args){

System。out。println(arg);

}

}

public final void printSelfArgs(S。。。 args){

for (S arg : this。args) {

System。out。println(arg);

}

}

public static void loopPrintInfo(T。。。 infos){

for(T info:infos){

System。out。println(info);

}

}

public static void main(String[] args) {

SafeVarargsAnnotation。loopPrintInfo(“A”,“B”,“C”);

}

}

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

註解的正確使用方式:

package com。guor。ClientNew;

public class SafeVarargsAnnotation {

private S[] args;

//建構函式可以使用@SafeVarargs標記

@SafeVarargs

public SafeVarargsAnnotation(S。。。 args){

this。args = args;

}

//此處不能使用@SafeVarargs,因為此方法未宣告為static或final方法,

// 如果要抑制unchecked警告,可以使用@SuppressWarnings註解

@SuppressWarnings(“unchecked”)

public void loopPrintArgs(S。。。 args){

for (S arg : args){

System。out。println(arg);

}

}

//final方法可以使用@SafeVarargs標記

@SafeVarargs

public final void printSelfArgs(S。。。 args){

for (S arg : this。args) {

System。out。println(arg);

}

}

//static方法可以使用@SafeVarargs標記

@SafeVarargs

public static void loopPrintInfo(T。。。 infos){

for(T info:infos){

System。out。println(info);

}

}

public static void main(String[] args) {

SafeVarargsAnnotation。loopPrintInfo(“A”,“B”,“C”);

}

}

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

5、@FunctionalInterface

Java 8為函式式介面引入了一個新註解@FunctionalInterface,主要用於編譯級錯誤檢查,加上該註解,當你寫的介面不符合函式式介面定義的時候,編譯器會報錯。

它們主要用在Lambda表示式和方法引用(實際上也可認為是Lambda表示式)上。

如定義了一個函式式介面如下:

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

那麼就可以使用Lambda表示式來表示該介面的一個實現(注:JAVA 8 之前一般是用匿名類實現的):

GreetingService greetService1 = message -> System。out。println(“Hello ” + message);

淺談lambda表示式<最通俗易懂的講解>

六、註解與反射

1、註解透過反射獲取

首先可以透過 Class 物件的 isAnnotationPresent() 方法判斷它是否應用了某個註解。

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

2、getAnnotations() 方法

public Annotation[] getAnnotations() {}

前一種方法返回指定型別的註解,後一種方法返回註解到這個元素上的所有註解。

3、程式碼例項:

① 沒加註解的時候:

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

屁都沒有!

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

② 加上註解

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

這個正是 TestAnnotation 中 id 和 msg 的預設值。

上面的例子只是檢閱出了註解在類上的註解,其實屬性、方法上的註解也是一樣的。同樣還是要假手與反射。

③ 屬性和方法上的註解:

package com。guor。Annotation;

import OSChina。ClientNew。Hero;

import java。lang。annotation。Annotation;

import java。lang。reflect。Field;

import java。lang。reflect。Method;

@TestAnnotation(msg=“hello”)

public class Test {

@Check(value=“hi”)

int a;

@Perform

public void testMethod(){}

@SuppressWarnings(“deprecation”)

public void test1(){

Hero hero = new Hero();

hero。say();

hero。speak();

}

public static void main(String[] args) {

boolean hasAnnotation = Test。class。isAnnotationPresent(TestAnnotation。class);

if ( hasAnnotation ) {

TestAnnotation testAnnotation = Test。class。getAnnotation(TestAnnotation。class);

//獲取類的註解

System。out。println(“id:”+testAnnotation。id());

System。out。println(“msg:”+testAnnotation。msg());

}

try {

Field a = Test。class。getDeclaredField(“a”);

a。setAccessible(true);

//獲取一個成員變數上的註解

Check check = a。getAnnotation(Check。class);

if ( check != null ) {

System。out。println(“check value:”+check。value());

}

Method testMethod = Test。class。getDeclaredMethod(“testMethod”);

if ( testMethod != null ) {

// 獲取方法中的註解

Annotation[] ans = testMethod。getAnnotations();

for( int i = 0;i < ans。length;i++) {

System。out。println(“method testMethod annotation:”+ans[i]。annotationType()。getSimpleName());

}

}

} catch (NoSuchFieldException e) {

// TODO Auto-generated catch block

e。printStackTrace();

System。out。println(e。getMessage());

} catch (SecurityException e) {

// TODO Auto-generated catch block

e。printStackTrace();

System。out。println(e。getMessage());

} catch (NoSuchMethodException e) {

// TODO Auto-generated catch block

e。printStackTrace();

System。out。println(e。getMessage());

}

}

}

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

需要注意的是,如果一個註解要在執行時被成功提取,那麼 @Retention(RetentionPolicy。RUNTIME) 是必須的。

七、註解的使用場景

1、註解的官方釋義

註解是一系列元資料,它提供資料用來解釋程式程式碼,但是註解並非是所解釋的程式碼本身的一部分。註解對於程式碼的執行效果沒有直接影響。

2、註解有許多用處:

① 提供資訊給編譯器:編譯器可以利用註解來探測錯誤或警告資訊

② 編譯階段時的處理:軟體工具可以利用註解資訊來生成程式碼、HTML文件或其它響應處理。

③ 執行時的處理:某些註解可以在程式執行時接受程式碼的提取。

值得注意的是,註解不是程式碼本身的一部分。

3、註解運用的地方太多了,比如JUnit測試框架,典型的使用方法

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

@Test 標記了要進行測試的方法 addition_isCorrect()。

還有例如ssm框架等運用了大量的註解。

八、註解的應用例項

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

該註解Desc前增加了三個註解:Retention表示的是該註解的保留級別,Target表示的是註解可以標註在什麼地方,@Inherited表示該註解會被自動繼承。

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

上面程式聲明瞭一個Bird抽象類,並且標註了Desc註解,描述鳥類的顏色是白色,然後編寫一個麻雀Sparrow類,它有兩個建構函式,一個是預設的建構函式,也就是我們經常看到的麻雀是淺灰色的,另外一個建構函式是自定義麻雀的顏色,之後又定義了一個鳥巢(工廠方法模式),它是專門負責鳥類繁殖的,它的生產方法reproduce會根據實現類註解資訊生成不同顏色的麻雀。我們編寫一個客戶端呼叫,程式碼如下:

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

會打印出什麼呢?因為採用了工廠方法模式,它主要的問題是bird比那裡到底採用了哪個建構函式來生成,如果單獨看子類Sparrow,它沒有任何註釋,那工廠方法中bd變數應該就是null了,應該呼叫無參構造!

突然接到開除通知,原因竟然是我不用Java註解?網友直呼:好傢伙

輸出為什麼會是白色呢?這是我們新增到父類的顏色,why?這是因為我們在註解上加了@Inherited註解,它表示的意思是我們只要把註解@Desc加到父類Bird上,它的所有子類都會從父類繼承@Desc註解。

小結:

Java註解(Annotation)亦叫Java標註,是JDK5。0開始引入的一種註釋機制。 可以對包、類、介面、欄位、方法引數、區域性變數等進行註解。註解可以設定存在於不同的生命週期中,例如SOURCE(原始碼中),CLASS(Class檔案中,預設是此保留級別),RUNTIME(執行期中)。

今日份分享已結束,請大家多多包涵和指點!